284 lines · c
1/*2 * (C) Copyright 2009-20103 * Nokia Siemens Networks, michael.lawnick.ext@nsn.com4 *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 Public10 * License version 2. This program is licensed "as is" without any11 * 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 interrupt33 * @i2c: The struct octeon_i2c34 *35 * The interrupt will be asserted when there is non-STAT_IDLE state in36 * 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 interrupt52 * @i2c: The struct octeon_i2c53 *54 * The interrupt will be asserted when there is non-STAT_IDLE state in the55 * 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 only69 * want to make the disable_irq_nosync() call once, so keep70 * 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 interrupt85 * @i2c: The struct octeon_i2c86 *87 * The interrupt will be asserted when there is non-STAT_IDLE state in88 * 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 .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 is177 * "clock-frequency". Try the official one first and then178 * 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 void 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};262 263static const struct of_device_id octeon_i2c_match[] = {264 { .compatible = "cavium,octeon-3860-twsi", },265 { .compatible = "cavium,octeon-7890-twsi", },266 {},267};268MODULE_DEVICE_TABLE(of, octeon_i2c_match);269 270static struct platform_driver octeon_i2c_driver = {271 .probe = octeon_i2c_probe,272 .remove_new = octeon_i2c_remove,273 .driver = {274 .name = DRV_NAME,275 .of_match_table = octeon_i2c_match,276 },277};278 279module_platform_driver(octeon_i2c_driver);280 281MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>");282MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors");283MODULE_LICENSE("GPL");284