diff --git a/libraries/libstratosphere/include/stratosphere/i2c.hpp b/libraries/libstratosphere/include/stratosphere/i2c.hpp index c7579354a..c87f96732 100644 --- a/libraries/libstratosphere/include/stratosphere/i2c.hpp +++ b/libraries/libstratosphere/include/stratosphere/i2c.hpp @@ -21,5 +21,6 @@ #include #include #include +#include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_driver_service_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_driver_service_api.hpp new file mode 100644 index 000000000..08c3662e7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_driver_service_api.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::i2c::driver { + + void RegisterDriver(II2cDriver *driver); + void UnregisterDriver(II2cDriver *driver); + + Result RegisterDeviceCode(DeviceCode device_code, I2cDeviceProperty *device); + bool UnregisterDeviceCode(DeviceCode device_code); + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp index da96da9db..bab1817fc 100644 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp @@ -14,6 +14,8 @@ * along with this program. If not, see . */ #include +#include "impl/i2c_bus_manager.hpp" +#include "impl/i2c_device_property_manager.hpp" namespace ams::i2c::driver::board::nintendo_nx { @@ -32,14 +34,75 @@ namespace ams::i2c::driver::board::nintendo_nx { os::InterruptName interrupt_name; const I2cDeviceDefinition *devices; size_t num_devices; + + constexpr bool IsPowerBus() const { + return this->device_code == DeviceCode_I2c5; + } }; #include "i2c_bus_device_map.inc" + void CheckSpeedMode(SpeedMode speed_mode) { + switch (speed_mode) { + case SpeedMode_Standard: break; + case SpeedMode_Fast: break; + case SpeedMode_FastPlus: break; + case SpeedMode_HighSpeed: break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void Initialize(impl::I2cBusAccessorManager &bus_manager, impl::I2cDevicePropertyManager &device_manager) { + /* Create an accessor for each bus. */ + for (const auto &bus_def : I2cBusList) { + /* Check that the speed mode is valid. */ + CheckSpeedMode(bus_def.speed_mode); + + /* Find the bus. */ + auto *bus = bus_manager.Find([&bus_def](const auto &it) { + return it.GetRegistersPhysicalAddress() == bus_def.registers_phys_addr; + }); + + /* If the bus doesn't exist, create it. */ + if (bus == nullptr) { + /* Allocate the bus. */ + bus = bus_manager.Allocate(); + + /* Initialize the bus. */ + bus->Initialize(bus_def.registers_phys_addr, bus_def.registers_size, bus_def.interrupt_name, bus_def.IsPowerBus(), bus_def.speed_mode); + + /* Register the bus. */ + i2c::driver::RegisterDriver(bus); + } + + /* Set the bus's device code. */ + bus->RegisterDeviceCode(bus_def.device_code); + + /* Allocate and register the devices for the bus. */ + for (size_t i = 0; i < bus_def.num_devices; ++i) { + /* Get the device definition. */ + const auto &entry = bus_def.devices[i]; + + /* Allocate the device. */ + I2cDeviceProperty *device = device_manager.Allocate(entry.slave_address, AddressingMode_SevenBit); + + /* Register the device with our bus. */ + bus->RegisterDevice(device); + + /* Register the device code with our driver. */ + R_ABORT_UNLESS(i2c::driver::RegisterDeviceCode(entry.device_code, device)); + } + } + } + } void Initialize() { - /* TODO */ + /* TODO: Should these be moved into getters? They're only used here, and they never destruct. */ + static impl::I2cBusAccessorManager s_bus_accessor_manager(ddsf::GetMemoryResource()); + static impl::I2cDevicePropertyManager s_device_manager(ddsf::GetMemoryResource()); + + return Initialize(s_bus_accessor_manager, s_device_manager); } } diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp index 058f8e39d..8691dbaf5 100644 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp @@ -73,14 +73,6 @@ namespace ams::i2c::driver::board::nintendo_nx::impl { dd::PhysicalAddress GetRegistersPhysicalAddress() const { return this->registers_phys_addr; } size_t GetRegistersSize() const { return this->registers_size; } os::InterruptName GetInterruptName() const { return this->interrupt_name; } - - void RemoveFrom(BusAccessorList &lst) { - lst.erase(lst.iterator_to(*this)); - } - - bool IsLinkedToList() const { - return this->bus_accessor_list_node.IsLinked(); - } private: Result TryOpenRegulatorSession(); diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_manager.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_manager.hpp new file mode 100644 index 000000000..64ae4668b --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_manager.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "i2c_bus_accessor.hpp" +#include "i2c_i_allocator.hpp" + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + class I2cBusAccessorManager : public IAllocator { + /* ... */ + }; + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_device_property_manager.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_device_property_manager.hpp new file mode 100644 index 000000000..726d87632 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_device_property_manager.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "i2c_i_allocator.hpp" + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + class I2cDevicePropertyManager : public IAllocator { + /* ... */ + }; + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i_allocator.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i_allocator.hpp new file mode 100644 index 000000000..44e18b9ca --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i_allocator.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + template + class IAllocator { + private: + using T = typename ListType::value_type; + private: + ams::MemoryResource *memory_resource; + ListType list; + mutable os::SdkMutex list_lock; + public: + IAllocator(ams::MemoryResource *mr) : memory_resource(mr), list(), list_lock() { /* ... */ } + + ~IAllocator() { + /* TODO: Remove all entries, etc */ + AMS_ABORT("BusAccessorManager not destructible"); + } + + template + T *Allocate(Args &&...args) { + std::scoped_lock lk(this->list_lock); + + /* Allocate space for the object. */ + void *storage = this->memory_resource->Allocate(sizeof(T), alignof(T)); + AMS_ABORT_UNLESS(storage != nullptr); + + /* Construct the object. */ + T *t = new (static_cast(storage)) T(std::forward(args)...); + + /* Link the object into our list. */ + this->list.push_back(*t); + + return t; + } + + template + T *Find(F f) { + std::scoped_lock lk(this->list_lock); + + for (T &it : this->list) { + if (f(static_cast(it))) { + return std::addressof(it); + } + } + + return nullptr; + } + + /* TODO: Support free */ + }; + +} diff --git a/libraries/libstratosphere/source/i2c/driver/i2c_driver_client_api.cpp b/libraries/libstratosphere/source/i2c/driver/i2c_driver_client_api.cpp new file mode 100644 index 000000000..c5717e503 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/i2c_driver_client_api.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +#include "impl/i2c_driver_core.hpp" + +namespace ams::i2c::driver { + + void Initialize() { + return impl::InitializeDrivers(); + } + + void Finalize() { + return impl::FinalizeDrivers(); + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/i2c_driver_service_api.cpp b/libraries/libstratosphere/source/i2c/driver/i2c_driver_service_api.cpp new file mode 100644 index 000000000..340615743 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/i2c_driver_service_api.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +#include "impl/i2c_driver_core.hpp" + +namespace ams::i2c::driver { + + void RegisterDriver(II2cDriver *driver) { + return impl::RegisterDriver(driver); + } + + void UnregisterDriver(II2cDriver *driver) { + return impl::UnregisterDriver(driver); + } + + Result RegisterDeviceCode(DeviceCode device_code, I2cDeviceProperty *device) { + return impl::RegisterDeviceCode(device_code, device); + } + + bool UnregisterDeviceCode(DeviceCode device_code) { + return impl::UnregisterDeviceCode(device_code); + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.cpp b/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.cpp new file mode 100644 index 000000000..08d2609bc --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "i2c_driver_core.hpp" + +namespace ams::i2c::driver::impl { + + namespace { + + constinit os::SdkMutex g_init_mutex; + constinit int g_init_count = 0; + + i2c::driver::II2cDriver::List &GetI2cDriverList() { + static i2c::driver::II2cDriver::List s_driver_list; + return s_driver_list; + } + + ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() { + static ddsf::DeviceCodeEntryManager s_device_code_entry_manager(ddsf::GetDeviceCodeEntryHolderMemoryResource()); + return s_device_code_entry_manager; + } + + } + + + void InitializeDrivers() { + std::scoped_lock lk(g_init_mutex); + + /* Initialize all registered drivers, if this is our first initialization. */ + if ((g_init_count++) == 0) { + for (auto &driver : GetI2cDriverList()) { + driver.SafeCastTo().InitializeDriver(); + } + } + } + + void FinalizeDrivers() { + std::scoped_lock lk(g_init_mutex); + + /* If we have no remaining sessions, close. */ + if ((--g_init_count) == 0) { + /* Reset all device code entries. */ + GetDeviceCodeEntryManager().Reset(); + + /* Finalize all drivers. */ + for (auto &driver : GetI2cDriverList()) { + driver.SafeCastTo().FinalizeDriver(); + } + } + } + + void RegisterDriver(II2cDriver *driver) { + AMS_ASSERT(driver != nullptr); + GetI2cDriverList().push_back(*driver); + } + + void UnregisterDriver(II2cDriver *driver) { + AMS_ASSERT(driver != nullptr); + if (driver->IsLinkedToList()) { + auto &list = GetI2cDriverList(); + list.erase(list.iterator_to(*driver)); + } + } + + Result RegisterDeviceCode(DeviceCode device_code, I2cDeviceProperty *device) { + AMS_ASSERT(device != nullptr); + R_TRY(GetDeviceCodeEntryManager().Add(device_code, device)); + return ResultSuccess(); + } + + bool UnregisterDeviceCode(DeviceCode device_code) { + return GetDeviceCodeEntryManager().Remove(device_code); + } + + Result FindDevice(I2cDeviceProperty **out, DeviceCode device_code) { + /* Validate output. */ + AMS_ASSERT(out != nullptr); + + /* Find the device. */ + ddsf::IDevice *device; + R_TRY(GetDeviceCodeEntryManager().FindDevice(std::addressof(device), device_code)); + + /* Set output. */ + *out = device->SafeCastToPointer(); + return ResultSuccess(); + } + + Result FindDeviceByBusIndexAndAddress(I2cDeviceProperty **out, i2c::I2cBus bus_index, u16 slave_address) { + /* Validate output. */ + AMS_ASSERT(out != nullptr); + + /* Convert the bus index to a device code. */ + const DeviceCode device_code = ConvertToDeviceCode(bus_index); + + /* Find the device. */ + bool found = false; + GetDeviceCodeEntryManager().ForEachEntry([&](ddsf::DeviceCodeEntry &entry) -> bool { + /* Convert the entry to an I2cDeviceProperty. */ + auto &device = entry.GetDevice().SafeCastTo(); + auto &driver = device.GetDriver().SafeCastTo(); + + /* Check if the device is the one we're looking for. */ + if (driver.GetDeviceCode() == device_code && device.GetAddress() == slave_address) { + found = true; + *out = std::addressof(device); + return false; + } + return true; + }); + + /* Check that we found the pad. */ + R_UNLESS(found, ddsf::ResultDeviceCodeNotFound()); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.hpp b/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.hpp new file mode 100644 index 000000000..9df2e60f5 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::i2c::driver::impl { + + void InitializeDrivers(); + void FinalizeDrivers(); + + void RegisterDriver(II2cDriver *driver); + void UnregisterDriver(II2cDriver *driver); + + Result RegisterDeviceCode(DeviceCode device_code, I2cDeviceProperty *device); + bool UnregisterDeviceCode(DeviceCode device_code); + + Result FindDevice(I2cDeviceProperty **out, DeviceCode device_code); + Result FindDeviceByBusIndexAndAddress(I2cDeviceProperty **out, i2c::I2cBus bus_index, u16 slave_address); + +}