mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-04 09:34:12 +08:00
1d375b58c1
On some devices the contents of the ctrl register get lost over a suspend/resume and the PWM comes back up disabled after the resume. This is seen on some Bay Trail devices with the PWM in ACPI enumerated mode, so it shows up as a platform device instead of a PCI device. If we still think it is enabled and then try to change the duty-cycle after this, we end up with a "PWM_SW_UPDATE was not cleared" error and the PWM is stuck in that state from then on. This commit adds suspend and resume pm callbacks to the pwm-lpss-platform code, which save/restore the ctrl register over a suspend/resume, fixing this. Note that: 1) There is no need to do this over a runtime suspend, since we only runtime suspend when disabled and then we properly set the enable bit and reprogram the timings when we re-enable the PWM. 2) This may be happening on more systems then we realize, but has been covered up sofar by a bug in the acpi-lpss.c code which was save/restoring the regular device registers instead of the lpss private registers due to lpss_device_desc.prv_offset not being set. This is fixed by a later patch in this series. Cc: stable@vger.kernel.org Signed-off-by: Hans de Goede <hdegoede@redhat.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
103 lines
2.5 KiB
C
103 lines
2.5 KiB
C
/*
|
|
* Intel Low Power Subsystem PWM controller driver
|
|
*
|
|
* Copyright (C) 2014, Intel Corporation
|
|
*
|
|
* Derived from the original pwm-lpss.c
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/acpi.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
|
|
#include "pwm-lpss.h"
|
|
|
|
/* BayTrail */
|
|
static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
|
|
.clk_rate = 25000000,
|
|
.npwm = 1,
|
|
.base_unit_bits = 16,
|
|
};
|
|
|
|
/* Braswell */
|
|
static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
|
|
.clk_rate = 19200000,
|
|
.npwm = 1,
|
|
.base_unit_bits = 16,
|
|
};
|
|
|
|
/* Broxton */
|
|
static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = {
|
|
.clk_rate = 19200000,
|
|
.npwm = 4,
|
|
.base_unit_bits = 22,
|
|
.bypass = true,
|
|
};
|
|
|
|
static int pwm_lpss_probe_platform(struct platform_device *pdev)
|
|
{
|
|
const struct pwm_lpss_boardinfo *info;
|
|
const struct acpi_device_id *id;
|
|
struct pwm_lpss_chip *lpwm;
|
|
struct resource *r;
|
|
|
|
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
|
|
if (!id)
|
|
return -ENODEV;
|
|
|
|
info = (const struct pwm_lpss_boardinfo *)id->driver_data;
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
lpwm = pwm_lpss_probe(&pdev->dev, r, info);
|
|
if (IS_ERR(lpwm))
|
|
return PTR_ERR(lpwm);
|
|
|
|
platform_set_drvdata(pdev, lpwm);
|
|
|
|
pm_runtime_set_active(&pdev->dev);
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pwm_lpss_remove_platform(struct platform_device *pdev)
|
|
{
|
|
struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
return pwm_lpss_remove(lpwm);
|
|
}
|
|
|
|
static SIMPLE_DEV_PM_OPS(pwm_lpss_platform_pm_ops,
|
|
pwm_lpss_suspend,
|
|
pwm_lpss_resume);
|
|
|
|
static const struct acpi_device_id pwm_lpss_acpi_match[] = {
|
|
{ "80860F09", (unsigned long)&pwm_lpss_byt_info },
|
|
{ "80862288", (unsigned long)&pwm_lpss_bsw_info },
|
|
{ "80865AC8", (unsigned long)&pwm_lpss_bxt_info },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
|
|
|
|
static struct platform_driver pwm_lpss_driver_platform = {
|
|
.driver = {
|
|
.name = "pwm-lpss",
|
|
.acpi_match_table = pwm_lpss_acpi_match,
|
|
.pm = &pwm_lpss_platform_pm_ops,
|
|
},
|
|
.probe = pwm_lpss_probe_platform,
|
|
.remove = pwm_lpss_remove_platform,
|
|
};
|
|
module_platform_driver(pwm_lpss_driver_platform);
|
|
|
|
MODULE_DESCRIPTION("PWM platform driver for Intel LPSS");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:pwm-lpss");
|