📄 twl4030_core.c
字号:
local_irq_disable(); if (retval != IRQ_HANDLED) printk(KERN_ERR "ISR for TWL4030 module" " irq %d can't handle interrupt\n", irq); /* * Here is where we should call the unmask method, but * again we won't bother since it is NULL. */ } else printk(KERN_CRIT "TWL4030 module irq %d has no ISR" " but can't be masked!\n", irq); } else printk(KERN_CRIT "TWL4030 module irq %d is disabled but can't" " be masked!\n", irq);}/* * twl4030_irq_thread() runs as a kernel thread. It queries the twl4030 * interrupt controller to see which modules are generating interrupt requests * and then calls the desc->handle method for each module requesting service. */static int twl4030_irq_thread(void *data){ int irq = (int)data; irq_desc_t *desc = irq_desc + irq; static unsigned i2c_errors; const static unsigned max_i2c_errors = 100; daemonize("twl4030-irq"); current->flags |= PF_NOFREEZE; while (!kthread_should_stop()) { int ret; int module_irq; u8 pih_isr; wait_for_completion_interruptible(&irq_event); ret = twl4030_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr, REG_PIH_ISR_P1); if (ret) { printk(KERN_WARNING "I2C error %d while reading TWL4030" " PIH ISR register.\n", ret); if (++i2c_errors >= max_i2c_errors) { printk(KERN_ERR "Maximum I2C error count" " exceeded. Terminating %s.\n", __FUNCTION__); break; } continue; } for (module_irq = IH_TWL4030_BASE; 0 != pih_isr; pih_isr >>= 1, module_irq++) { if (pih_isr & 0x1) { irq_desc_t *d = irq_desc + module_irq; local_irq_disable(); d->handle_irq(module_irq, d); local_irq_enable(); } } desc->chip->unmask(irq); } return 0;}/* * do_twl4030_irq() is the desc->handle method for the twl4030 interrupt. * This is a chained interrupt, so there is no desc->action method for it. * Now we need to query the interrupt controller in the twl4030 to determine * which module is generating the interrupt request. However, we can't do i2c * transactions in interrupt context, so we must defer that work to a kernel * thread. All we do here is acknowledge and mask the interrupt and wakeup * the kernel thread. */static void do_twl4030_irq(unsigned int irq, irq_desc_t *desc){ const unsigned int cpu = smp_processor_id(); /* * Earlier this was desc->triggered = 1; */ desc->status |= IRQ_LEVEL; /* * Acknowledge, clear _AND_ disable the interrupt. */ desc->chip->ack(irq); if (!desc->depth) { kstat_cpu(cpu).irqs[irq]++; complete(&irq_event); }}/* attach a client to the adapter */static int twl4030_detect_client(struct i2c_adapter *adapter, unsigned char sid){ int err = 0; struct twl4030_client *client; if (unlikely(sid >= TWL4030_NUM_SLAVES)) { pr_err("sid[%d] > MOD_LAST[%d]\n", sid, TWL4030_NUM_SLAVES); return -EPERM; } /* Check basic functionality */ if (!(err = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_WRITE_BYTE))) { pr_err("SlaveID=%d functionality check failed\n", sid); return err; } client = &(twl4030_modules[sid]); if (unlikely(client->inuse)) { pr_err("Client %s is already in use\n", client->client_name); return -EPERM; } memset(&(client->client), 0, sizeof(struct i2c_client)); client->client.addr = client->address; client->client.adapter = adapter; client->client.driver = &twl4030_driver; memcpy(&(client->client.name), client->client_name, sizeof(TWL_CLIENT_STRING) + 1); pr_info("TWL4030: TRY attach Slave %s on Adapter %s [%x]\n", client->client_name, adapter->name, err); if ((err = i2c_attach_client(&(client->client)))) { pr_err("Couldn't attach Slave %s on Adapter" "%s [%x]\n", client->client_name, adapter->name, err); } else { client->inuse = TWL_CLIENT_USED; init_MUTEX(&client->xfer_lock); } return err;}/* adapter callback */static int twl4030_attach_adapter(struct i2c_adapter *adapter){ int i; int ret = 0; static int twl_i2c_adapter = 1; for (i = 0; i < TWL4030_NUM_SLAVES; i++) { /* Check if I need to hook on to this adapter or not */ if (twl4030_modules[i].adapter_index == twl_i2c_adapter) { if ((ret = twl4030_detect_client(adapter, i))) goto free_client; } } twl_i2c_adapter++; /* * Check if the PIH module is initialized, if yes, then init * the T2 Interrupt subsystem */ if ((twl4030_modules[twl4030_map[TWL4030_MODULE_PIH].sid].inuse == TWL_CLIENT_USED) && (twl_irq_used != USED)) { twl_init_irq(); twl_irq_used = USED; } twl4030_sysfs_debug_create(); return 0;free_client: pr_err("TWL_CLIENT(Idx=%d] registration failed[0x%x]\n",i,ret); /* ignore current slave..it never got registered */ i--; while (i >= 0) { /* now remove all those from the current adapter... */ if (twl4030_modules[i].adapter_index == twl_i2c_adapter) (void)twl4030_detach_client(&(twl4030_modules[i].client)); i--; } return ret;}/* adapter's callback */static int twl4030_detach_client(struct i2c_client *iclient){ int err; twl4030_sysfs_debug_remove(); if ((err = i2c_detach_client(iclient))) { pr_err("Client detach failed\n"); return err; } return 0;}struct task_struct *start_twl4030_irq_thread(int irq){ struct task_struct *thread; init_completion(&irq_event); thread = kthread_run(twl4030_irq_thread, (void *)irq, "twl4030 irq %d", irq); if (!thread) pr_err("%s: could not create twl4030 irq %d thread!\n", __FUNCTION__,irq); return thread;}static void twl_init_irq(void){ int i = 0; int res = 0; int line = 0; /* * We end up with interrupts from other modules before * they get a chance to handle them... */ /* PWR_ISR1 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x00); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* PWR_ISR2 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x02); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* PWR_IMR1 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x1); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* PWR_IMR2 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x3); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* Clear off any other pending interrupts on power */ /* PWR_ISR1 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x00); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* PWR_ISR2 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x02); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* POWER HACK (END) */ /* Slave address 0x4A */ /* BCIIMR1_1 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x3); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* BCIIMR1_2 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x4); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* BCIIMR2_1 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x7); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* BCIIMR2_2 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x8); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* MAD C */ /* MADC_IMR1 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, 0xFF, 0x62); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* MADC_IMR2 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, 0xFF, 0x64); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* key Pad */ /* KEYPAD - IMR1 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, 0xFF, (0x12)); if (res < 0) { line = __LINE__; goto irq_exit_path; } { u8 clear; /* Clear ISR */ twl4030_i2c_read_u8(TWL4030_MODULE_KEYPAD, &clear, 0x11); twl4030_i2c_read_u8(TWL4030_MODULE_KEYPAD, &clear, 0x11); } /* KEYPAD - IMR2 */ res = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, 0xFF, (0x14)); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* Slave address 0x49 */ /* GPIO_IMR1A */ res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x1C)); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* GPIO_IMR2A */ res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x1D)); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* GPIO_IMR3A */ res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x1E)); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* GPIO_IMR1B */ res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x22)); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* GPIO_IMR2B */ res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x23)); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* GPIO_IMR3B */ res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x24)); if (res < 0) { line = __LINE__; goto irq_exit_path; } /* install an irq handler for each of the PIH modules */ for (i = IH_TWL4030_BASE; i < IH_TWL4030_END; i++) { set_irq_chip(i, &twl4030_irq_chip); set_irq_handler(i, do_twl4030_module_irq); set_irq_flags(i, IRQF_VALID); } /* install an irq handler to demultiplex the TWL4030 interrupt */ set_irq_data(TWL4030_IRQNUM, start_twl4030_irq_thread(TWL4030_IRQNUM)); set_irq_type(TWL4030_IRQNUM, IRQT_FALLING); set_irq_chained_handler(TWL4030_IRQNUM, do_twl4030_irq); res = power_companion_init(); if (res < 0) { line = __LINE__; goto irq_exit_path; }irq_exit_path: if (res) pr_err("Unable to register interrupt subsystem[%d][%d]\n", res,line);}static int __init twl4030_init(void){ int res; if ((res = i2c_add_driver(&twl4030_driver))) { printk(KERN_ERR "TWL4030: Driver registration failed \n"); return res; } pr_info(KERN_INFO "TWL4030: Driver registration complete.\n"); return 0;}static void __exit twl4030_exit(void){ i2c_del_driver(&twl4030_driver); twl_irq_used = FREE;}subsys_initcall(twl4030_init);module_exit(twl4030_exit);EXPORT_SYMBOL(twl4030_i2c_write_u8);EXPORT_SYMBOL(twl4030_i2c_read_u8);EXPORT_SYMBOL(twl4030_i2c_read);EXPORT_SYMBOL(twl4030_i2c_write);MODULE_AUTHOR("Texas Instruments, Inc.");MODULE_DESCRIPTION("I2C Core interface for TWL4030");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -