static int tmu_suspend(struct platform_device *pdev, pm_message_t state) { struct tmu_info *info = platform_get_drvdata(pdev); pm_tmu_save(info); return 0; }
static irqreturn_t tmu_irq(int irq, void *id) { struct tmu_info *info = id; unsigned int status; disable_irq_nosync(irq); status = __raw_readl(info->tmu_base + INTSTAT); /* To handle multiple interrupt pending, * interrupt by high temperature are serviced with priority. */ #if defined(CONFIG_TC_VOLTAGE) if (status & INTSTAT_FALL0) { pr_info("TC interrupt occured..!\n"); __raw_writel(INTCLEAR_FALL0, info->tmu_base + INTCLEAR); info->tmu_state = TMU_STATUS_TC; } else if (status & INTSTAT_RISE2) { #else if (status & INTSTAT_RISE2) { #endif pr_info("Tripping interrupt occured..!\n"); info->tmu_state = TMU_STATUS_TRIPPED; __raw_writel(INTCLEAR_RISE2, info->tmu_base + INTCLEAR); } else if (status & INTSTAT_RISE1) { pr_info("Warning interrupt occured..!\n"); __raw_writel(INTCLEAR_RISE1, info->tmu_base + INTCLEAR); info->tmu_state = TMU_STATUS_WARNING; } else if (status & INTSTAT_RISE0) { pr_info("Throttling interrupt occured..!\n"); __raw_writel(INTCLEAR_RISE0, info->tmu_base + INTCLEAR); info->tmu_state = TMU_STATUS_THROTTLED; } else { pr_err("%s: TMU interrupt error\n", __func__); return -ENODEV; } queue_delayed_work_on(0, tmu_monitor_wq, &info->polling, usecs_to_jiffies(1 * 1000)); return IRQ_HANDLED; } static irqreturn_t exynos4210_tmu_irq(int irq, void *id) { struct tmu_info *info = id; unsigned int status; disable_irq_nosync(irq); status = __raw_readl(info->tmu_base + INTSTAT); if (status & INTSTAT2) { pr_info("Tripping interrupt occured..!\n"); info->tmu_state = TMU_STATUS_TRIPPED; __raw_writel(INTCLEAR2, info->tmu_base + INTCLEAR); } else if (status & INTSTAT1) { pr_info("Warning interrupt occured..!\n"); __raw_writel(INTCLEAR1, info->tmu_base + INTCLEAR); info->tmu_state = TMU_STATUS_WARNING; } else if (status & INTSTAT0) { pr_info("Throttling interrupt occured..!\n"); __raw_writel(INTCLEAR0, info->tmu_base + INTCLEAR); info->tmu_state = TMU_STATUS_THROTTLED; } else { pr_err("%s: TMU interrupt error\n", __func__); return -ENODEV; } queue_delayed_work_on(0, tmu_monitor_wq, &info->polling, usecs_to_jiffies(1000)); return IRQ_HANDLED; } static int __devinit tmu_probe(struct platform_device *pdev) { struct tmu_info *info; struct resource *res; int ret = 0; pr_debug("%s: probe=%p\n", __func__, pdev); info = kzalloc(sizeof(struct tmu_info), GFP_KERNEL); if (!info) { dev_err(&pdev->dev, "failed to alloc memory!\n"); ret = -ENOMEM; goto err_nomem; } pr_emerg("TMU: Memory Allocation Sucessful\n"); platform_set_drvdata(pdev, info); pr_emerg("TMU: Platform data set\n"); info->dev = &pdev->dev; pr_emerg("TMU: Copied the Dev access Information \n"); info->irq = platform_get_irq(pdev, 0); if (info->irq < 0) { dev_err(&pdev->dev, "no irq for thermal\n"); ret = -ENOENT; goto err_noirq; } if (soc_is_exynos4210()) ret = request_irq(info->irq, exynos4210_tmu_irq, IRQF_DISABLED, "tmu interrupt", info); else ret = request_irq(info->irq, tmu_irq, IRQF_DISABLED, "tmu interrupt", info); if (ret) { dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq, ret); goto err_noirq; } pr_emerg("TMU: IRQ Granted!\n"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "failed to get memory region resource\n"); ret = -ENODEV; goto err_nores; } pr_emerg("TMU: IO Resource alloced on Memory\n"); info->ioarea = request_mem_region(res->start, res->end-res->start+1, pdev->name); if (!(info->ioarea)) { dev_err(&pdev->dev, "failed to reserve memory region\n"); ret = -EBUSY; goto err_nores; } pr_emerg("TMU: Memory area resersed\n"); info->tmu_base = ioremap(res->start, (res->end - res->start) + 1); if (!(info->tmu_base)) { dev_err(&pdev->dev, "failed ioremap()\n"); ret = -EINVAL; goto err_nomap; } pr_emerg("TMU: IO Memory Remapped\n"); if (thermal_create_sysfs_file(&pdev->dev)) goto err_sysfs; pr_emerg("TMU: Created Sysfs\n"); tmu_monitor_wq = create_freezable_workqueue("tmu"); if (!tmu_monitor_wq) { dev_err(&pdev->dev, "Creation of tmu_monitor_wq failed\n"); ret = -EFAULT; goto err_wq; } pr_emerg("TMU: Workqueue Created\n"); INIT_DELAYED_WORK_DEFERRABLE(&info->polling, tmu_monitor); pr_emerg("TMU: Work Created\n"); #ifdef CONFIG_TMU_DEBUG INIT_DELAYED_WORK_DEFERRABLE(&info->monitor, cur_temp_monitor); #endif print_temperature_params(info); pr_emerg("TMU: Printed Parameters\n"); ret = tmu_initialize(pdev); if (ret < 0) goto err_noinit; #ifdef CONFIG_TMU_DEBUG queue_delayed_work_on(0, tmu_monitor_wq, &info->monitor, info->sampling_rate); #endif pr_info("Tmu Initialization is sucessful...!\n"); return ret; err_noinit: destroy_workqueue(tmu_monitor_wq); err_wq: thermal_remove_sysfs_file(&pdev->dev); err_sysfs: iounmap(info->tmu_base); err_nomap: release_resource(info->ioarea); err_nores: free_irq(info->irq, info); err_noirq: kfree(info); info = NULL; err_nomem: dev_err(&pdev->dev, "initialization failed.\n"); return ret; } static int __devinit tmu_remove(struct platform_device *pdev) { struct tmu_info *info = platform_get_drvdata(pdev); cancel_delayed_work(&info->polling); destroy_workqueue(tmu_monitor_wq); thermal_remove_sysfs_file(&pdev->dev); iounmap(info->tmu_base); release_resource(info->ioarea); free_irq(info->irq, (void *)pdev); kfree(info); info = NULL; pr_info("%s is removed\n", dev_name(&pdev->dev)); return 0; } #ifdef CONFIG_PM static int tmu_suspend(struct platform_device *pdev, pm_message_t state) { struct tmu_info *info = platform_get_drvdata(pdev); pm_tmu_save(info); return 0; } static int tmu_resume(struct platform_device *pdev) { struct tmu_info *info = platform_get_drvdata(pdev); #if defined(CONFIG_TC_VOLTAGE) struct tmu_data *data = info->dev->platform_data; #endif pm_tmu_restore(info); #if defined(CONFIG_TC_VOLTAGE) /* s/w workaround for fast service when interrupt is not occured, * such as current temp is lower than tc interrupt temperature * or current temp is continuosly increased. */ mdelay(1); if (get_cur_temp(info) <= data->ts.start_tc) { disable_irq_nosync(info->irq); if (exynos_tc_volt(info, 1) < 0) pr_err("%s\n", __func__); info->tmu_state = TMU_STATUS_TC; already_limit = 1; queue_delayed_work_on(0, tmu_monitor_wq, &info->polling, usecs_to_jiffies(1 * 1000)); } #endif return 0; } #else #define tmu_suspend NULL #define tmu_resume NULL #endif static struct platform_driver tmu_driver = { .probe = tmu_probe, .remove = tmu_remove, .suspend = tmu_suspend, .resume = tmu_resume, .driver = { .name = "tmu", .owner = THIS_MODULE, }, }; static int __init tmu_driver_init(void) { return platform_driver_register(&tmu_driver); } late_initcall(tmu_driver_init);