Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: octeon: Add support for cn78xx chips

cn78xx has a different interrupt architecture, so we have to manage
the interrupts differently.

Signed-off-by: David Daney <ddaney@caviumnetworks.com>
Signed-off-by: Jan Glauber <jglauber@cavium.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>

authored by

Jan Glauber and committed by
Wolfram Sang
4729cbe0 d1fbff89

+125 -10
+6
Documentation/devicetree/bindings/i2c/i2c-octeon.txt
··· 4 4 5 5 Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. 6 6 7 + or 8 + 9 + compatible: "cavium,octeon-7890-twsi" 10 + 11 + Compatibility with cn78XX SOCs. 12 + 7 13 - reg: The base address of the TWSI/I2C bus controller register bank. 8 14 9 15 - #address-cells: Must be <1>.
+119 -10
drivers/i2c/busses/i2c-octeon.c
··· 11 11 * warranty of any kind, whether express or implied. 12 12 */ 13 13 14 + #include <linux/atomic.h> 14 15 #include <linux/platform_device.h> 15 16 #include <linux/interrupt.h> 16 17 #include <linux/kernel.h> ··· 113 112 wait_queue_head_t queue; 114 113 struct i2c_adapter adap; 115 114 int irq; 115 + int hlc_irq; /* For cn7890 only */ 116 116 u32 twsi_freq; 117 117 int sys_freq; 118 118 void __iomem *twsi_base; 119 119 struct device *dev; 120 120 bool hlc_enabled; 121 + void (*int_enable)(struct octeon_i2c *); 122 + void (*int_disable)(struct octeon_i2c *); 123 + void (*hlc_int_enable)(struct octeon_i2c *); 124 + void (*hlc_int_disable)(struct octeon_i2c *); 125 + atomic_t int_enable_cnt; 126 + atomic_t hlc_int_enable_cnt; 121 127 }; 122 128 123 129 static void octeon_i2c_writeq_flush(u64 val, void __iomem *addr) ··· 224 216 octeon_i2c_write_int(i2c, 0); 225 217 } 226 218 219 + /** 220 + * octeon_i2c_int_enable78 - enable the CORE interrupt 221 + * @i2c: The struct octeon_i2c 222 + * 223 + * The interrupt will be asserted when there is non-STAT_IDLE state in the 224 + * SW_TWSI_EOP_TWSI_STAT register. 225 + */ 226 + static void octeon_i2c_int_enable78(struct octeon_i2c *i2c) 227 + { 228 + atomic_inc_return(&i2c->int_enable_cnt); 229 + enable_irq(i2c->irq); 230 + } 231 + 232 + static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq) 233 + { 234 + int count; 235 + 236 + /* 237 + * The interrupt can be disabled in two places, but we only 238 + * want to make the disable_irq_nosync() call once, so keep 239 + * track with the atomic variable. 240 + */ 241 + count = atomic_dec_if_positive(cnt); 242 + if (count >= 0) 243 + disable_irq_nosync(irq); 244 + } 245 + 246 + /* disable the CORE interrupt */ 247 + static void octeon_i2c_int_disable78(struct octeon_i2c *i2c) 248 + { 249 + __octeon_i2c_irq_disable(&i2c->int_enable_cnt, i2c->irq); 250 + } 251 + 252 + /** 253 + * octeon_i2c_hlc_int_enable78 - enable the ST interrupt 254 + * @i2c: The struct octeon_i2c 255 + * 256 + * The interrupt will be asserted when there is non-STAT_IDLE state in 257 + * the SW_TWSI_EOP_TWSI_STAT register. 258 + */ 259 + static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c) 260 + { 261 + atomic_inc_return(&i2c->hlc_int_enable_cnt); 262 + enable_irq(i2c->hlc_irq); 263 + } 264 + 265 + /* disable the ST interrupt */ 266 + static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c) 267 + { 268 + __octeon_i2c_irq_disable(&i2c->hlc_int_enable_cnt, i2c->hlc_irq); 269 + } 270 + 227 271 /* 228 272 * Cleanup low-level state & enable high-level controller. 229 273 */ ··· 322 262 { 323 263 struct octeon_i2c *i2c = dev_id; 324 264 325 - octeon_i2c_int_disable(i2c); 265 + i2c->int_disable(i2c); 266 + wake_up(&i2c->queue); 267 + 268 + return IRQ_HANDLED; 269 + } 270 + 271 + /* HLC interrupt service routine */ 272 + static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) 273 + { 274 + struct octeon_i2c *i2c = dev_id; 275 + 276 + i2c->hlc_int_disable(i2c); 326 277 wake_up(&i2c->queue); 327 278 328 279 return IRQ_HANDLED; ··· 354 283 { 355 284 long time_left; 356 285 357 - octeon_i2c_int_enable(i2c); 286 + i2c->int_enable(i2c); 358 287 time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), 359 288 i2c->adap.timeout); 360 - octeon_i2c_int_disable(i2c); 289 + i2c->int_disable(i2c); 361 290 if (!time_left) { 362 291 dev_dbg(i2c->dev, "%s: timeout\n", __func__); 363 292 return -ETIMEDOUT; ··· 455 384 { 456 385 int time_left; 457 386 458 - octeon_i2c_hlc_int_enable(i2c); 387 + i2c->hlc_int_enable(i2c); 459 388 time_left = wait_event_timeout(i2c->queue, 460 389 octeon_i2c_hlc_test_ready(i2c), 461 390 i2c->adap.timeout); 462 - octeon_i2c_int_disable(i2c); 391 + i2c->hlc_int_disable(i2c); 463 392 if (!time_left) { 464 393 octeon_i2c_hlc_int_clear(i2c); 465 394 return -ETIMEDOUT; ··· 1017 946 static int octeon_i2c_probe(struct platform_device *pdev) 1018 947 { 1019 948 struct device_node *node = pdev->dev.of_node; 949 + int irq, result = 0, hlc_irq = 0; 1020 950 struct resource *res_mem; 1021 951 struct octeon_i2c *i2c; 1022 - int irq, result = 0; 952 + bool cn78xx_style; 1023 953 1024 - /* All adaptors have an irq. */ 1025 - irq = platform_get_irq(pdev, 0); 1026 - if (irq < 0) 1027 - return irq; 954 + cn78xx_style = of_device_is_compatible(node, "cavium,octeon-7890-twsi"); 955 + if (cn78xx_style) { 956 + hlc_irq = platform_get_irq(pdev, 0); 957 + if (hlc_irq < 0) 958 + return hlc_irq; 959 + 960 + irq = platform_get_irq(pdev, 2); 961 + if (irq < 0) 962 + return irq; 963 + } else { 964 + /* All adaptors have an irq. */ 965 + irq = platform_get_irq(pdev, 0); 966 + if (irq < 0) 967 + return irq; 968 + } 1028 969 1029 970 i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); 1030 971 if (!i2c) { ··· 1070 987 init_waitqueue_head(&i2c->queue); 1071 988 1072 989 i2c->irq = irq; 990 + 991 + if (cn78xx_style) { 992 + i2c->hlc_irq = hlc_irq; 993 + 994 + i2c->int_enable = octeon_i2c_int_enable78; 995 + i2c->int_disable = octeon_i2c_int_disable78; 996 + i2c->hlc_int_enable = octeon_i2c_hlc_int_enable78; 997 + i2c->hlc_int_disable = octeon_i2c_hlc_int_disable78; 998 + 999 + irq_set_status_flags(i2c->irq, IRQ_NOAUTOEN); 1000 + irq_set_status_flags(i2c->hlc_irq, IRQ_NOAUTOEN); 1001 + 1002 + result = devm_request_irq(&pdev->dev, i2c->hlc_irq, 1003 + octeon_i2c_hlc_isr78, 0, 1004 + DRV_NAME, i2c); 1005 + if (result < 0) { 1006 + dev_err(i2c->dev, "failed to attach interrupt\n"); 1007 + goto out; 1008 + } 1009 + } else { 1010 + i2c->int_enable = octeon_i2c_int_enable; 1011 + i2c->int_disable = octeon_i2c_int_disable; 1012 + i2c->hlc_int_enable = octeon_i2c_hlc_int_enable; 1013 + i2c->hlc_int_disable = octeon_i2c_int_disable; 1014 + } 1073 1015 1074 1016 result = devm_request_irq(&pdev->dev, i2c->irq, 1075 1017 octeon_i2c_isr, 0, DRV_NAME, i2c); ··· 1142 1034 1143 1035 static const struct of_device_id octeon_i2c_match[] = { 1144 1036 { .compatible = "cavium,octeon-3860-twsi", }, 1037 + { .compatible = "cavium,octeon-7890-twsi", }, 1145 1038 {}, 1146 1039 }; 1147 1040 MODULE_DEVICE_TABLE(of, octeon_i2c_match);