blob: bf20a132736909c089f87f831562fc0fdc11aafa [file] [log] [blame]
// Copyright 2018 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 <hw/sdmmc.h>
#include <hwreg/bitfields.h>
// Define weak specialization methods that can be overridden in tests. There is a slight performance
// hit as doing this prevents the MMIO accesses from being inlined.
template <>
template <>
__WEAK uint8_t ddk::MmioBuffer::Read<uint8_t>(zx_off_t offs) const {
return *reinterpret_cast<volatile uint8_t*>(ptr_ + offs);
}
template <>
template <>
__WEAK uint32_t ddk::MmioBuffer::Read<uint32_t>(zx_off_t offs) const {
return *reinterpret_cast<volatile uint32_t*>(ptr_ + offs);
}
template <>
template <>
__WEAK void ddk::MmioBuffer::Write<uint32_t>(uint32_t val, zx_off_t offs) const {
*reinterpret_cast<volatile uint32_t*>(ptr_ + offs) = val;
hw_mb();
}
namespace sdmmc {
class MsdcCfg : public hwreg::RegisterBase<MsdcCfg, uint32_t> {
public:
static constexpr uint32_t kCardCkModeDiv = 0;
static constexpr uint32_t kCardCkModeNoDiv = 1;
static constexpr uint32_t kCardCkModeDdr = 2;
static constexpr uint32_t kCardCkModeHs400 = 3;
static auto Get() { return hwreg::RegisterAddr<MsdcCfg>(0x00); }
DEF_FIELD(21, 20, card_ck_mode);
DEF_BIT(18, hs400_ck_mode);
DEF_FIELD(15, 8, card_ck_div);
DEF_BIT(7, card_ck_stable);
DEF_BIT(3, pio_mode);
DEF_BIT(2, reset);
DEF_BIT(1, ck_pwr_down);
};
class MsdcIoCon : public hwreg::RegisterBase<MsdcIoCon, uint32_t> {
public:
static constexpr uint32_t kSampleRisingEdge = 0;
static constexpr uint32_t kSampleFallingEdge = 1;
static auto Get() { return hwreg::RegisterAddr<MsdcIoCon>(0x04); }
DEF_BIT(2, data_sample);
DEF_BIT(1, cmd_sample);
};
class MsdcInt : public hwreg::RegisterBase<MsdcInt, uint32_t> {
public:
static constexpr uint32_t kAllInterruptBits = 0xffffffff;
static auto Get() { return hwreg::RegisterAddr<MsdcInt>(0x0c); }
bool CmdInterrupt() {
return cmd_ready() || cmd_timeout() || cmd_crc_err();
}
bool DataInterrupt() {
return transfer_complete() || data_timeout() || data_crc_err() || bd_checksum_err() ||
gpd_checksum_err();
}
DEF_BIT(18, gpd_checksum_err);
DEF_BIT(17, bd_checksum_err);
DEF_BIT(15, data_crc_err);
DEF_BIT(14, data_timeout);
DEF_BIT(12, transfer_complete);
DEF_BIT(10, cmd_crc_err);
DEF_BIT(9, cmd_timeout);
DEF_BIT(8, cmd_ready);
};
class MsdcIntEn : public hwreg::RegisterBase<MsdcIntEn, uint32_t> {
public:
static auto Get() { return hwreg::RegisterAddr<MsdcIntEn>(0x10); }
DEF_BIT(18, gpd_checksum_err_enable);
DEF_BIT(17, bd_checksum_err_enable);
DEF_BIT(15, data_crc_err_enable);
DEF_BIT(14, data_timeout_enable);
DEF_BIT(12, transfer_complete_enable);
DEF_BIT(10, cmd_crc_err_enable);
DEF_BIT(9, cmd_timeout_enable);
DEF_BIT(8, cmd_ready_enable);
};
class MsdcFifoCs : public hwreg::RegisterBase<MsdcFifoCs, uint32_t> {
public:
static auto Get() { return hwreg::RegisterAddr<MsdcFifoCs>(0x14); }
DEF_BIT(31, fifo_clear);
DEF_FIELD(23, 16, tx_fifo_count);
DEF_FIELD(7, 0, rx_fifo_count);
};
class MsdcRxData : public hwreg::RegisterBase<MsdcRxData, uint8_t> {
public:
static auto Get() { return hwreg::RegisterAddr<MsdcRxData>(0x1c); }
DEF_FIELD(7, 0, data);
};
class SdcCfg : public hwreg::RegisterBase<SdcCfg, uint32_t> {
public:
static constexpr uint32_t kReadTimeoutMax = 0xff;
static constexpr uint32_t kWriteTimeoutMax = 0x1fff;
static constexpr uint32_t kBusWidth1 = 0;
static constexpr uint32_t kBusWidth4 = 1;
static constexpr uint32_t kBusWidth8 = 2;
static auto Get() { return hwreg::RegisterAddr<SdcCfg>(0x30); }
DEF_FIELD(31, 24, read_timeout);
DEF_BIT(20, sdio_interrupt_enable);
DEF_BIT(19, sdio_enable);
DEF_FIELD(17, 16, bus_width);
};
class SdcCmd : public hwreg::RegisterBase<SdcCmd, uint32_t> {
public:
static constexpr uint32_t kAutoCmd12 = 1;
static constexpr uint32_t kBlockTypeSingle = 1;
static constexpr uint32_t kBlockTypeMulti = 2;
static constexpr uint32_t kRespTypeR1 = 1;
static constexpr uint32_t kRespTypeR2 = 2;
static constexpr uint32_t kRespTypeR3 = 3;
static constexpr uint32_t kRespTypeR4 = 4;
static constexpr uint32_t kRespTypeR1b = 7;
static auto Get() { return hwreg::RegisterAddr<SdcCmd>(0x34); }
static SdcCmd FromRequest(const sdmmc_req_t* req) {
SdcCmd cmd = Get().FromValue(0);
cmd.set_cmd(req->cmd_idx);
uint32_t resp_type = req->cmd_flags & (SDMMC_RESP_R1 |
SDMMC_RESP_R2 |
SDMMC_RESP_R3 |
SDMMC_RESP_R1b);
if (resp_type == SDMMC_RESP_R1) {
cmd.set_resp_type(kRespTypeR1);
} else if (resp_type == SDMMC_RESP_R2) {
cmd.set_resp_type(kRespTypeR2);
} else if (resp_type == SDMMC_RESP_R3) {
cmd.set_resp_type(kRespTypeR3);
} else if (resp_type == SDMMC_RESP_R1b) {
cmd.set_resp_type(kRespTypeR1b);
}
cmd.set_block_size(req->blocksize);
if (req->cmd_flags & SDMMC_RESP_DATA_PRESENT) {
if (req->cmd_flags & SDMMC_CMD_MULTI_BLK) {
cmd.set_block_type(kBlockTypeMulti);
} else {
cmd.set_block_type(kBlockTypeSingle);
}
if (!(req->cmd_flags & SDMMC_CMD_READ)) {
cmd.set_write(1);
}
if (req->blockcount > 1) {
cmd.set_auto_cmd(kAutoCmd12);
}
}
if (req->cmd_flags & SDMMC_CMD_TYPE_ABORT) {
cmd.set_stop(1);
}
return cmd;
}
DEF_FIELD(29, 28, auto_cmd);
DEF_FIELD(27, 16, block_size);
DEF_BIT(14, stop);
DEF_BIT(13, write);
DEF_FIELD(12, 11, block_type);
DEF_FIELD(9, 7, resp_type);
DEF_FIELD(5, 0, cmd);
};
class SdcArg : public hwreg::RegisterBase<SdcArg, uint32_t> {
public:
static auto Get() { return hwreg::RegisterAddr<SdcArg>(0x38); }
};
class SdcStatus : public hwreg::RegisterBase<SdcStatus, uint32_t> {
public:
static auto Get() { return hwreg::RegisterAddr<SdcStatus>(0x3c); }
uint32_t busy() {
return cmd_busy() || sdc_busy();
}
DEF_BIT(1, cmd_busy);
DEF_BIT(0, sdc_busy);
};
class SdcResponse : public hwreg::RegisterBase<SdcResponse, uint32_t> {
public:
static auto Get(int index) { return hwreg::RegisterAddr<SdcResponse>(0x40 + (index << 2)); }
DEF_FIELD(31, 0, response);
};
class SdcBlockNum : public hwreg::RegisterBase<SdcBlockNum, uint32_t> {
public:
static auto Get() { return hwreg::RegisterAddr<SdcStatus>(0x50); }
};
class DmaStartAddrHigh4Bits : public hwreg::RegisterBase<DmaStartAddrHigh4Bits, uint32_t> {
public:
static auto Get() { return hwreg::RegisterAddr<DmaStartAddrHigh4Bits>(0x8c); }
DEF_FIELD(3, 0, address);
DmaStartAddrHigh4Bits& set(uint64_t addr) {
set_address((addr & kAddressMask) >> 32);
return *this;
}
private:
static constexpr uint64_t kAddressMask = 0xf00000000;
};
class DmaStartAddr : public hwreg::RegisterBase<DmaStartAddr, uint32_t> {
public:
static auto Get() { return hwreg::RegisterAddr<DmaStartAddr>(0x90); }
DEF_FIELD(31, 0, address);
DmaStartAddr& set(uint64_t addr) {
set_address(addr & kAddressMask);
return *this;
}
private:
static constexpr uint64_t kAddressMask = 0xffffffff;
};
class DmaCtrl : public hwreg::RegisterBase<DmaCtrl, uint32_t> {
public:
static constexpr uint32_t kDmaModeBasic = 0;
static constexpr uint32_t kDmaModeDescriptor = 1;
static auto Get() { return hwreg::RegisterAddr<DmaCtrl>(0x98); }
DEF_BIT(10, last_buffer);
DEF_BIT(8, dma_mode);
DEF_BIT(1, dma_stop);
DEF_BIT(0, dma_start);
};
class DmaCfg : public hwreg::RegisterBase<DmaCfg, uint32_t> {
public:
static auto Get() { return hwreg::RegisterAddr<DmaCfg>(0x9c); }
DEF_BIT(1, checksum_enable);
DEF_BIT(0, dma_active);
};
class DmaLength : public hwreg::RegisterBase<DmaLength, uint32_t> {
public:
static auto Get() { return hwreg::RegisterAddr<DmaLength>(0xa8); }
};
class PadTune0 : public hwreg::RegisterBase<PadTune0, uint32_t> {
public:
static constexpr int kDelayMax = 0x1f;
static auto Get() { return hwreg::RegisterAddr<PadTune0>(0xf0); }
DEF_BIT(21, cmd_delay_sel);
DEF_FIELD(20, 16, cmd_delay);
DEF_BIT(13, data_delay_sel);
DEF_FIELD(12, 8, data_delay);
};
class GpDmaDescriptorInfo : public hwreg::RegisterBase<GpDmaDescriptorInfo, uint32_t> {
public:
DEF_FIELD(31, 28, bdma_desc_addr_high_4_bits);
DEF_FIELD(27, 24, next_addr_high_4_bits);
DEF_FIELD(15, 8, checksum);
DEF_BIT(1, bdp);
DEF_BIT(0, hwo);
GpDmaDescriptorInfo& set_bdma_desc_addr(uint64_t addr) {
set_bdma_desc_addr_high_4_bits((addr & kAddressMask) >> 32);
return *this;
}
GpDmaDescriptorInfo& set_next_addr(uint64_t addr) {
set_next_addr_high_4_bits((addr & kAddressMask) >> 32);
return *this;
}
private:
static constexpr uint64_t kAddressMask = 0xf00000000;
};
class BDmaDescriptorInfo : public hwreg::RegisterBase<BDmaDescriptorInfo, uint32_t> {
public:
DEF_FIELD(31, 28, buffer_addr_high_4_bits);
DEF_FIELD(27, 24, next_addr_high_4_bits);
DEF_FIELD(15, 8, checksum);
DEF_BIT(0, last);
BDmaDescriptorInfo& set_buffer_addr(uint64_t addr) {
set_buffer_addr_high_4_bits((addr & kAddressMask) >> 32);
return *this;
}
BDmaDescriptorInfo& set_next_addr(uint64_t addr) {
set_next_addr_high_4_bits((addr & kAddressMask) >> 32);
return *this;
}
private:
static constexpr uint64_t kAddressMask = 0xf00000000;
};
} // namespace sdmmc