blob: e9c34c248181bffc9e3f6df0ee6b7f16ceb9c05c [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVICES_BLOCK_DRIVERS_AML_SDMMC_AML_SDMMC_H_
#define SRC_DEVICES_BLOCK_DRIVERS_AML_SDMMC_AML_SDMMC_H_
#include <fuchsia/hardware/gpio/cpp/banjo.h>
#include <fuchsia/hardware/platform/device/cpp/banjo.h>
#include <fuchsia/hardware/sdmmc/cpp/banjo.h>
#include <lib/ddk/phys-iter.h>
#include <lib/mmio/mmio.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/interrupt.h>
#include <lib/zx/status.h>
#include <threads.h>
#include <optional>
#include <vector>
#include <ddktl/device.h>
#include <fbl/auto_lock.h>
#include <fbl/condition_variable.h>
#include <fbl/span.h>
#include <soc/aml-common/aml-sdmmc.h>
#include "src/lib/vmo_store/vmo_store.h"
namespace sdmmc {
class AmlSdmmc;
using AmlSdmmcType = ddk::Device<AmlSdmmc, ddk::Suspendable, ddk::Unbindable>;
class AmlSdmmc : public AmlSdmmcType, public ddk::SdmmcProtocol<AmlSdmmc, ddk::base_protocol> {
public:
AmlSdmmc(zx_device_t* parent, zx::bti bti, ddk::MmioBuffer mmio,
ddk::MmioPinnedBuffer pinned_mmio, aml_sdmmc_config_t config, zx::interrupt irq,
const ddk::GpioProtocolClient& gpio);
virtual ~AmlSdmmc() = default;
static zx_status_t Create(void* ctx, zx_device_t* parent);
// Device protocol implementation
void DdkRelease();
void DdkSuspend(ddk::SuspendTxn txn);
void DdkUnbind(ddk::UnbindTxn txn);
// Sdmmc Protocol implementation
zx_status_t SdmmcHostInfo(sdmmc_host_info_t* out_info);
zx_status_t SdmmcSetSignalVoltage(sdmmc_voltage_t voltage);
zx_status_t SdmmcSetBusWidth(sdmmc_bus_width_t bus_width);
zx_status_t SdmmcSetBusFreq(uint32_t bus_freq);
zx_status_t SdmmcSetTiming(sdmmc_timing_t timing);
void SdmmcHwReset();
zx_status_t SdmmcPerformTuning(uint32_t cmd_idx);
zx_status_t SdmmcRequest(sdmmc_req_t* req);
zx_status_t SdmmcRegisterInBandInterrupt(const in_band_interrupt_protocol_t* interrupt_cb);
zx_status_t SdmmcRegisterVmo(uint32_t vmo_id, uint8_t client_id, zx::vmo vmo, uint64_t offset,
uint64_t size, uint32_t vmo_rights);
zx_status_t SdmmcUnregisterVmo(uint32_t vmo_id, uint8_t client_id, zx::vmo* out_vmo);
zx_status_t SdmmcRequestNew(const sdmmc_req_new_t* req, uint32_t out_response[4]);
// Visible for tests
zx_status_t Init();
void set_board_config(const aml_sdmmc_config_t& board_config) { board_config_ = board_config; }
protected:
// Visible for tests
zx_status_t Bind();
virtual zx_status_t WaitForInterruptImpl();
virtual void WaitForBus() const;
aml_sdmmc_desc_t* descs() { return static_cast<aml_sdmmc_desc_t*>(descs_buffer_.virt()); }
ddk::MmioBuffer mmio_;
private:
constexpr static size_t kResponseCount = 4;
struct TuneWindow {
uint32_t start = 0;
uint32_t size = 0;
uint32_t middle() const { return start + (size / 2); }
};
// VMO metadata that needs to be stored in accordance with the SDMMC protocol.
struct OwnedVmoInfo {
uint64_t offset;
uint64_t size;
uint32_t rights;
};
using SdmmcVmoStore = vmo_store::VmoStore<vmo_store::HashTableStorage<uint32_t, OwnedVmoInfo>>;
zx_status_t TuningDoTransfer(uint8_t* tuning_res, size_t blk_pattern_size,
uint32_t tuning_cmd_idx);
bool TuningTestSettings(fbl::Span<const uint8_t> tuning_blk, uint32_t tuning_cmd_idx);
template <typename SetParamCallback>
TuneWindow TuneDelayParam(fbl::Span<const uint8_t> tuning_blk, uint32_t tuning_cmd_idx,
uint32_t param_max, SetParamCallback& set_param);
void SetAdjDelay(uint32_t adj_delay);
void SetDelayLines(uint32_t delay);
uint32_t max_delay() const;
void ConfigureDefaultRegs();
void SetupCmdDesc(sdmmc_req_t* req, aml_sdmmc_desc_t** out_desc);
aml_sdmmc_desc_t* SetupCmdDescNew(const sdmmc_req_new_t& req);
// Prepares the VMO and sets up the data descriptors
zx_status_t SetupDataDescsDma(sdmmc_req_t* req, aml_sdmmc_desc_t* cur_desc,
aml_sdmmc_desc_t** last_desc);
// Sets up the data descriptors using the ping/pong buffers
zx_status_t SetupDataDescsPio(sdmmc_req_t* req, aml_sdmmc_desc_t* desc,
aml_sdmmc_desc_t** last_desc);
zx_status_t SetupDataDescs(sdmmc_req_t* req, aml_sdmmc_desc_t* desc,
aml_sdmmc_desc_t** last_desc);
// Returns a pointer to the LAST descriptor used.
zx::status<std::pair<aml_sdmmc_desc_t*, std::vector<fzl::PinnedVmo>>> SetupDataDescsNew(
const sdmmc_req_new_t& req, aml_sdmmc_desc_t* cur_desc);
// These return pointers to the NEXT descriptor to use.
zx::status<aml_sdmmc_desc_t*> SetupOwnedVmoDescs(const sdmmc_req_new_t& req,
const sdmmc_buffer_region_t& buffer,
vmo_store::StoredVmo<OwnedVmoInfo>& vmo,
aml_sdmmc_desc_t* cur_desc);
zx::status<std::pair<aml_sdmmc_desc_t*, fzl::PinnedVmo>> SetupUnownedVmoDescs(
const sdmmc_req_new_t& req, const sdmmc_buffer_region_t& buffer, aml_sdmmc_desc_t* cur_desc);
zx::status<aml_sdmmc_desc_t*> PopulateDescriptors(const sdmmc_req_new_t& req,
aml_sdmmc_desc_t* cur_desc,
fzl::PinnedVmo::Region region);
static zx_status_t FinishReq(sdmmc_req_t* req);
void ClearStatus();
zx_status_t WaitForInterrupt(sdmmc_req_t* req);
zx::status<std::array<uint32_t, kResponseCount>> WaitForInterruptNew(const sdmmc_req_new_t& req);
void ShutDown();
zx::bti bti_;
ddk::MmioPinnedBuffer pinned_mmio_;
const ddk::GpioProtocolClient reset_gpio_;
zx::interrupt irq_;
aml_sdmmc_config_t board_config_;
sdmmc_host_info_t dev_info_;
ddk::IoBuffer descs_buffer_;
uint32_t max_freq_, min_freq_;
fbl::Mutex mtx_;
fbl::ConditionVariable txn_finished_ TA_GUARDED(mtx_);
std::atomic<bool> dead_ TA_GUARDED(mtx_);
std::atomic<bool> pending_txn_ TA_GUARDED(mtx_);
std::optional<SdmmcVmoStore> registered_vmos_[SDMMC_MAX_CLIENT_ID + 1];
size_t request_count_ = 0;
};
} // namespace sdmmc
#endif // SRC_DEVICES_BLOCK_DRIVERS_AML_SDMMC_AML_SDMMC_H_