/* * drivers/thermal/sunxi_thermal/sunxi_ths_combine.c * * Copyright (C) 2013-2024 allwinner. * JiaRui Xiao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define NEED_DEBUG (0) #if NEED_DEBUG #define DEBUG #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PM #include #endif #include "sunxi_ths.h" #include "sunxi_ths_core.h" #define thsprintk(fmt, arg...) \ pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg) static LIST_HEAD(controller_list); static DEFINE_MUTEX(controller_list_lock); static const char * const combine_types[] = { [COMBINE_MAX_TEMP] = "max", [COMBINE_AVG_TMP] = "avg", [COMBINE_MIN_TMP] = "min", }; static enum combine_ths_temp_type get_combine_type(const char *t) { int i; for (i = 0; i < ARRAY_SIZE(combine_types); i++) if (!strcasecmp(t, combine_types[i])) { return i; } return 0; } static int sunxi_combine_get_temp(void *data, int *temperature) { struct sunxi_ths_sensor *sensor = data; struct sunxi_ths_controller *controller = sensor->combine->controller; int i, ret, is_suspend; u32 sensor_id; int temp = 0, taget = 0; is_suspend = atomic_read(&sensor->is_suspend); if (!is_suspend) { switch (sensor->combine->type) { case COMBINE_MAX_TEMP: for (i = 0, taget = -40; i < sensor->combine->combine_ths_count; i++) { sensor_id = sensor->combine->combine_ths_id[i]; ret = controller->ops->get_temp(controller, sensor_id, &temp); if (ret) return ret; if (temp > taget) taget = temp; } break; case COMBINE_AVG_TMP: for (i = 0, taget = 0; i < sensor->combine->combine_ths_count; i++) { sensor_id = sensor->combine->combine_ths_id[i]; ret = controller->ops->get_temp(controller, sensor_id, &temp); if (ret) return ret; taget += temp; } do_div(taget, sensor->combine->combine_ths_count); break; case COMBINE_MIN_TMP: for (i = 0, taget = 180; i < sensor->combine->combine_ths_count; i++) { sensor_id = sensor->combine->combine_ths_id[i]; ret = controller->ops->get_temp(controller, sensor_id, &temp); if (ret) return ret; if (temp < taget) taget = temp; } break; default: break; } *temperature = taget; sensor->last_temp = taget; }else{ *temperature = sensor->last_temp; } thsprintk("get temp %d\n", (*temperature)); return 0; } static struct sunxi_ths_controller * combine_find_controller(struct device_node *np) { struct sunxi_ths_controller *controller; struct device_node *pnp = of_get_parent(np); if (IS_ERR_OR_NULL(pnp)) { pr_err("ths combine: get parent err\n"); return NULL; } mutex_lock(&controller_list_lock); list_for_each_entry(controller, &controller_list, node){ if(pnp == controller->dev->of_node){ mutex_unlock(&controller_list_lock); return controller; } } mutex_unlock(&controller_list_lock); return NULL; } struct sunxi_ths_controller * sunxi_ths_controller_register(struct device *dev ,struct sunxi_ths_controller_ops *ops, void *data) { struct sunxi_ths_controller *controller = NULL; struct device_node *np = dev->of_node; struct device_node *combine_np = NULL; struct platform_device *combine_dev = NULL; int i = 0, combine_num = 0; char combine_name[50]; controller = kzalloc(sizeof(*controller), GFP_KERNEL); if (IS_ERR_OR_NULL(controller)) { pr_err("ths controller: not enough memory for controller data\n"); return NULL; } if (of_property_read_u32(np, "combine_num", &combine_num)) { pr_err("%s: get combine_num failed\n", __func__); return NULL; } controller->dev = dev; controller->ops = ops; controller->data = data; controller->combine_num = combine_num; atomic_set(&controller->is_suspend, 0); atomic_set(&controller->usage, 0); mutex_init(&controller->lock); INIT_LIST_HEAD(&controller->combine_list); mutex_lock(&controller_list_lock); list_add_tail(&controller->node, &controller_list); mutex_unlock(&controller_list_lock); for_each_child_of_node(np, combine_np) { combine_dev = kzalloc(sizeof(*combine_dev), GFP_KERNEL); if (IS_ERR_OR_NULL(combine_dev)) { pr_err("combine_dev: not enough memory for combine_dev data\n"); return NULL; } combine_dev->dev.of_node = combine_np; sprintf(combine_name, "sunxi_ths_combine_%d", i); combine_dev->name = combine_name; combine_dev->id = PLATFORM_DEVID_NONE; platform_device_register(combine_dev); i++; } return controller; } EXPORT_SYMBOL(sunxi_ths_controller_register); void sunxi_ths_controller_unregister(struct sunxi_ths_controller *controller) { struct sunxi_ths_sensor *sensor; struct platform_device *pdev; list_for_each_entry(sensor, &controller->combine_list, node) { pdev = sensor->pdev; platform_device_unregister(pdev); kfree(pdev); } mutex_lock(&controller_list_lock); list_del(&controller->node); mutex_unlock(&controller_list_lock); kfree(controller); } EXPORT_SYMBOL(sunxi_ths_controller_unregister); static int sunxi_combine_parse(struct sunxi_ths_sensor *sensor) { struct device_node *np = NULL; struct sunxi_ths_combine_disc *combine; const char *type = NULL; int combine_sensor_num = 0; int i = 0; combine = kzalloc(sizeof(*combine), GFP_KERNEL); if (IS_ERR_OR_NULL(combine)) { pr_err("ths combine: not enough memory for combine data\n"); return -ENOMEM; } np = sensor->pdev->dev.of_node; combine->controller = combine_find_controller(np); if (!combine->controller) { pr_err("sensor find no controller\n"); goto parse_fail; } /* getting the combine sensor have sensor num*/ if (of_property_read_u32(np, "combine_sensor_num", &combine_sensor_num)) { pr_err("%s: get sensor_num failed\n", __func__); return -EBUSY; } else { combine->combine_ths_count = combine_sensor_num; } /* getting the combine sensor how to calcular all the sensor temp */ if (of_property_read_string(np, "combine_sensor_temp_type", &type)) { pr_err("%s: get combine type failed\n", __func__); return -EBUSY; } else { combine->type = get_combine_type(type); } /* getting the combine sensor where focus on */ if (of_property_read_string(np, "combine_sensor_type", &combine->combine_ths_type)) { pr_err("%s: get sensor_num failed\n", __func__); return -EBUSY; } /* getting the combine sensor include all sensor id */ for (i = 0; i < combine->combine_ths_count; i++) { if (of_property_read_u32_index(np, "combine_sensor_id", i, &(combine->combine_ths_id[i]))) { pr_err("node combine chn get failed!\n"); goto parse_fail; } } sensor->combine = combine; list_add_tail(&sensor->node, &combine->controller->combine_list); return 0; parse_fail: kfree(combine); return -ENOMEM; } static int sunxi_combine_probe(struct platform_device *pdev) { int err = 0, id = 0; struct sunxi_ths_sensor *sensor; struct sunxi_ths_data *ths_data; thsprintk("sunxi ths sensor probe start !\n"); if (!pdev->dev.of_node) { pr_err("sunxi ths sensor register err!\n"); return -EBUSY; } sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); if (IS_ERR_OR_NULL(sensor)) { pr_err("ths combine: not enough memory for sensor data\n"); return -EBUSY; } sensor->pdev = pdev; err = sunxi_combine_parse(sensor); if (err) goto fail; sscanf(pdev->name, "sunxi_ths_combine_%d", &id); sensor->sensor_id = id; atomic_add(1, &sensor->combine->controller->usage); sensor->tz = thermal_zone_of_sensor_register(&pdev->dev, id, sensor, sunxi_combine_get_temp, NULL); if (IS_ERR(sensor->tz)) { pr_err("sunxi ths sensor register err!\n"); goto fail1; } ths_data = (struct sunxi_ths_data *)sensor->combine->controller->data; ths_data->comb_sensor[id] = sensor; dev_set_drvdata(&pdev->dev, sensor); atomic_set(&sensor->is_suspend, 0); if (sensor->tz->ops->set_mode) sensor->tz->ops->set_mode(sensor->tz, THERMAL_DEVICE_ENABLED); else thermal_zone_device_update(sensor->tz); thsprintk("%s probe end!\n", pdev->name); return 0; fail1: kfree(sensor->combine); fail: kfree(sensor); return -EBUSY; } static int sunxi_combine_remove(struct platform_device *pdev) { struct sunxi_ths_sensor *sensor; sensor = dev_get_drvdata(&pdev->dev); thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tz); kfree(sensor->combine); kfree(sensor); return 0; } #ifdef CONFIG_PM static int sunxi_combine_suspend(struct device *dev) { struct sunxi_ths_sensor *sensor = dev_get_drvdata(dev); struct sunxi_ths_controller *controller = sensor->combine->controller; thsprintk("enter: sunxi_ths_suspend.\n"); atomic_set(&sensor->is_suspend, 1); if (atomic_sub_return(1, &controller->usage) == 0) if (controller->ops->suspend) return controller->ops->suspend(controller); return 0; } static int sunxi_combine_resume(struct device *dev) { struct sunxi_ths_sensor *sensor = dev_get_drvdata(dev); struct sunxi_ths_controller *controller = sensor->combine->controller; int max_combine; thsprintk("enter: sunxi_ths_resume.\n"); max_combine = sensor->combine->controller->combine_num; if (atomic_add_return(1, &controller->usage) == max_combine) if (controller->ops->resume) controller->ops->resume(controller); atomic_set(&sensor->is_suspend, 0); return 0; } static const struct dev_pm_ops sunxi_combine_pm_ops = { .suspend = sunxi_combine_suspend, .resume = sunxi_combine_resume, }; #endif static const struct of_device_id of_sunxi_combine_ths_match[] = { { .compatible = "allwinner,ths_combine0" }, { .compatible = "allwinner,ths_combine1" }, { .compatible = "allwinner,ths_combine2" }, { /* end */ } }; static struct platform_driver sunxi_combine_driver = { .probe = sunxi_combine_probe, .remove = sunxi_combine_remove, .driver = { .name = SUNXI_THS_COMBINE_NAME, .owner = THIS_MODULE, .of_match_table = of_sunxi_combine_ths_match, #ifdef CONFIG_PM .pm = &sunxi_combine_pm_ops, #endif }, }; static int __init sunxi_ths_combine_init(void) { return platform_driver_register(&sunxi_combine_driver); } static void __exit sunxi_ths_combine_exit(void) { platform_driver_unregister(&sunxi_combine_driver); mutex_destroy(&controller_list_lock); } subsys_initcall_sync(sunxi_ths_combine_init); module_exit(sunxi_ths_combine_exit); MODULE_DESCRIPTION("SUNXI combine thermal sensor driver"); MODULE_AUTHOR("QIn"); MODULE_LICENSE("GPL v2");