12fa97febSLan Tianyu /* 22fa97febSLan Tianyu * ACPI support for CMOS RTC Address Space access 32fa97febSLan Tianyu * 42fa97febSLan Tianyu * Copyright (C) 2013, Intel Corporation 52fa97febSLan Tianyu * Authors: Lan Tianyu <tianyu.lan@intel.com> 62fa97febSLan Tianyu * 72fa97febSLan Tianyu * This program is free software; you can redistribute it and/or modify 82fa97febSLan Tianyu * it under the terms of the GNU General Public License version 2 as 92fa97febSLan Tianyu * published by the Free Software Foundation. 102fa97febSLan Tianyu */ 112fa97febSLan Tianyu 122fa97febSLan Tianyu #include <linux/acpi.h> 132fa97febSLan Tianyu #include <linux/device.h> 142fa97febSLan Tianyu #include <linux/err.h> 152fa97febSLan Tianyu #include <linux/kernel.h> 162fa97febSLan Tianyu #include <linux/module.h> 172fa97febSLan Tianyu #include <asm-generic/rtc.h> 182fa97febSLan Tianyu 192fa97febSLan Tianyu #include "internal.h" 202fa97febSLan Tianyu 212fa97febSLan Tianyu ACPI_MODULE_NAME("cmos rtc"); 222fa97febSLan Tianyu 232fa97febSLan Tianyu static const struct acpi_device_id acpi_cmos_rtc_ids[] = { 242fa97febSLan Tianyu { "PNP0B00" }, 252fa97febSLan Tianyu { "PNP0B01" }, 262fa97febSLan Tianyu { "PNP0B02" }, 272fa97febSLan Tianyu {} 282fa97febSLan Tianyu }; 292fa97febSLan Tianyu 302fa97febSLan Tianyu static acpi_status 312fa97febSLan Tianyu acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address, 322fa97febSLan Tianyu u32 bits, u64 *value64, 332fa97febSLan Tianyu void *handler_context, void *region_context) 342fa97febSLan Tianyu { 352fa97febSLan Tianyu int i; 369389f46eSLee, Chun-Yi u8 *value = (u8 *)value64; 372fa97febSLan Tianyu 382fa97febSLan Tianyu if (address > 0xff || !value64) 392fa97febSLan Tianyu return AE_BAD_PARAMETER; 402fa97febSLan Tianyu 412fa97febSLan Tianyu if (function != ACPI_WRITE && function != ACPI_READ) 422fa97febSLan Tianyu return AE_BAD_PARAMETER; 432fa97febSLan Tianyu 442fa97febSLan Tianyu spin_lock_irq(&rtc_lock); 452fa97febSLan Tianyu 462fa97febSLan Tianyu for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value) 472fa97febSLan Tianyu if (function == ACPI_READ) 482fa97febSLan Tianyu *value = CMOS_READ(address); 492fa97febSLan Tianyu else 502fa97febSLan Tianyu CMOS_WRITE(*value, address); 512fa97febSLan Tianyu 522fa97febSLan Tianyu spin_unlock_irq(&rtc_lock); 532fa97febSLan Tianyu 542fa97febSLan Tianyu return AE_OK; 552fa97febSLan Tianyu } 562fa97febSLan Tianyu 572fa97febSLan Tianyu static int acpi_install_cmos_rtc_space_handler(struct acpi_device *adev, 582fa97febSLan Tianyu const struct acpi_device_id *id) 592fa97febSLan Tianyu { 602fa97febSLan Tianyu acpi_status status; 612fa97febSLan Tianyu 622fa97febSLan Tianyu status = acpi_install_address_space_handler(adev->handle, 632fa97febSLan Tianyu ACPI_ADR_SPACE_CMOS, 642fa97febSLan Tianyu &acpi_cmos_rtc_space_handler, 652fa97febSLan Tianyu NULL, NULL); 662fa97febSLan Tianyu if (ACPI_FAILURE(status)) { 672fa97febSLan Tianyu pr_err(PREFIX "Error installing CMOS-RTC region handler\n"); 682fa97febSLan Tianyu return -ENODEV; 692fa97febSLan Tianyu } 702fa97febSLan Tianyu 71eec15edbSZhang Rui return 1; 722fa97febSLan Tianyu } 732fa97febSLan Tianyu 742fa97febSLan Tianyu static void acpi_remove_cmos_rtc_space_handler(struct acpi_device *adev) 752fa97febSLan Tianyu { 762fa97febSLan Tianyu if (ACPI_FAILURE(acpi_remove_address_space_handler(adev->handle, 772fa97febSLan Tianyu ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler))) 782fa97febSLan Tianyu pr_err(PREFIX "Error removing CMOS-RTC region handler\n"); 792fa97febSLan Tianyu } 802fa97febSLan Tianyu 812fa97febSLan Tianyu static struct acpi_scan_handler cmos_rtc_handler = { 822fa97febSLan Tianyu .ids = acpi_cmos_rtc_ids, 832fa97febSLan Tianyu .attach = acpi_install_cmos_rtc_space_handler, 842fa97febSLan Tianyu .detach = acpi_remove_cmos_rtc_space_handler, 852fa97febSLan Tianyu }; 862fa97febSLan Tianyu 872fa97febSLan Tianyu void __init acpi_cmos_rtc_init(void) 882fa97febSLan Tianyu { 892fa97febSLan Tianyu acpi_scan_add_handler(&cmos_rtc_handler); 902fa97febSLan Tianyu } 91