Commit d0706021 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-cmos-rtc'

Merge updates related to the CMOS RTC driver and x86/ACPI CMOS RTC
support for 7.1-rc1:

 - Add ACPI support to the platform device interface in the CMOS RTC
   driver, make the ACPI core device enumeration code create a platform
   device for the CMOS RTC, and drop CMOS RTC PNP device support (Rafael
   Wysocki)

 - Consolidate the x86-specific CMOS RTC handling with the ACPI TAD
   driver and clean up the CMOS RTC ACPI address space handler (Rafael
   Wysocki)

 - Enable ACPI alarm in the CMOS RTC driver if advertised in ACPI FADT
   and allow that driver to work without a dedicated IRQ if the ACPI
   alarm is used (Rafael Wysocki)

* acpi-cmos-rtc:
  rtc: cmos: Do not require IRQ if ACPI alarm is used
  rtc: cmos: Enable ACPI alarm if advertised in ACPI FADT
  ACPI: TAD/x86: cmos_rtc: Consolidate address space handler setup
  rtc: cmos: Drop PNP device support
  x86: rtc: Drop PNP device check
  ACPI: PNP: Drop CMOS RTC PNP device support
  ACPI: x86/rtc-cmos: Use platform device for driver binding
  ACPI: x86: cmos_rtc: Create a CMOS RTC platform device
  ACPI: x86: cmos_rtc: Improve coordination with ACPI TAD driver
  ACPI: x86: cmos_rtc: Clean up address space handler driver
parents bfb0315a b48ee1f7
Loading
Loading
Loading
Loading
+5 −16
Original line number Diff line number Diff line
@@ -2,10 +2,10 @@
/*
 * RTC related functions
 */
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/mc146818rtc.h>
#include <linux/export.h>
#include <linux/pnp.h>

#include <asm/vsyscall.h>
#include <asm/x86_init.h>
@@ -133,25 +133,14 @@ static struct platform_device rtc_device = {

static __init int add_rtc_cmos(void)
{
#ifdef CONFIG_PNP
	static const char * const ids[] __initconst =
	    { "PNP0b00", "PNP0b01", "PNP0b02", };
	struct pnp_dev *dev;
	int i;

	pnp_for_each_dev(dev) {
		for (i = 0; i < ARRAY_SIZE(ids); i++) {
			if (compare_pnp_id(dev->id, ids[i]) != 0)
	if (cmos_rtc_platform_device_present)
		return 0;
		}
	}
#endif

	if (!x86_platform.legacy.rtc)
		return -ENODEV;

	platform_device_register(&rtc_device);
	dev_info(&rtc_device.dev,
		 "registered platform RTC device (no PNP device found)\n");
	dev_info(&rtc_device.dev, "registered fallback platform RTC device\n");

	return 0;
}
+1 −21
Original line number Diff line number Diff line
@@ -125,10 +125,6 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = {
	{"PNP0401"},		/* ECP Printer Port */
	/* apple-gmux */
	{"APP000B"},
	/* rtc_cmos */
	{"PNP0b00"},
	{"PNP0b01"},
	{"PNP0b02"},
	/* c6xdigio */
	{"PNP0400"},		/* Standard LPT Printer Port */
	{"PNP0401"},		/* ECP Printer Port */
@@ -355,25 +351,9 @@ static struct acpi_scan_handler acpi_pnp_handler = {
	.attach = acpi_pnp_attach,
};

/*
 * For CMOS RTC devices, the PNP ACPI scan handler does not work, because
 * there is a CMOS RTC ACPI scan handler installed already, so we need to
 * check those devices and enumerate them to the PNP bus directly.
 */
static int is_cmos_rtc_device(struct acpi_device *adev)
{
	static const struct acpi_device_id ids[] = {
		{ "PNP0B00" },
		{ "PNP0B01" },
		{ "PNP0B02" },
		{""},
	};
	return !acpi_match_device_ids(adev, ids);
}

bool acpi_is_pnp_device(struct acpi_device *adev)
{
	return adev->handler == &acpi_pnp_handler || is_cmos_rtc_device(adev);
	return adev->handler == &acpi_pnp_handler;
}
EXPORT_SYMBOL_GPL(acpi_is_pnp_device);

+5 −22
Original line number Diff line number Diff line
@@ -563,7 +563,6 @@ static int acpi_tad_disable_timer(struct device *dev, u32 timer_id)
static void acpi_tad_remove(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	acpi_handle handle = ACPI_HANDLE(dev);
	struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);

	device_init_wakeup(dev, false);
@@ -587,7 +586,6 @@ static void acpi_tad_remove(struct platform_device *pdev)

	pm_runtime_suspend(dev);
	pm_runtime_disable(dev);
	acpi_remove_cmos_rtc_space_handler(handle);
}

static int acpi_tad_probe(struct platform_device *pdev)
@@ -599,11 +597,6 @@ static int acpi_tad_probe(struct platform_device *pdev)
	unsigned long long caps;
	int ret;

	ret = acpi_install_cmos_rtc_space_handler(handle);
	if (ret < 0) {
		dev_info(dev, "Unable to install space handler\n");
		return -ENODEV;
	}
	/*
	 * Initialization failure messages are mostly about firmware issues, so
	 * print them at the "info" level.
@@ -611,27 +604,22 @@ static int acpi_tad_probe(struct platform_device *pdev)
	status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps);
	if (ACPI_FAILURE(status)) {
		dev_info(dev, "Unable to get capabilities\n");
		ret = -ENODEV;
		goto remove_handler;
		return -ENODEV;
	}

	if (!(caps & ACPI_TAD_AC_WAKE)) {
		dev_info(dev, "Unsupported capabilities\n");
		ret = -ENODEV;
		goto remove_handler;
		return -ENODEV;
	}

	if (!acpi_has_method(handle, "_PRW")) {
		dev_info(dev, "Missing _PRW\n");
		ret = -ENODEV;
		goto remove_handler;
		return -ENODEV;
	}

	dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
	if (!dd) {
		ret = -ENOMEM;
		goto remove_handler;
	}
	if (!dd)
		return -ENOMEM;

	dd->capabilities = caps;
	dev_set_drvdata(dev, dd);
@@ -673,11 +661,6 @@ static int acpi_tad_probe(struct platform_device *pdev)

fail:
	acpi_tad_remove(pdev);
	/* Don't fallthrough because cmos rtc space handler is removed in acpi_tad_remove() */
	return ret;

remove_handler:
	acpi_remove_cmos_rtc_space_handler(handle);
	return ret;
}

+47 −39
Original line number Diff line number Diff line
@@ -18,78 +18,86 @@
#include "../internal.h"

static const struct acpi_device_id acpi_cmos_rtc_ids[] = {
	{ "PNP0B00" },
	{ "PNP0B01" },
	{ "PNP0B02" },
	{}
	{ "ACPI000E", 1 }, /* ACPI Time and Alarm Device (TAD) */
	ACPI_CMOS_RTC_IDS
};

static acpi_status
acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address,
bool cmos_rtc_platform_device_present;

static acpi_status acpi_cmos_rtc_space_handler(u32 function,
					       acpi_physical_address address,
					       u32 bits, u64 *value64,
		      void *handler_context, void *region_context)
					       void *handler_context,
					       void *region_context)
{
	int i;
	unsigned int i, bytes = DIV_ROUND_UP(bits, 8);
	u8 *value = (u8 *)value64;

	if (address > 0xff || !value64)
		return AE_BAD_PARAMETER;

	if (function != ACPI_WRITE && function != ACPI_READ)
		return AE_BAD_PARAMETER;

	spin_lock_irq(&rtc_lock);
	guard(spinlock_irq)(&rtc_lock);

	for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value)
		if (function == ACPI_READ)
			*value = CMOS_READ(address);
		else
	if (function == ACPI_WRITE) {
		for (i = 0; i < bytes; i++, address++, value++)
			CMOS_WRITE(*value, address);

	spin_unlock_irq(&rtc_lock);
		return AE_OK;
	}

	if (function == ACPI_READ) {
		for (i = 0; i < bytes; i++, address++, value++)
			*value = CMOS_READ(address);

		return AE_OK;
	}

int acpi_install_cmos_rtc_space_handler(acpi_handle handle)
	return AE_BAD_PARAMETER;
}

static int acpi_install_cmos_rtc_space_handler(acpi_handle handle)
{
	static bool cmos_rtc_space_handler_present __read_mostly;
	acpi_status status;

	if (cmos_rtc_space_handler_present)
		return 0;

	status = acpi_install_address_space_handler(handle,
						    ACPI_ADR_SPACE_CMOS,
			&acpi_cmos_rtc_space_handler,
						    acpi_cmos_rtc_space_handler,
						    NULL, NULL);
	if (ACPI_FAILURE(status)) {
		pr_err("Error installing CMOS-RTC region handler\n");
		pr_err("Failed to install CMOS-RTC address space handler\n");
		return -ENODEV;
	}

	cmos_rtc_space_handler_present = true;

	return 1;
}
EXPORT_SYMBOL_GPL(acpi_install_cmos_rtc_space_handler);

void acpi_remove_cmos_rtc_space_handler(acpi_handle handle)
static int acpi_cmos_rtc_attach(struct acpi_device *adev,
				const struct acpi_device_id *id)
{
	if (ACPI_FAILURE(acpi_remove_address_space_handler(handle,
			ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler)))
		pr_err("Error removing CMOS-RTC region handler\n");
}
EXPORT_SYMBOL_GPL(acpi_remove_cmos_rtc_space_handler);
	int ret;

static int acpi_cmos_rtc_attach_handler(struct acpi_device *adev, const struct acpi_device_id *id)
{
	return acpi_install_cmos_rtc_space_handler(adev->handle);
}
	ret = acpi_install_cmos_rtc_space_handler(adev->handle);
	if (ret < 0)
		return ret;

static void acpi_cmos_rtc_detach_handler(struct acpi_device *adev)
{
	acpi_remove_cmos_rtc_space_handler(adev->handle);
	if (IS_ERR_OR_NULL(acpi_create_platform_device(adev, NULL))) {
		pr_err("Failed to create a platform device for %s\n", (char *)id->id);
		return 0;
	} else if (!id->driver_data) {
		cmos_rtc_platform_device_present = true;
	}
	return 1;
}

static struct acpi_scan_handler cmos_rtc_handler = {
	.ids = acpi_cmos_rtc_ids,
	.attach = acpi_cmos_rtc_attach_handler,
	.detach = acpi_cmos_rtc_detach_handler,
	.attach = acpi_cmos_rtc_attach,
};

void __init acpi_cmos_rtc_init(void)
+31 −112
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -215,6 +216,11 @@ static inline void cmos_write_bank2(unsigned char val, unsigned char addr)

/*----------------------------------------------------------------*/

static bool cmos_no_alarm(struct cmos_rtc *cmos)
{
	return !is_valid_irq(cmos->irq) && !cmos_use_acpi_alarm();
}

static int cmos_read_time(struct device *dev, struct rtc_time *t)
{
	int ret;
@@ -286,7 +292,7 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
	};

	/* This not only a rtc_op, but also called directly */
	if (!is_valid_irq(cmos->irq))
	if (cmos_no_alarm(cmos))
		return -ETIMEDOUT;

	/* Basic alarms only support hour, minute, and seconds fields.
@@ -519,7 +525,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
	int ret;

	/* This not only a rtc_op, but also called directly */
	if (!is_valid_irq(cmos->irq))
	if (cmos_no_alarm(cmos))
		return -EIO;

	ret = cmos_validate_alarm(dev, t);
@@ -816,6 +822,9 @@ static void rtc_wake_off(struct device *dev)
#ifdef CONFIG_X86
static void use_acpi_alarm_quirks(void)
{
	if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC)
		return;

	switch (boot_cpu_data.x86_vendor) {
	case X86_VENDOR_INTEL:
		if (dmi_get_bios_year() < 2015)
@@ -829,8 +838,6 @@ static void use_acpi_alarm_quirks(void)
	default:
		return;
	}
	if (!is_hpet_enabled())
		return;

	use_acpi_alarm = true;
}
@@ -1094,7 +1101,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
			dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
			goto cleanup1;
		}
	} else {
	} else if (!cmos_use_acpi_alarm()) {
		clear_bit(RTC_FEATURE_ALARM, cmos_rtc.rtc->features);
	}

@@ -1119,7 +1126,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
		acpi_rtc_event_setup(dev);

	dev_info(dev, "%s%s, %d bytes nvram%s\n",
		 !is_valid_irq(rtc_irq) ? "no alarms" :
		 cmos_no_alarm(&cmos_rtc) ? "no alarms" :
		 cmos_rtc.mon_alrm ? "alarms up to one year" :
		 cmos_rtc.day_alrm ? "alarms up to one month" :
		 "alarms up to one day",
@@ -1145,7 +1152,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
static void cmos_do_shutdown(int rtc_irq)
{
	spin_lock_irq(&rtc_lock);
	if (is_valid_irq(rtc_irq))
	if (!cmos_no_alarm(&cmos_rtc))
		cmos_irq_disable(&cmos_rtc, RTC_IRQMASK);
	spin_unlock_irq(&rtc_lock);
}
@@ -1369,85 +1376,6 @@ static int __maybe_unused cmos_resume(struct device *dev)

static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);

/*----------------------------------------------------------------*/

/* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus.
 * ACPI systems always list these as PNPACPI devices, and pre-ACPI PCs
 * probably list them in similar PNPBIOS tables; so PNP is more common.
 *
 * We don't use legacy "poke at the hardware" probing.  Ancient PCs that
 * predate even PNPBIOS should set up platform_bus devices.
 */

#ifdef	CONFIG_PNP

#include <linux/pnp.h>

static int cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
{
	int irq;

	if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0)) {
		irq = 0;
#ifdef CONFIG_X86
		/* Some machines contain a PNP entry for the RTC, but
		 * don't define the IRQ. It should always be safe to
		 * hardcode it on systems with a legacy PIC.
		 */
		if (nr_legacy_irqs())
			irq = RTC_IRQ;
#endif
	} else {
		irq = pnp_irq(pnp, 0);
	}

	return cmos_do_probe(&pnp->dev, pnp_get_resource(pnp, IORESOURCE_IO, 0), irq);
}

static void cmos_pnp_remove(struct pnp_dev *pnp)
{
	cmos_do_remove(&pnp->dev);
}

static void cmos_pnp_shutdown(struct pnp_dev *pnp)
{
	struct device *dev = &pnp->dev;
	struct cmos_rtc	*cmos = dev_get_drvdata(dev);

	if (system_state == SYSTEM_POWER_OFF) {
		int retval = cmos_poweroff(dev);

		if (cmos_aie_poweroff(dev) < 0 && !retval)
			return;
	}

	cmos_do_shutdown(cmos->irq);
}

static const struct pnp_device_id rtc_ids[] = {
	{ .id = "PNP0b00", },
	{ .id = "PNP0b01", },
	{ .id = "PNP0b02", },
	{ },
};
MODULE_DEVICE_TABLE(pnp, rtc_ids);

static struct pnp_driver cmos_pnp_driver = {
	.name		= driver_name,
	.id_table	= rtc_ids,
	.probe		= cmos_pnp_probe,
	.remove		= cmos_pnp_remove,
	.shutdown	= cmos_pnp_shutdown,

	/* flag ensures resume() gets called, and stops syslog spam */
	.flags		= PNP_DRIVER_RES_DO_NOT_CHANGE,
	.driver		= {
			.pm = &cmos_pm_ops,
	},
};

#endif	/* CONFIG_PNP */

#ifdef CONFIG_OF
static const struct of_device_id of_cmos_match[] = {
	{
@@ -1476,6 +1404,14 @@ static __init void cmos_of_init(struct platform_device *pdev)
#else
static inline void cmos_of_init(struct platform_device *pdev) {}
#endif

#ifdef CONFIG_ACPI
static const struct acpi_device_id acpi_cmos_rtc_ids[] = {
	ACPI_CMOS_RTC_IDS
};
MODULE_DEVICE_TABLE(acpi, acpi_cmos_rtc_ids);
#endif

/*----------------------------------------------------------------*/

/* Platform setup should have set up an RTC device, when PNP is
@@ -1530,48 +1466,31 @@ static struct platform_driver cmos_platform_driver = {
		.name		= driver_name,
		.pm		= &cmos_pm_ops,
		.of_match_table = of_match_ptr(of_cmos_match),
		.acpi_match_table = ACPI_PTR(acpi_cmos_rtc_ids),
	}
};

#ifdef CONFIG_PNP
static bool pnp_driver_registered;
#endif
static bool platform_driver_registered;

static int __init cmos_init(void)
{
	int retval = 0;
	int retval;

#ifdef	CONFIG_PNP
	retval = pnp_register_driver(&cmos_pnp_driver);
	if (retval == 0)
		pnp_driver_registered = true;
#endif
	if (cmos_rtc.dev)
		return 0;

	retval = platform_driver_probe(&cmos_platform_driver, cmos_platform_probe);
	if (retval)
		return retval;

	if (!cmos_rtc.dev) {
		retval = platform_driver_probe(&cmos_platform_driver,
					       cmos_platform_probe);
		if (retval == 0)
	platform_driver_registered = true;
	}

	if (retval == 0)
	return 0;

#ifdef	CONFIG_PNP
	if (pnp_driver_registered)
		pnp_unregister_driver(&cmos_pnp_driver);
#endif
	return retval;
}
module_init(cmos_init);

static void __exit cmos_exit(void)
{
#ifdef	CONFIG_PNP
	if (pnp_driver_registered)
		pnp_unregister_driver(&cmos_pnp_driver);
#endif
	if (platform_driver_registered)
		platform_driver_unregister(&cmos_platform_driver);
}
Loading