1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22fa97febSLan Tianyu /* 32fa97febSLan Tianyu * ACPI support for CMOS RTC Address Space access 42fa97febSLan Tianyu * 52fa97febSLan Tianyu * Copyright (C) 2013, Intel Corporation 62fa97febSLan Tianyu * Authors: Lan Tianyu <tianyu.lan@intel.com> 72fa97febSLan Tianyu */ 82fa97febSLan Tianyu 9*606e56c6SHanjun Guo #define pr_fmt(fmt) "ACPI: " fmt 10*606e56c6SHanjun Guo 112fa97febSLan Tianyu #include <linux/acpi.h> 122fa97febSLan Tianyu #include <linux/device.h> 132fa97febSLan Tianyu #include <linux/err.h> 142fa97febSLan Tianyu #include <linux/kernel.h> 152fa97febSLan Tianyu #include <linux/module.h> 16463a8630SArnd Bergmann #include <linux/mc146818rtc.h> 172fa97febSLan Tianyu 182fa97febSLan Tianyu #include "internal.h" 192fa97febSLan Tianyu 202fa97febSLan Tianyu static const struct acpi_device_id acpi_cmos_rtc_ids[] = { 212fa97febSLan Tianyu { "PNP0B00" }, 222fa97febSLan Tianyu { "PNP0B01" }, 232fa97febSLan Tianyu { "PNP0B02" }, 242fa97febSLan Tianyu {} 252fa97febSLan Tianyu }; 262fa97febSLan Tianyu 272fa97febSLan Tianyu static acpi_status 282fa97febSLan Tianyu acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address, 292fa97febSLan Tianyu u32 bits, u64 *value64, 302fa97febSLan Tianyu void *handler_context, void *region_context) 312fa97febSLan Tianyu { 322fa97febSLan Tianyu int i; 339389f46eSLee, Chun-Yi u8 *value = (u8 *)value64; 342fa97febSLan Tianyu 352fa97febSLan Tianyu if (address > 0xff || !value64) 362fa97febSLan Tianyu return AE_BAD_PARAMETER; 372fa97febSLan Tianyu 382fa97febSLan Tianyu if (function != ACPI_WRITE && function != ACPI_READ) 392fa97febSLan Tianyu return AE_BAD_PARAMETER; 402fa97febSLan Tianyu 412fa97febSLan Tianyu spin_lock_irq(&rtc_lock); 422fa97febSLan Tianyu 432fa97febSLan Tianyu for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value) 442fa97febSLan Tianyu if (function == ACPI_READ) 452fa97febSLan Tianyu *value = CMOS_READ(address); 462fa97febSLan Tianyu else 472fa97febSLan Tianyu CMOS_WRITE(*value, address); 482fa97febSLan Tianyu 492fa97febSLan Tianyu spin_unlock_irq(&rtc_lock); 502fa97febSLan Tianyu 512fa97febSLan Tianyu return AE_OK; 522fa97febSLan Tianyu } 532fa97febSLan Tianyu 542fa97febSLan Tianyu static int acpi_install_cmos_rtc_space_handler(struct acpi_device *adev, 552fa97febSLan Tianyu const struct acpi_device_id *id) 562fa97febSLan Tianyu { 572fa97febSLan Tianyu acpi_status status; 582fa97febSLan Tianyu 592fa97febSLan Tianyu status = acpi_install_address_space_handler(adev->handle, 602fa97febSLan Tianyu ACPI_ADR_SPACE_CMOS, 612fa97febSLan Tianyu &acpi_cmos_rtc_space_handler, 622fa97febSLan Tianyu NULL, NULL); 632fa97febSLan Tianyu if (ACPI_FAILURE(status)) { 64*606e56c6SHanjun Guo pr_err("Error installing CMOS-RTC region handler\n"); 652fa97febSLan Tianyu return -ENODEV; 662fa97febSLan Tianyu } 672fa97febSLan Tianyu 68eec15edbSZhang Rui return 1; 692fa97febSLan Tianyu } 702fa97febSLan Tianyu 712fa97febSLan Tianyu static void acpi_remove_cmos_rtc_space_handler(struct acpi_device *adev) 722fa97febSLan Tianyu { 732fa97febSLan Tianyu if (ACPI_FAILURE(acpi_remove_address_space_handler(adev->handle, 742fa97febSLan Tianyu ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler))) 75*606e56c6SHanjun Guo pr_err("Error removing CMOS-RTC region handler\n"); 762fa97febSLan Tianyu } 772fa97febSLan Tianyu 782fa97febSLan Tianyu static struct acpi_scan_handler cmos_rtc_handler = { 792fa97febSLan Tianyu .ids = acpi_cmos_rtc_ids, 802fa97febSLan Tianyu .attach = acpi_install_cmos_rtc_space_handler, 812fa97febSLan Tianyu .detach = acpi_remove_cmos_rtc_space_handler, 822fa97febSLan Tianyu }; 832fa97febSLan Tianyu 842fa97febSLan Tianyu void __init acpi_cmos_rtc_init(void) 852fa97febSLan Tianyu { 862fa97febSLan Tianyu acpi_scan_add_handler(&cmos_rtc_handler); 872fa97febSLan Tianyu } 88