0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
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
0033
0034
0035
0036
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
0044 static void octeon_i2c_int_disable(struct octeon_i2c *i2c)
0045 {
0046
0047 octeon_i2c_write_int(i2c, 0);
0048 }
0049
0050
0051
0052
0053
0054
0055
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
0069
0070
0071
0072 count = atomic_dec_if_positive(cnt);
0073 if (count >= 0)
0074 disable_irq_nosync(irq);
0075 }
0076
0077
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
0085
0086
0087
0088
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
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
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
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
0177
0178
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");