1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Implement 'Simple Boot Flag Specification 2.0' 4 */ 5#include <linux/types.h> 6#include <linux/kernel.h> 7#include <linux/init.h> 8#include <linux/string.h> 9#include <linux/spinlock.h> 10#include <linux/acpi.h> 11#include <asm/io.h> 12 13#include <linux/mc146818rtc.h> 14 15#define SBF_RESERVED (0x78) 16#define SBF_PNPOS (1<<0) 17#define SBF_BOOTING (1<<1) 18#define SBF_DIAG (1<<2) 19#define SBF_PARITY (1<<7) 20 21int sbf_port __initdata = -1; /* set via acpi_boot_init() */ 22 23static int __init parity(u8 v) 24{ 25 int x = 0; 26 int i; 27 28 for (i = 0; i < 8; i++) { 29 x ^= (v & 1); 30 v >>= 1; 31 } 32 33 return x; 34} 35 36static void __init sbf_write(u8 v) 37{ 38 unsigned long flags; 39 40 if (sbf_port != -1) { 41 v &= ~SBF_PARITY; 42 if (!parity(v)) 43 v |= SBF_PARITY; 44 45 printk(KERN_INFO "Simple Boot Flag at 0x%x set to 0x%x\n", 46 sbf_port, v); 47 48 spin_lock_irqsave(&rtc_lock, flags); 49 CMOS_WRITE(v, sbf_port); 50 spin_unlock_irqrestore(&rtc_lock, flags); 51 } 52} 53 54static u8 __init sbf_read(void) 55{ 56 unsigned long flags; 57 u8 v; 58 59 if (sbf_port == -1) 60 return 0; 61 62 spin_lock_irqsave(&rtc_lock, flags); 63 v = CMOS_READ(sbf_port); 64 spin_unlock_irqrestore(&rtc_lock, flags); 65 66 return v; 67} 68 69static int __init sbf_value_valid(u8 v) 70{ 71 if (v & SBF_RESERVED) /* Reserved bits */ 72 return 0; 73 if (!parity(v)) 74 return 0; 75 76 return 1; 77} 78 79static int __init sbf_init(void) 80{ 81 u8 v; 82 83 if (sbf_port == -1) 84 return 0; 85 86 v = sbf_read(); 87 if (!sbf_value_valid(v)) { 88 printk(KERN_WARNING "Simple Boot Flag value 0x%x read from " 89 "CMOS RAM was invalid\n", v); 90 } 91 92 v &= ~SBF_RESERVED; 93 v &= ~SBF_BOOTING; 94 v &= ~SBF_DIAG; 95#if defined(CONFIG_ISAPNP) 96 v |= SBF_PNPOS; 97#endif 98 sbf_write(v); 99 100 return 0; 101} 102arch_initcall(sbf_init); 103