Serenity Operating System
at master 123 lines 3.7 kB view raw
1/* 2 * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <Kernel/Arch/Delay.h> 8#include <Kernel/Graphics/Intel/Auxiliary/GMBusConnector.h> 9#include <Kernel/PhysicalAddress.h> 10 11namespace Kernel { 12 13enum class GMBusStatus { 14 TransactionCompletion, 15 HardwareReady 16}; 17 18enum GMBusCycle { 19 Wait = 1, 20 Stop = 4, 21}; 22 23ErrorOr<NonnullOwnPtr<GMBusConnector>> GMBusConnector::create_with_physical_address(PhysicalAddress gmbus_start_address) 24{ 25 auto registers_mapping = TRY(map_typed<GMBusRegisters volatile>(gmbus_start_address, sizeof(GMBusRegisters), Memory::Region::Access::ReadWrite)); 26 return adopt_nonnull_own_or_enomem(new (nothrow) GMBusConnector(move(registers_mapping))); 27} 28 29GMBusConnector::GMBusConnector(Memory::TypedMapping<GMBusRegisters volatile> registers_mapping) 30 : m_gmbus_registers(move(registers_mapping)) 31{ 32 set_default_rate(); 33 set_pin_pair(PinPair::DedicatedAnalog); 34} 35 36bool GMBusConnector::wait_for(GMBusStatus desired_status, size_t milliseconds_timeout) 37{ 38 VERIFY(m_access_lock.is_locked()); 39 size_t milliseconds_passed = 0; 40 while (1) { 41 if (milliseconds_timeout < milliseconds_passed) 42 return false; 43 full_memory_barrier(); 44 u32 status = m_gmbus_registers->status; 45 full_memory_barrier(); 46 VERIFY(!(status & (1 << 10))); // error happened 47 switch (desired_status) { 48 case GMBusStatus::HardwareReady: 49 if (status & (1 << 11)) 50 return true; 51 break; 52 case GMBusStatus::TransactionCompletion: 53 if (status & (1 << 14)) 54 return true; 55 break; 56 default: 57 VERIFY_NOT_REACHED(); 58 } 59 microseconds_delay(1000); 60 milliseconds_passed++; 61 } 62} 63 64ErrorOr<void> GMBusConnector::write(unsigned address, u32 data) 65{ 66 VERIFY(address < 256); 67 SpinlockLocker locker(m_access_lock); 68 full_memory_barrier(); 69 m_gmbus_registers->data = data; 70 full_memory_barrier(); 71 m_gmbus_registers->command = ((address << 1) | (1 << 16) | (GMBusCycle::Wait << 25) | (1 << 30)); 72 full_memory_barrier(); 73 if (!wait_for(GMBusStatus::TransactionCompletion, 250)) 74 return Error::from_errno(EBUSY); 75 return {}; 76} 77 78void GMBusConnector::set_default_rate() 79{ 80 // FIXME: Verify GMBUS Rate Select is set only when GMBUS is idle 81 SpinlockLocker locker(m_access_lock); 82 // Set the rate to 100KHz 83 m_gmbus_registers->clock = m_gmbus_registers->clock & ~(0b111 << 8); 84} 85 86void GMBusConnector::set_pin_pair(PinPair pin_pair) 87{ 88 // FIXME: Verify GMBUS is idle 89 SpinlockLocker locker(m_access_lock); 90 m_gmbus_registers->clock = (m_gmbus_registers->clock & (~0b111)) | (pin_pair & 0b111); 91} 92 93ErrorOr<void> GMBusConnector::read(unsigned address, u8* buf, size_t length) 94{ 95 VERIFY(address < 256); 96 SpinlockLocker locker(m_access_lock); 97 size_t nread = 0; 98 auto read_set = [&] { 99 full_memory_barrier(); 100 u32 data = m_gmbus_registers->data; 101 full_memory_barrier(); 102 for (size_t index = 0; index < 4; index++) { 103 if (nread == length) 104 break; 105 buf[nread] = (data >> (8 * index)) & 0xFF; 106 nread++; 107 } 108 }; 109 110 full_memory_barrier(); 111 m_gmbus_registers->command = (1 | (address << 1) | (length << 16) | (GMBusCycle::Wait << 25) | (1 << 30)); 112 full_memory_barrier(); 113 while (nread < length) { 114 if (!wait_for(GMBusStatus::HardwareReady, 250)) 115 return Error::from_errno(EBUSY); 116 read_set(); 117 } 118 if (!wait_for(GMBusStatus::TransactionCompletion, 250)) 119 return Error::from_errno(EBUSY); 120 return {}; 121} 122 123}