| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame^] | 1 | // SPDX-License-Identifier: GPL-2.0 | 
|  | 2 | /* | 
|  | 3 | * Copyright (c) 2019 MediaTek Inc. | 
|  | 4 | */ | 
|  | 5 |  | 
|  | 6 |  | 
|  | 7 | #include <linux/kernel.h> | 
|  | 8 | #include <linux/of.h> | 
|  | 9 | #include <linux/platform_device.h> | 
|  | 10 | #include <linux/device.h> | 
|  | 11 | #include <linux/cpuidle.h> | 
|  | 12 | #include <linux/soc/mediatek/mtk-lpm.h> | 
|  | 13 |  | 
|  | 14 | #define MTK_PWR_CONSERVATION_PREPARE	(0) | 
|  | 15 | #define MTK_PWR_CONSERVATION_RESUME	(1) | 
|  | 16 |  | 
|  | 17 |  | 
|  | 18 | typedef int (*mtk_pwr_conservation_fn)(int type, | 
|  | 19 | struct cpuidle_driver *drv, | 
|  | 20 | int index); | 
|  | 21 |  | 
|  | 22 | static struct mtk_cpuidle_op *mtk_lpm_ops __read_mostly; | 
|  | 23 |  | 
|  | 24 | int mtk_lpm_drv_cpuidle_ops_set(struct mtk_cpuidle_op *op) | 
|  | 25 | { | 
|  | 26 | int ret = 0; | 
|  | 27 |  | 
|  | 28 | cpuidle_pause_and_lock(); | 
|  | 29 |  | 
|  | 30 | if ((!op && mtk_lpm_ops) || (op && !mtk_lpm_ops)) | 
|  | 31 | rcu_assign_pointer(mtk_lpm_ops, (typeof(mtk_lpm_ops))(op)); | 
|  | 32 | else | 
|  | 33 | ret = -EACCES; | 
|  | 34 |  | 
|  | 35 | cpuidle_resume_and_unlock(); | 
|  | 36 |  | 
|  | 37 | return ret; | 
|  | 38 | } | 
|  | 39 | EXPORT_SYMBOL(mtk_lpm_drv_cpuidle_ops_set); | 
|  | 40 |  | 
|  | 41 | int mtk_lpm_pwr_conservation(int type, | 
|  | 42 | struct cpuidle_driver *drv, | 
|  | 43 | int index) | 
|  | 44 | { | 
|  | 45 | int ret = -EBADR; | 
|  | 46 |  | 
|  | 47 | if (!mtk_lpm_ops) | 
|  | 48 | return ret; | 
|  | 49 |  | 
|  | 50 | switch (type) { | 
|  | 51 | case MTK_PWR_CONSERVATION_PREPARE: | 
|  | 52 | if (mtk_lpm_ops->cpuidle_prepare) | 
|  | 53 | ret = mtk_lpm_ops->cpuidle_prepare(drv, index); | 
|  | 54 | break; | 
|  | 55 | case MTK_PWR_CONSERVATION_RESUME: | 
|  | 56 | if (mtk_lpm_ops->cpuidle_resume) | 
|  | 57 | mtk_lpm_ops->cpuidle_resume(drv, index); | 
|  | 58 | ret = 0; | 
|  | 59 | break; | 
|  | 60 | default: | 
|  | 61 | break; | 
|  | 62 | } | 
|  | 63 |  | 
|  | 64 | return ret; | 
|  | 65 | } | 
|  | 66 |  | 
|  | 67 | static int mtk_lp_pm_driver_probe(struct platform_device *pdev) | 
|  | 68 | { | 
|  | 69 | struct platform_device	*mtk_cpuidle_pm_dev; | 
|  | 70 | int ret = -ENOMEM; | 
|  | 71 | mtk_pwr_conservation_fn mtk_lpm_pwr = mtk_lpm_pwr_conservation; | 
|  | 72 |  | 
|  | 73 | mtk_cpuidle_pm_dev = | 
|  | 74 | platform_device_alloc(MTK_CPUIDLE_PM_NAME, -1); | 
|  | 75 |  | 
|  | 76 | if (!mtk_cpuidle_pm_dev) | 
|  | 77 | goto out_probe_alloc_fail; | 
|  | 78 |  | 
|  | 79 | mtk_cpuidle_pm_dev->dev.parent = &pdev->dev; | 
|  | 80 | ret = platform_device_add(mtk_cpuidle_pm_dev); | 
|  | 81 |  | 
|  | 82 | if (ret) | 
|  | 83 | goto put_device; | 
|  | 84 |  | 
|  | 85 | platform_device_add_data(mtk_cpuidle_pm_dev, | 
|  | 86 | &mtk_lpm_pwr, | 
|  | 87 | sizeof(mtk_lpm_pwr)); | 
|  | 88 |  | 
|  | 89 | device_init_wakeup(&mtk_cpuidle_pm_dev->dev, true); | 
|  | 90 |  | 
|  | 91 | return 0; | 
|  | 92 |  | 
|  | 93 | put_device: | 
|  | 94 | platform_device_put(mtk_cpuidle_pm_dev); | 
|  | 95 | out_probe_alloc_fail: | 
|  | 96 |  | 
|  | 97 | return ret; | 
|  | 98 | } | 
|  | 99 |  | 
|  | 100 | static const struct of_device_id of_mtk_lp_pm_match[] = { | 
|  | 101 | { .compatible = "mediatek,mtk-lpm" }, | 
|  | 102 | {} | 
|  | 103 | }; | 
|  | 104 |  | 
|  | 105 | static struct platform_driver mtk_lp_pm_driver = { | 
|  | 106 | .probe = mtk_lp_pm_driver_probe, | 
|  | 107 | .driver = { | 
|  | 108 | .name = "mtk_lpm_driver", | 
|  | 109 | .owner = THIS_MODULE, | 
|  | 110 | .of_match_table = of_match_ptr(of_mtk_lp_pm_match), | 
|  | 111 | }, | 
|  | 112 | }; | 
|  | 113 | builtin_platform_driver(mtk_lp_pm_driver); |