Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * (C) Copyright 2009-2010
0003  * Nokia Siemens Networks, michael.lawnick.ext@nsn.com
0004  *
0005  * Portions Copyright (C) 2010 - 2016 Cavium, Inc.
0006  *
0007  * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors.
0008  *
0009  * This file is licensed under the terms of the GNU General Public
0010  * License version 2. This program is licensed "as is" without any
0011  * warranty of any kind, whether express or implied.
0012  */
0013 
0014 #include <linux/atomic.h>
0015 #include <linux/delay.h>
0016 #include <linux/i2c.h>
0017 #include <linux/interrupt.h>
0018 #include <linux/io.h>
0019 #include <linux/kernel.h>
0020 #include <linux/module.h>
0021 #include <linux/of.h>
0022 #include <linux/platform_device.h>
0023 #include <linux/sched.h>
0024 #include <linux/slab.h>
0025 
0026 #include <asm/octeon/octeon.h>
0027 #include "i2c-octeon-core.h"
0028 
0029 #define DRV_NAME "i2c-octeon"
0030 
0031 /**
0032  * octeon_i2c_int_enable - enable the CORE interrupt
0033  * @i2c: The struct octeon_i2c
0034  *
0035  * The interrupt will be asserted when there is non-STAT_IDLE state in
0036  * the SW_TWSI_EOP_TWSI_STAT register.
0037  */
0038 static void octeon_i2c_int_enable(struct octeon_i2c *i2c)
0039 {
0040     octeon_i2c_write_int(i2c, TWSI_INT_CORE_EN);
0041 }
0042 
0043 /* disable the CORE interrupt */
0044 static void octeon_i2c_int_disable(struct octeon_i2c *i2c)
0045 {
0046     /* clear TS/ST/IFLG events */
0047     octeon_i2c_write_int(i2c, 0);
0048 }
0049 
0050 /**
0051  * octeon_i2c_int_enable78 - enable the CORE interrupt
0052  * @i2c: The struct octeon_i2c
0053  *
0054  * The interrupt will be asserted when there is non-STAT_IDLE state in the
0055  * SW_TWSI_EOP_TWSI_STAT register.
0056  */
0057 static void octeon_i2c_int_enable78(struct octeon_i2c *i2c)
0058 {
0059     atomic_inc_return(&i2c->int_enable_cnt);
0060     enable_irq(i2c->irq);
0061 }
0062 
0063 static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq)
0064 {
0065     int count;
0066 
0067     /*
0068      * The interrupt can be disabled in two places, but we only
0069      * want to make the disable_irq_nosync() call once, so keep
0070      * track with the atomic variable.
0071      */
0072     count = atomic_dec_if_positive(cnt);
0073     if (count >= 0)
0074         disable_irq_nosync(irq);
0075 }
0076 
0077 /* disable the CORE interrupt */
0078 static void octeon_i2c_int_disable78(struct octeon_i2c *i2c)
0079 {
0080     __octeon_i2c_irq_disable(&i2c->int_enable_cnt, i2c->irq);
0081 }
0082 
0083 /**
0084  * octeon_i2c_hlc_int_enable78 - enable the ST interrupt
0085  * @i2c: The struct octeon_i2c
0086  *
0087  * The interrupt will be asserted when there is non-STAT_IDLE state in
0088  * the SW_TWSI_EOP_TWSI_STAT register.
0089  */
0090 static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c)
0091 {
0092     atomic_inc_return(&i2c->hlc_int_enable_cnt);
0093     enable_irq(i2c->hlc_irq);
0094 }
0095 
0096 /* disable the ST interrupt */
0097 static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c)
0098 {
0099     __octeon_i2c_irq_disable(&i2c->hlc_int_enable_cnt, i2c->hlc_irq);
0100 }
0101 
0102 /* HLC interrupt service routine */
0103 static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id)
0104 {
0105     struct octeon_i2c *i2c = dev_id;
0106 
0107     i2c->hlc_int_disable(i2c);
0108     wake_up(&i2c->queue);
0109 
0110     return IRQ_HANDLED;
0111 }
0112 
0113 static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c)
0114 {
0115     octeon_i2c_write_int(i2c, TWSI_INT_ST_EN);
0116 }
0117 
0118 static u32 octeon_i2c_functionality(struct i2c_adapter *adap)
0119 {
0120     return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
0121            I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL;
0122 }
0123 
0124 static const struct i2c_algorithm octeon_i2c_algo = {
0125     .master_xfer = octeon_i2c_xfer,
0126     .functionality = octeon_i2c_functionality,
0127 };
0128 
0129 static const struct i2c_adapter octeon_i2c_ops = {
0130     .owner = THIS_MODULE,
0131     .name = "OCTEON adapter",
0132     .algo = &octeon_i2c_algo,
0133 };
0134 
0135 static int octeon_i2c_probe(struct platform_device *pdev)
0136 {
0137     struct device_node *node = pdev->dev.of_node;
0138     int irq, result = 0, hlc_irq = 0;
0139     struct octeon_i2c *i2c;
0140     bool cn78xx_style;
0141 
0142     cn78xx_style = of_device_is_compatible(node, "cavium,octeon-7890-twsi");
0143     if (cn78xx_style) {
0144         hlc_irq = platform_get_irq(pdev, 0);
0145         if (hlc_irq < 0)
0146             return hlc_irq;
0147 
0148         irq = platform_get_irq(pdev, 2);
0149         if (irq < 0)
0150             return irq;
0151     } else {
0152         /* All adaptors have an irq.  */
0153         irq = platform_get_irq(pdev, 0);
0154         if (irq < 0)
0155             return irq;
0156     }
0157 
0158     i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
0159     if (!i2c) {
0160         result = -ENOMEM;
0161         goto out;
0162     }
0163     i2c->dev = &pdev->dev;
0164 
0165     i2c->roff.sw_twsi = 0x00;
0166     i2c->roff.twsi_int = 0x10;
0167     i2c->roff.sw_twsi_ext = 0x18;
0168 
0169     i2c->twsi_base = devm_platform_ioremap_resource(pdev, 0);
0170     if (IS_ERR(i2c->twsi_base)) {
0171         result = PTR_ERR(i2c->twsi_base);
0172         goto out;
0173     }
0174 
0175     /*
0176      * "clock-rate" is a legacy binding, the official binding is
0177      * "clock-frequency".  Try the official one first and then
0178      * fall back if it doesn't exist.
0179      */
0180     if (of_property_read_u32(node, "clock-frequency", &i2c->twsi_freq) &&
0181         of_property_read_u32(node, "clock-rate", &i2c->twsi_freq)) {
0182         dev_err(i2c->dev,
0183             "no I2C 'clock-rate' or 'clock-frequency' property\n");
0184         result = -ENXIO;
0185         goto out;
0186     }
0187 
0188     i2c->sys_freq = octeon_get_io_clock_rate();
0189 
0190     init_waitqueue_head(&i2c->queue);
0191 
0192     i2c->irq = irq;
0193 
0194     if (cn78xx_style) {
0195         i2c->hlc_irq = hlc_irq;
0196 
0197         i2c->int_enable = octeon_i2c_int_enable78;
0198         i2c->int_disable = octeon_i2c_int_disable78;
0199         i2c->hlc_int_enable = octeon_i2c_hlc_int_enable78;
0200         i2c->hlc_int_disable = octeon_i2c_hlc_int_disable78;
0201 
0202         irq_set_status_flags(i2c->irq, IRQ_NOAUTOEN);
0203         irq_set_status_flags(i2c->hlc_irq, IRQ_NOAUTOEN);
0204 
0205         result = devm_request_irq(&pdev->dev, i2c->hlc_irq,
0206                       octeon_i2c_hlc_isr78, 0,
0207                       DRV_NAME, i2c);
0208         if (result < 0) {
0209             dev_err(i2c->dev, "failed to attach interrupt\n");
0210             goto out;
0211         }
0212     } else {
0213         i2c->int_enable = octeon_i2c_int_enable;
0214         i2c->int_disable = octeon_i2c_int_disable;
0215         i2c->hlc_int_enable = octeon_i2c_hlc_int_enable;
0216         i2c->hlc_int_disable = octeon_i2c_int_disable;
0217     }
0218 
0219     result = devm_request_irq(&pdev->dev, i2c->irq,
0220                   octeon_i2c_isr, 0, DRV_NAME, i2c);
0221     if (result < 0) {
0222         dev_err(i2c->dev, "failed to attach interrupt\n");
0223         goto out;
0224     }
0225 
0226     if (OCTEON_IS_MODEL(OCTEON_CN38XX))
0227         i2c->broken_irq_check = true;
0228 
0229     result = octeon_i2c_init_lowlevel(i2c);
0230     if (result) {
0231         dev_err(i2c->dev, "init low level failed\n");
0232         goto  out;
0233     }
0234 
0235     octeon_i2c_set_clock(i2c);
0236 
0237     i2c->adap = octeon_i2c_ops;
0238     i2c->adap.timeout = msecs_to_jiffies(2);
0239     i2c->adap.retries = 5;
0240     i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info;
0241     i2c->adap.dev.parent = &pdev->dev;
0242     i2c->adap.dev.of_node = node;
0243     i2c_set_adapdata(&i2c->adap, i2c);
0244     platform_set_drvdata(pdev, i2c);
0245 
0246     result = i2c_add_adapter(&i2c->adap);
0247     if (result < 0)
0248         goto out;
0249     dev_info(i2c->dev, "probed\n");
0250     return 0;
0251 
0252 out:
0253     return result;
0254 };
0255 
0256 static int octeon_i2c_remove(struct platform_device *pdev)
0257 {
0258     struct octeon_i2c *i2c = platform_get_drvdata(pdev);
0259 
0260     i2c_del_adapter(&i2c->adap);
0261     return 0;
0262 };
0263 
0264 static const struct of_device_id octeon_i2c_match[] = {
0265     { .compatible = "cavium,octeon-3860-twsi", },
0266     { .compatible = "cavium,octeon-7890-twsi", },
0267     {},
0268 };
0269 MODULE_DEVICE_TABLE(of, octeon_i2c_match);
0270 
0271 static struct platform_driver octeon_i2c_driver = {
0272     .probe      = octeon_i2c_probe,
0273     .remove     = octeon_i2c_remove,
0274     .driver     = {
0275         .name   = DRV_NAME,
0276         .of_match_table = octeon_i2c_match,
0277     },
0278 };
0279 
0280 module_platform_driver(octeon_i2c_driver);
0281 
0282 MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>");
0283 MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors");
0284 MODULE_LICENSE("GPL");