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