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/acpi.h>
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/platform_device.h>
14#include <linux/pm_runtime.h>
15
16#include "pwm-lpss.h"
17
18/* BayTrail */
19static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
20	.clk_rate = 25000000,
21	.npwm = 1,
22	.base_unit_bits = 16,
23};
24
25/* Braswell */
26static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
27	.clk_rate = 19200000,
28	.npwm = 1,
29	.base_unit_bits = 16,
30	.other_devices_aml_touches_pwm_regs = true,
31};
32
33/* Broxton */
34static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = {
35	.clk_rate = 19200000,
36	.npwm = 4,
37	.base_unit_bits = 22,
38	.bypass = true,
39};
40
41static int pwm_lpss_probe_platform(struct platform_device *pdev)
42{
43	const struct pwm_lpss_boardinfo *info;
44	const struct acpi_device_id *id;
45	struct pwm_lpss_chip *lpwm;
46	struct resource *r;
47
48	id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
49	if (!id)
50		return -ENODEV;
51
52	info = (const struct pwm_lpss_boardinfo *)id->driver_data;
53	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
54
55	lpwm = pwm_lpss_probe(&pdev->dev, r, info);
56	if (IS_ERR(lpwm))
57		return PTR_ERR(lpwm);
58
59	platform_set_drvdata(pdev, lpwm);
60
61	dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE);
62	pm_runtime_set_active(&pdev->dev);
63	pm_runtime_enable(&pdev->dev);
64
65	return 0;
66}
67
68static int pwm_lpss_remove_platform(struct platform_device *pdev)
69{
70	struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
71
72	pm_runtime_disable(&pdev->dev);
73	return pwm_lpss_remove(lpwm);
74}
75
76static int pwm_lpss_prepare(struct device *dev)
77{
78	struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev);
79
80	/*
81	 * If other device's AML code touches the PWM regs on suspend/resume
82	 * force runtime-resume the PWM controller to allow this.
83	 */
84	if (lpwm->info->other_devices_aml_touches_pwm_regs)
85		return 0; /* Force runtime-resume */
86
87	return 1; /* If runtime-suspended leave as is */
88}
89
90static const struct dev_pm_ops pwm_lpss_platform_pm_ops = {
91	.prepare = pwm_lpss_prepare,
92};
93
94static const struct acpi_device_id pwm_lpss_acpi_match[] = {
95	{ "80860F09", (unsigned long)&pwm_lpss_byt_info },
96	{ "80862288", (unsigned long)&pwm_lpss_bsw_info },
97	{ "80862289", (unsigned long)&pwm_lpss_bsw_info },
98	{ "80865AC8", (unsigned long)&pwm_lpss_bxt_info },
99	{ },
100};
101MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
102
103static struct platform_driver pwm_lpss_driver_platform = {
104	.driver = {
105		.name = "pwm-lpss",
106		.acpi_match_table = pwm_lpss_acpi_match,
107		.pm = &pwm_lpss_platform_pm_ops,
108	},
109	.probe = pwm_lpss_probe_platform,
110	.remove = pwm_lpss_remove_platform,
111};
112module_platform_driver(pwm_lpss_driver_platform);
113
114MODULE_DESCRIPTION("PWM platform driver for Intel LPSS");
115MODULE_LICENSE("GPL v2");
116MODULE_ALIAS("platform:pwm-lpss");
117