#include #include #include #include #include #include #include #include #include #include #include #include "sunxi_gpu_cooling.h" #define SUNXI_GPU_COOLING_NAME "sunxi-gpu-cooling" static struct sunxi_gpu_cooling_device *main_cooling = NULL; static int sunxi_gpufreq_update_state(struct sunxi_gpu_cooling_device *cooling_device) { if(!cooling_device->cool) return 0; pr_info("gpu cooling callback set freq limit %d\n",cooling_device->gpu_freq_limit); return cooling_device->cool(cooling_device->gpu_freq_limit); } static int sunxi_gpu_apply_cooling(struct sunxi_gpu_cooling_device *cooling_device, unsigned long cooling_state) { unsigned long flags; /* struct thermal_instance *instance; */ /* int temperature = 0; */ /* Check if the old cooling action is same as new cooling action */ if (cooling_device->cooling_state == cooling_state) return 0; if(cooling_state >= cooling_device->state_num) return -EINVAL; cooling_device->cooling_state = cooling_state; spin_lock_irqsave(&cooling_device->lock, flags); cooling_device->gpu_freq_limit = cooling_device->freq_table[cooling_state]; spin_unlock_irqrestore(&cooling_device->lock, flags); return sunxi_gpufreq_update_state(cooling_device); } /** * gpu_cooling_get_max_state - callback function to get the max cooling state. * @cdev: thermal cooling device pointer. * @state: fill this variable with the max cooling state. */ static int gpu_cooling_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct sunxi_gpu_cooling_device *cooling_device = cdev->devdata; *state = cooling_device->state_num - 1; return 0; } /** * gpu_cooling_get_cur_state - callback function to get the current cooling state. * @cdev: thermal cooling device pointer. * @state: fill this variable with the current cooling state. */ static int gpu_cooling_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct sunxi_gpu_cooling_device *cooling_device = cdev->devdata; *state = cooling_device->cooling_state; return 0; } /** * gpu_cooling_set_cur_state - callback function to set the current cooling state. * @cdev: thermal cooling device pointer. * @state: set this variable to the current cooling state. */ static int gpu_cooling_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { struct sunxi_gpu_cooling_device *cooling_device = cdev->devdata; return sunxi_gpu_apply_cooling(cooling_device, state); } static struct thermal_cooling_device_ops const sunxi_gpu_cooling_ops = { .get_max_state = gpu_cooling_get_max_state, .get_cur_state = gpu_cooling_get_cur_state, .set_cur_state = gpu_cooling_set_cur_state, }; int gpu_thermal_cool_register(int (*cool) (int)) { struct thermal_cooling_device *cool_dev; if(IS_ERR_OR_NULL(cool)) return -1; if(IS_ERR_OR_NULL(main_cooling)) return -1; main_cooling->cool = cool; cool_dev = thermal_of_cooling_device_register(main_cooling->dev->of_node, SUNXI_GPU_COOLING_NAME, main_cooling, &sunxi_gpu_cooling_ops); if (!cool_dev){ main_cooling->cool = NULL; return -1; } main_cooling->cool_dev = cool_dev; pr_info("gpu cooling callback register Success\n"); return 0; } EXPORT_SYMBOL(gpu_thermal_cool_register); int gpu_thermal_cool_unregister(void) { if(IS_ERR_OR_NULL(main_cooling)) return 0; thermal_cooling_device_unregister(main_cooling->cool_dev); main_cooling->cool = NULL; pr_info("gpu cooling callback unregister Success\n"); return 0; } EXPORT_SYMBOL(gpu_thermal_cool_unregister); static struct sunxi_gpu_cooling_device * sunxi_gpu_cooling_parse(struct platform_device *pdev) { struct device_node *np = NULL; struct sunxi_gpu_cooling_device *gpu_cool_dev; int i; char name[32]; np = pdev->dev.of_node; if (!of_device_is_available(np)) { pr_err("%s: gpu cooling is disable", __func__); return NULL; } gpu_cool_dev = kzalloc(sizeof(*gpu_cool_dev), GFP_KERNEL); if (IS_ERR_OR_NULL(gpu_cool_dev)) { pr_err("gpu_cool_dev: not enough memory for cooling data\n"); return NULL; } if (of_property_read_u32(np, "state_cnt", &gpu_cool_dev->state_num)) { pr_err("%s: get state_cnt failed", __func__); goto parse_fail; } for(i = 0; i < gpu_cool_dev->state_num; i++){ sprintf(name, "state%d", i); if (of_property_read_u32(np, (const char *)&name, &gpu_cool_dev->freq_table[i])) { pr_err("node %s get failed!\n", name); goto parse_fail; } } gpu_cool_dev->gpu_freq_limit = gpu_cool_dev->freq_table[0]; gpu_cool_dev->gpu_freq_roof = gpu_cool_dev->gpu_freq_limit; return gpu_cool_dev; parse_fail: kfree(gpu_cool_dev); return NULL; } static int sunxi_gpu_cooling_probe(struct platform_device *pdev) { struct sunxi_gpu_cooling_device *gpu_cool_dev; pr_info("sunxi gpu cooling probe start !\n"); if(!pdev->dev.of_node){ pr_err("%s:sunxi gpu cooling device tree err!\n",__func__); return -EBUSY; } gpu_cool_dev = sunxi_gpu_cooling_parse(pdev); if(!gpu_cool_dev) return -EBUSY; gpu_cool_dev->dev = &pdev->dev; spin_lock_init(&gpu_cool_dev->lock); main_cooling = gpu_cool_dev; gpu_cool_dev->cooling_state = -1; dev_set_drvdata(&pdev->dev, gpu_cool_dev); pr_info("CPU gpu cooling register Success\n"); return 0; } static int sunxi_gpu_cooling_remove(struct platform_device *pdev) { struct sunxi_gpu_cooling_device *gpu_cool_dev = dev_get_drvdata(&pdev->dev); if(gpu_cool_dev->cool_dev) thermal_cooling_device_unregister(gpu_cool_dev->cool_dev); kfree(gpu_cool_dev); gpu_cool_dev = NULL; pr_info("CPU budget cooling unregister Success\n"); return 0; } #ifdef CONFIG_OF /* Translate OpenFirmware node properties into platform_data */ static struct of_device_id sunxi_gpu_cooling_of_match[] = { { .compatible = "allwinner,gpu_cooling", }, { }, }; MODULE_DEVICE_TABLE(of, sunxi_gpu_cooling_of_match); #else /* !CONFIG_OF */ #endif static struct platform_driver sunxi_gpu_cooling_driver = { .probe = sunxi_gpu_cooling_probe, .remove = sunxi_gpu_cooling_remove, .driver = { .name = SUNXI_GPU_COOLING_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(sunxi_gpu_cooling_of_match), } }; module_platform_driver(sunxi_gpu_cooling_driver); MODULE_DESCRIPTION("SUNXI gpu cooling driver"); MODULE_AUTHOR("QIn"); MODULE_LICENSE("GPL v2");