Serenity Operating System
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}