| // 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. |
| |
| #pragma once |
| |
| #include <threads.h> |
| |
| #include <atomic> |
| |
| #include <ddk/trace/event.h> |
| #include <ddktl/device.h> |
| #include <ddktl/protocol/block.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/condition_variable.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| #include <lib/operation/block.h> |
| #include <zircon/thread_annotations.h> |
| |
| #include "sdmmc-device.h" |
| |
| namespace sdmmc { |
| |
| class SdmmcBlockDevice; |
| using SdmmcBlockDeviceType = ddk::Device<SdmmcBlockDevice, ddk::GetSizable, ddk::Unbindable>; |
| |
| class SdmmcBlockDevice : public SdmmcBlockDeviceType, |
| public ddk::BlockImplProtocol<SdmmcBlockDevice, ddk::base_protocol>, |
| public fbl::RefCounted<SdmmcBlockDevice> { |
| public: |
| using BlockOperation = block::UnownedOperation<>; |
| |
| SdmmcBlockDevice(zx_device_t* parent, const SdmmcDevice& sdmmc) |
| : SdmmcBlockDeviceType(parent), sdmmc_(sdmmc) { |
| block_info_.max_transfer_size = static_cast<uint32_t>(sdmmc_.host_info().max_transfer_size); |
| } |
| virtual ~SdmmcBlockDevice() = default; |
| |
| static zx_status_t Create(zx_device_t* parent, const SdmmcDevice& sdmmc, |
| fbl::RefPtr<SdmmcBlockDevice>* out_dev); |
| |
| zx_status_t ProbeSd(); |
| zx_status_t ProbeMmc(); |
| |
| zx_status_t AddDevice(); |
| |
| void DdkUnbind(); |
| void DdkRelease(); |
| |
| zx_off_t DdkGetSize(); |
| |
| void BlockImplQuery(block_info_t* info_out, size_t* block_op_size_out); |
| void BlockImplQueue(block_op_t* btxn, block_impl_queue_callback completion_cb, void* cookie); |
| |
| // Visible for testing. |
| zx_status_t StartWorkerThread(); |
| void StopWorkerThread(); |
| |
| virtual void DoTxn(BlockOperation* txn); |
| |
| protected: |
| virtual SdmmcDevice& sdmmc() { return sdmmc_; } |
| |
| virtual void BlockComplete(BlockOperation* txn, zx_status_t status, trace_async_id_t async_id); |
| |
| virtual zx_status_t WaitForTran(); |
| |
| block_info_t block_info_; |
| |
| private: |
| int WorkerThread(); |
| |
| zx_status_t MmcDoSwitch(uint8_t index, uint8_t value); |
| zx_status_t MmcSetBusWidth(sdmmc_bus_width_t bus_width, uint8_t mmc_ext_csd_bus_width); |
| sdmmc_bus_width_t MmcSelectBusWidth(); |
| zx_status_t MmcSwitchTiming(sdmmc_timing_t new_timing); |
| zx_status_t MmcSwitchFreq(uint32_t new_freq); |
| zx_status_t MmcDecodeExtCsd(const uint8_t* raw_ext_csd); |
| bool MmcSupportsHs(); |
| bool MmcSupportsHsDdr(); |
| bool MmcSupportsHs200(); |
| bool MmcSupportsHs400(); |
| |
| std::atomic<trace_async_id_t> async_id_; |
| |
| SdmmcDevice sdmmc_; |
| |
| sdmmc_bus_width_t bus_width_; |
| sdmmc_timing_t timing_; |
| |
| uint32_t clock_rate_; // Bus clock rate |
| |
| // mmc |
| uint32_t raw_cid_[4]; |
| uint32_t raw_csd_[4]; |
| uint8_t raw_ext_csd_[512]; |
| |
| fbl::Mutex lock_; |
| fbl::ConditionVariable worker_event_ TA_GUARDED(lock_); |
| |
| // blockio requests |
| block::UnownedOperationQueue<> txn_list_ TA_GUARDED(lock_); |
| |
| // outstanding request (1 right now) |
| sdmmc_req_t req_; |
| |
| thrd_t worker_thread_ = 0; |
| |
| std::atomic<bool> dead_ = false; |
| |
| bool is_sd_ = false; |
| }; |
| |
| } // namespace sdmmc |