xref: /linux/drivers/pwm/pwm-lpss-platform.c (revision 2da68a77)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel Low Power Subsystem PWM controller driver
4  *
5  * Copyright (C) 2014, Intel Corporation
6  *
7  * Derived from the original pwm-lpss.c
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/mod_devicetable.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm_runtime.h>
15 #include <linux/property.h>
16 
17 #include "pwm-lpss.h"
18 
19 
20 static int pwm_lpss_probe_platform(struct platform_device *pdev)
21 {
22 	const struct pwm_lpss_boardinfo *info;
23 	struct pwm_lpss_chip *lpwm;
24 	void __iomem *base;
25 
26 	info = device_get_match_data(&pdev->dev);
27 	if (!info)
28 		return -ENODEV;
29 
30 	base = devm_platform_ioremap_resource(pdev, 0);
31 	if (IS_ERR(base))
32 		return PTR_ERR(base);
33 
34 	lpwm = pwm_lpss_probe(&pdev->dev, base, info);
35 	if (IS_ERR(lpwm))
36 		return PTR_ERR(lpwm);
37 
38 	platform_set_drvdata(pdev, lpwm);
39 
40 	/*
41 	 * On Cherry Trail devices the GFX0._PS0 AML checks if the controller
42 	 * is on and if it is not on it turns it on and restores what it
43 	 * believes is the correct state to the PWM controller.
44 	 * Because of this we must disallow direct-complete, which keeps the
45 	 * controller (runtime)suspended on resume, to avoid 2 issues:
46 	 * 1. The controller getting turned on without the linux-pm code
47 	 *    knowing about this. On devices where the controller is unused
48 	 *    this causes it to stay on during the next suspend causing high
49 	 *    battery drain (because S0i3 is not reached)
50 	 * 2. The state restoring code unexpectedly messing with the controller
51 	 *
52 	 * Leaving the controller runtime-suspended (skipping runtime-resume +
53 	 * normal-suspend) during suspend is fine.
54 	 */
55 	if (info->other_devices_aml_touches_pwm_regs)
56 		dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE|
57 						    DPM_FLAG_SMART_SUSPEND);
58 
59 	pm_runtime_set_active(&pdev->dev);
60 	pm_runtime_enable(&pdev->dev);
61 
62 	return 0;
63 }
64 
65 static int pwm_lpss_remove_platform(struct platform_device *pdev)
66 {
67 	pm_runtime_disable(&pdev->dev);
68 	return 0;
69 }
70 
71 static const struct acpi_device_id pwm_lpss_acpi_match[] = {
72 	{ "80860F09", (unsigned long)&pwm_lpss_byt_info },
73 	{ "80862288", (unsigned long)&pwm_lpss_bsw_info },
74 	{ "80862289", (unsigned long)&pwm_lpss_bsw_info },
75 	{ "80865AC8", (unsigned long)&pwm_lpss_bxt_info },
76 	{ },
77 };
78 MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
79 
80 static struct platform_driver pwm_lpss_driver_platform = {
81 	.driver = {
82 		.name = "pwm-lpss",
83 		.acpi_match_table = pwm_lpss_acpi_match,
84 	},
85 	.probe = pwm_lpss_probe_platform,
86 	.remove = pwm_lpss_remove_platform,
87 };
88 module_platform_driver(pwm_lpss_driver_platform);
89 
90 MODULE_DESCRIPTION("PWM platform driver for Intel LPSS");
91 MODULE_LICENSE("GPL v2");
92 MODULE_IMPORT_NS(PWM_LPSS);
93 MODULE_ALIAS("platform:pwm-lpss");
94