1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ACPI support for CMOS RTC Address Space access 4 * 5 * Copyright (C) 2013, Intel Corporation 6 * Authors: Lan Tianyu <tianyu.lan@intel.com> 7 */ 8 9 #define pr_fmt(fmt) "ACPI: " fmt 10 11 #include <linux/acpi.h> 12 #include <linux/device.h> 13 #include <linux/err.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/mc146818rtc.h> 17 18 #include "../internal.h" 19 20 static const struct acpi_device_id acpi_cmos_rtc_ids[] = { 21 { "ACPI000E", 1 }, /* ACPI Time and Alarm Device (TAD) */ 22 ACPI_CMOS_RTC_IDS 23 }; 24 25 bool cmos_rtc_platform_device_present; 26 27 static acpi_status acpi_cmos_rtc_space_handler(u32 function, 28 acpi_physical_address address, 29 u32 bits, u64 *value64, 30 void *handler_context, 31 void *region_context) 32 { 33 unsigned int i, bytes = DIV_ROUND_UP(bits, 8); 34 u8 *value = (u8 *)value64; 35 36 if (address > 0xff || !value64) 37 return AE_BAD_PARAMETER; 38 39 guard(spinlock_irq)(&rtc_lock); 40 41 if (function == ACPI_WRITE) { 42 for (i = 0; i < bytes; i++, address++, value++) 43 CMOS_WRITE(*value, address); 44 45 return AE_OK; 46 } 47 48 if (function == ACPI_READ) { 49 for (i = 0; i < bytes; i++, address++, value++) 50 *value = CMOS_READ(address); 51 52 return AE_OK; 53 } 54 55 return AE_BAD_PARAMETER; 56 } 57 58 static int acpi_install_cmos_rtc_space_handler(acpi_handle handle) 59 { 60 static bool cmos_rtc_space_handler_present __read_mostly; 61 acpi_status status; 62 63 if (cmos_rtc_space_handler_present) 64 return 0; 65 66 status = acpi_install_address_space_handler(handle, 67 ACPI_ADR_SPACE_CMOS, 68 acpi_cmos_rtc_space_handler, 69 NULL, NULL); 70 if (ACPI_FAILURE(status)) { 71 pr_err("Failed to install CMOS-RTC address space handler\n"); 72 return -ENODEV; 73 } 74 75 cmos_rtc_space_handler_present = true; 76 77 return 1; 78 } 79 80 static int acpi_cmos_rtc_attach(struct acpi_device *adev, 81 const struct acpi_device_id *id) 82 { 83 int ret; 84 85 ret = acpi_install_cmos_rtc_space_handler(adev->handle); 86 if (ret < 0) 87 return ret; 88 89 if (IS_ERR_OR_NULL(acpi_create_platform_device(adev, NULL))) { 90 pr_err("Failed to create a platform device for %s\n", (char *)id->id); 91 return 0; 92 } else if (!id->driver_data) { 93 cmos_rtc_platform_device_present = true; 94 } 95 return 1; 96 } 97 98 static struct acpi_scan_handler cmos_rtc_handler = { 99 .ids = acpi_cmos_rtc_ids, 100 .attach = acpi_cmos_rtc_attach, 101 }; 102 103 void __init acpi_cmos_rtc_init(void) 104 { 105 acpi_scan_add_handler(&cmos_rtc_handler); 106 } 107