diff options
Diffstat (limited to 'drivers/misc/therm_fan_est.c')
-rw-r--r-- | drivers/misc/therm_fan_est.c | 283 |
1 files changed, 243 insertions, 40 deletions
diff --git a/drivers/misc/therm_fan_est.c b/drivers/misc/therm_fan_est.c index 9312a0174..73c082ee0 100644 --- a/drivers/misc/therm_fan_est.c +++ b/drivers/misc/therm_fan_est.c | |||
@@ -52,6 +52,10 @@ struct therm_fan_estimator { | |||
52 | int active_trip_temps[MAX_ACTIVE_STATES]; | 52 | int active_trip_temps[MAX_ACTIVE_STATES]; |
53 | int active_hysteresis[MAX_ACTIVE_STATES]; | 53 | int active_hysteresis[MAX_ACTIVE_STATES]; |
54 | int active_trip_temps_hyst[(MAX_ACTIVE_STATES << 1) + 1]; | 54 | int active_trip_temps_hyst[(MAX_ACTIVE_STATES << 1) + 1]; |
55 | struct thermal_zone_params *tzp; | ||
56 | int num_resources; | ||
57 | int trip_length; | ||
58 | const char *name; | ||
55 | }; | 59 | }; |
56 | 60 | ||
57 | 61 | ||
@@ -350,73 +354,261 @@ static struct sensor_device_attribute therm_fan_est_nodes[] = { | |||
350 | #endif | 354 | #endif |
351 | }; | 355 | }; |
352 | 356 | ||
357 | |||
358 | static int fan_est_match(struct thermal_zone_device *thz, void *data) | ||
359 | { | ||
360 | return (strcmp((char *)data, thz->type) == 0); | ||
361 | } | ||
362 | |||
363 | static int fan_est_get_temp_func(void *data, long *temp) | ||
364 | { | ||
365 | struct thermal_zone_device *thz; | ||
366 | |||
367 | thz = thermal_zone_device_find(data, fan_est_match); | ||
368 | |||
369 | if (!thz || thz->ops->get_temp(thz, temp)) | ||
370 | *temp = 25000; | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | |||
353 | static int therm_fan_est_probe(struct platform_device *pdev) | 376 | static int therm_fan_est_probe(struct platform_device *pdev) |
354 | { | 377 | { |
355 | int i, j; | 378 | int i, j; |
356 | long temp; | 379 | long temp; |
357 | struct therm_fan_estimator *est; | 380 | int err = 0; |
381 | int of_err = 0; | ||
382 | struct therm_fan_estimator *est_data; | ||
383 | struct therm_fan_est_subdevice *subdevs; | ||
358 | struct therm_fan_est_subdevice *dev; | 384 | struct therm_fan_est_subdevice *dev; |
359 | struct therm_fan_est_data *data; | 385 | struct thermal_zone_params *tzp; |
386 | struct device_node *node = NULL; | ||
387 | struct device_node *data_node = NULL; | ||
388 | int child_count = 0; | ||
389 | struct device_node *child = NULL; | ||
390 | const char *gov_name; | ||
391 | u32 value; | ||
392 | |||
393 | pr_info("THERMAL EST start of therm_fan_est_probe.\n"); | ||
394 | if (!pdev) | ||
395 | return -EINVAL; | ||
396 | |||
397 | node = pdev->dev.of_node; | ||
398 | if (!node) { | ||
399 | pr_err("THERMAL EST: dev of_node NULL\n"); | ||
400 | return -EINVAL; | ||
401 | } | ||
402 | |||
403 | data_node = of_parse_phandle(node, "shared_data", 0); | ||
404 | if (!data_node) { | ||
405 | pr_err("THERMAL EST shared data node parsing failed\n"); | ||
406 | return -EINVAL; | ||
407 | } | ||
360 | 408 | ||
361 | est = devm_kzalloc(&pdev->dev, | 409 | child_count = of_get_child_count(data_node); |
410 | of_err |= of_property_read_u32(data_node, "ndevs", &value); | ||
411 | if (of_err) { | ||
412 | pr_err("THERMAL EST: missing ndevs\n"); | ||
413 | return -ENXIO; | ||
414 | } | ||
415 | if (child_count != (int)value) { | ||
416 | pr_err("THERMAL EST: ndevs count mismatch\n"); | ||
417 | return -EINVAL; | ||
418 | } | ||
419 | |||
420 | est_data = devm_kzalloc(&pdev->dev, | ||
362 | sizeof(struct therm_fan_estimator), GFP_KERNEL); | 421 | sizeof(struct therm_fan_estimator), GFP_KERNEL); |
363 | if (IS_ERR_OR_NULL(est)) | 422 | if (IS_ERR_OR_NULL(est_data)) |
364 | return -ENOMEM; | 423 | return -ENOMEM; |
365 | 424 | ||
366 | platform_set_drvdata(pdev, est); | 425 | est_data->ndevs = child_count; |
426 | pr_info("THERMAL EST: found %d subdevs\n", est_data->ndevs); | ||
367 | 427 | ||
368 | data = pdev->dev.platform_data; | 428 | of_err |= of_property_read_string(node, "name", &est_data->name); |
429 | if (of_err) { | ||
430 | pr_err("THERMAL EST: name is missing\n"); | ||
431 | err = -ENXIO; | ||
432 | goto free_est; | ||
433 | } | ||
434 | pr_info("THERMAL EST name: %s.\n", est_data->name); | ||
369 | 435 | ||
370 | est->devs = data->devs; | 436 | of_err |= of_property_read_u32(node, "num_resources", &value); |
371 | est->ndevs = data->ndevs; | 437 | if (of_err) { |
372 | est->toffset = data->toffset; | 438 | pr_err("THERMAL EST: num_resources is missing\n"); |
373 | est->polling_period = data->polling_period; | 439 | err = -ENXIO; |
440 | goto free_est; | ||
441 | } | ||
442 | est_data->num_resources = value; | ||
443 | pr_info("THERMAL EST num_resources: %d\n", est_data->num_resources); | ||
444 | |||
445 | of_err |= of_property_read_u32(node, "trip_length", &value); | ||
446 | if (of_err) { | ||
447 | pr_err("THERMAL EST: missing trip length\n"); | ||
448 | err = -ENXIO; | ||
449 | goto free_est; | ||
450 | } | ||
374 | 451 | ||
375 | for (i = 0; i < MAX_ACTIVE_STATES; i++) { | 452 | est_data->trip_length = (int)value; |
376 | est->active_trip_temps[i] = data->active_trip_temps[i]; | 453 | subdevs = devm_kzalloc(&pdev->dev, |
377 | est->active_hysteresis[i] = data->active_hysteresis[i]; | 454 | child_count * sizeof(struct therm_fan_est_subdevice), |
455 | GFP_KERNEL); | ||
456 | if (IS_ERR_OR_NULL(subdevs)) { | ||
457 | err = -ENOMEM; | ||
458 | goto free_est; | ||
378 | } | 459 | } |
379 | 460 | ||
380 | est->active_trip_temps_hyst[0] = data->active_trip_temps[0]; | 461 | /* initialize subdevs */ |
462 | j = 0; | ||
463 | for_each_child_of_node(data_node, child) { | ||
464 | pr_info("[THERMAL EST subdev %d]\n", j); | ||
465 | of_err |= of_property_read_string(child, "dev_data", | ||
466 | &subdevs[j].dev_data); | ||
467 | if (of_err) { | ||
468 | pr_err("THERMAL EST subdev[%d] dev_data missed\n", j); | ||
469 | err = -ENXIO; | ||
470 | goto free_subdevs; | ||
471 | } | ||
472 | pr_info("THERMAL EST subdev name: %s\n", | ||
473 | (char *)subdevs[j].dev_data); | ||
381 | 474 | ||
382 | for (i = 1; i < MAX_ACTIVE_STATES; i++) | 475 | subdevs[j].get_temp = &fan_est_get_temp_func; |
383 | fan_set_trip_temp_hyst(est, i, | ||
384 | data->active_hysteresis[i], est->active_trip_temps[i]); | ||
385 | 476 | ||
386 | /* initialize history */ | 477 | of_err |= of_property_read_u32_array(child, "coeffs", |
387 | for (i = 0; i < data->ndevs; i++) { | 478 | subdevs[j].coeffs, est_data->trip_length); |
388 | dev = &est->devs[i]; | 479 | for (i = 0; i < est_data->trip_length; i++) |
480 | pr_info("THERMAL EST index %d coeffs %d\n", | ||
481 | i, subdevs[j].coeffs[i]); | ||
482 | j++; | ||
483 | } | ||
484 | est_data->devs = subdevs; | ||
389 | 485 | ||
390 | if (dev->get_temp(dev->dev_data, &temp)) | 486 | of_err |= of_property_read_u32(data_node, "toffset", &value); |
391 | return -EINVAL; | 487 | if (of_err) { |
488 | pr_err("THERMAL EST: missing toffset\n"); | ||
489 | err = -ENXIO; | ||
490 | goto free_subdevs; | ||
491 | } | ||
492 | est_data->toffset = (long)value; | ||
493 | |||
494 | of_err |= of_property_read_u32(data_node, "polling_period", &value); | ||
495 | if (of_err) { | ||
496 | pr_err("THERMAL EST: missing polling_period\n"); | ||
497 | err = -ENXIO; | ||
498 | goto free_subdevs; | ||
499 | } | ||
500 | est_data->polling_period = (long)value; | ||
501 | |||
502 | of_err |= of_property_read_u32_array(node, "active_trip_temps", | ||
503 | est_data->active_trip_temps, (size_t) est_data->trip_length); | ||
504 | if (of_err) { | ||
505 | pr_err("THERMAL EST: active trip temps failed to parse.\n"); | ||
506 | err = -ENXIO; | ||
507 | goto free_subdevs; | ||
508 | } | ||
392 | 509 | ||
510 | of_err |= of_property_read_u32_array(node, "active_hysteresis", | ||
511 | est_data->active_hysteresis, (size_t) est_data->trip_length); | ||
512 | if (of_err) { | ||
513 | pr_err("THERMAL EST: active hysteresis failed to parse.\n"); | ||
514 | err = -ENXIO; | ||
515 | goto free_subdevs; | ||
516 | } | ||
517 | |||
518 | for (i = 0; i < est_data->trip_length; i++) | ||
519 | pr_info("THERMAL EST index %d: trip_temp %d, hyst %d\n", | ||
520 | i, est_data->active_trip_temps[i], | ||
521 | est_data->active_hysteresis[i]); | ||
522 | |||
523 | est_data->active_trip_temps_hyst[0] = est_data->active_trip_temps[0]; | ||
524 | for (i = 1; i < MAX_ACTIVE_STATES; i++) | ||
525 | fan_set_trip_temp_hyst(est_data, i, | ||
526 | est_data->active_hysteresis[i], | ||
527 | est_data->active_trip_temps[i]); | ||
528 | for (i = 0; i < (MAX_ACTIVE_STATES << 1) + 1; i++) | ||
529 | pr_info("THERMAL EST index %d: trip_temps_hyst %d\n", | ||
530 | i, est_data->active_trip_temps_hyst[i]); | ||
531 | |||
532 | for (i = 0; i < est_data->ndevs; i++) { | ||
533 | dev = &est_data->devs[i]; | ||
534 | if (dev->get_temp(dev->dev_data, &temp)) { | ||
535 | err = -EINVAL; | ||
536 | goto free_subdevs; | ||
537 | } | ||
393 | for (j = 0; j < HIST_LEN; j++) | 538 | for (j = 0; j < HIST_LEN; j++) |
394 | dev->hist[j] = temp; | 539 | dev->hist[j] = temp; |
540 | pr_info("THERMAL EST init dev[%d] temp hist to %ld\n", | ||
541 | i, temp); | ||
395 | } | 542 | } |
396 | 543 | ||
397 | est->workqueue = alloc_workqueue(dev_name(&pdev->dev), | 544 | of_err |= of_property_read_string(data_node, "cdev_type", |
398 | WQ_HIGHPRI | WQ_UNBOUND, 1); | 545 | &est_data->cdev_type); |
399 | if (!est->workqueue) | 546 | if (of_err) { |
400 | return -ENOMEM; | 547 | pr_err("THERMAL EST: cdev_type is missing\n"); |
548 | err = -EINVAL; | ||
549 | goto free_subdevs; | ||
550 | } | ||
551 | pr_info("THERMAL EST cdev_type: %s.\n", est_data->cdev_type); | ||
401 | 552 | ||
402 | est->current_trip_index = 0; | 553 | tzp = devm_kzalloc(&pdev->dev, sizeof(struct thermal_zone_params), |
554 | GFP_KERNEL); | ||
555 | if (IS_ERR_OR_NULL(tzp)) { | ||
556 | err = -ENOMEM; | ||
557 | goto free_subdevs; | ||
558 | } | ||
559 | memset(tzp, 0, sizeof(struct thermal_zone_params)); | ||
560 | of_err |= of_property_read_string(data_node, "tzp_governor_name", | ||
561 | &gov_name); | ||
562 | if (of_err) { | ||
563 | pr_err("THERMAL EST: governor name is missing\n"); | ||
564 | err = -EINVAL; | ||
565 | goto free_tzp; | ||
566 | } | ||
567 | strcpy(tzp->governor_name, gov_name); | ||
568 | pr_info("THERMAL EST governor name: %s\n", tzp->governor_name); | ||
569 | est_data->tzp = tzp; | ||
570 | est_data->thz = thermal_zone_device_register( | ||
571 | (char *)dev_name(&pdev->dev), | ||
572 | 10, 0x3FF, est_data, | ||
573 | &therm_fan_est_ops, tzp, 0, 0); | ||
574 | if (IS_ERR_OR_NULL(est_data->thz)) { | ||
575 | pr_err("THERMAL EST: thz register failed\n"); | ||
576 | err = -EINVAL; | ||
577 | goto free_tzp; | ||
578 | } | ||
579 | pr_info("THERMAL EST: thz register success.\n"); | ||
403 | 580 | ||
404 | INIT_DELAYED_WORK(&est->therm_fan_est_work, therm_fan_est_work_func); | 581 | /* workqueue related */ |
582 | est_data->workqueue = alloc_workqueue(dev_name(&pdev->dev), | ||
583 | WQ_HIGHPRI | WQ_UNBOUND, 1); | ||
584 | if (!est_data->workqueue) { | ||
585 | err = -ENOMEM; | ||
586 | goto free_tzp; | ||
587 | } | ||
588 | |||
589 | est_data->current_trip_index = 0; | ||
590 | INIT_DELAYED_WORK(&est_data->therm_fan_est_work, | ||
591 | therm_fan_est_work_func); | ||
592 | queue_delayed_work(est_data->workqueue, | ||
593 | &est_data->therm_fan_est_work, | ||
594 | msecs_to_jiffies(est_data->polling_period)); | ||
405 | 595 | ||
406 | queue_delayed_work(est->workqueue, | ||
407 | &est->therm_fan_est_work, | ||
408 | msecs_to_jiffies(est->polling_period)); | ||
409 | est->cdev_type = data->cdev_type; | ||
410 | est->thz = thermal_zone_device_register((char *) dev_name(&pdev->dev), | ||
411 | 10, 0x3FF, est, | ||
412 | &therm_fan_est_ops, data->tzp, 0, 0); | ||
413 | if (IS_ERR_OR_NULL(est->thz)) | ||
414 | return -EINVAL; | ||
415 | for (i = 0; i < ARRAY_SIZE(therm_fan_est_nodes); i++) | 596 | for (i = 0; i < ARRAY_SIZE(therm_fan_est_nodes); i++) |
416 | device_create_file(&pdev->dev, | 597 | device_create_file(&pdev->dev, |
417 | &therm_fan_est_nodes[i].dev_attr); | 598 | &therm_fan_est_nodes[i].dev_attr); |
418 | 599 | ||
419 | return 0; | 600 | platform_set_drvdata(pdev, est_data); |
601 | |||
602 | pr_info("THERMAL EST: end of probe, return err: %d\n", err); | ||
603 | return err; | ||
604 | |||
605 | free_tzp: | ||
606 | devm_kfree(&pdev->dev, (void *)tzp); | ||
607 | free_subdevs: | ||
608 | devm_kfree(&pdev->dev, (void *)subdevs); | ||
609 | free_est: | ||
610 | devm_kfree(&pdev->dev, (void *)est_data); | ||
611 | return err; | ||
420 | } | 612 | } |
421 | 613 | ||
422 | static int therm_fan_est_remove(struct platform_device *pdev) | 614 | static int therm_fan_est_remove(struct platform_device *pdev) |
@@ -427,8 +619,11 @@ static int therm_fan_est_remove(struct platform_device *pdev) | |||
427 | return -EINVAL; | 619 | return -EINVAL; |
428 | 620 | ||
429 | cancel_delayed_work(&est->therm_fan_est_work); | 621 | cancel_delayed_work(&est->therm_fan_est_work); |
622 | destroy_workqueue(est->workqueue); | ||
430 | thermal_zone_device_unregister(est->thz); | 623 | thermal_zone_device_unregister(est->thz); |
431 | 624 | devm_kfree(&pdev->dev, (void *)est->tzp); | |
625 | devm_kfree(&pdev->dev, (void *)est->devs); | ||
626 | devm_kfree(&pdev->dev, (void *)est); | ||
432 | return 0; | 627 | return 0; |
433 | } | 628 | } |
434 | 629 | ||
@@ -463,10 +658,18 @@ static int therm_fan_est_resume(struct platform_device *pdev) | |||
463 | } | 658 | } |
464 | #endif | 659 | #endif |
465 | 660 | ||
661 | static const struct of_device_id of_thermal_est_match[] = { | ||
662 | { .compatible = "loki-thermal-est", }, | ||
663 | { .compatible = "foster-thermal-est", }, | ||
664 | {}, | ||
665 | }; | ||
666 | MODULE_DEVICE_TABLE(of, of_thermal_est_match); | ||
667 | |||
466 | static struct platform_driver therm_fan_est_driver = { | 668 | static struct platform_driver therm_fan_est_driver = { |
467 | .driver = { | 669 | .driver = { |
670 | .name = "therm-fan-est-driver", | ||
468 | .owner = THIS_MODULE, | 671 | .owner = THIS_MODULE, |
469 | .name = "therm-fan-est", | 672 | .of_match_table = of_thermal_est_match, |
470 | }, | 673 | }, |
471 | .probe = therm_fan_est_probe, | 674 | .probe = therm_fan_est_probe, |
472 | .remove = therm_fan_est_remove, | 675 | .remove = therm_fan_est_remove, |