blob: e98079626dc9f3c85a3680c1b5cf49ceb94b3735 [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.
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/metadata.h>
#include <ddk/metadata/gpt.h>
#include <ddk/platform-defs.h>
#include <ddk/protocol/platform/bus.h>
#include <fbl/algorithm.h>
#include <hwreg/bitfields.h>
#include <soc/mt8167/mt8167-hw.h>
#include <soc/mt8167/mt8167-sdmmc.h>
#include "mt8167.h"
namespace {
constexpr uintptr_t kClkBaseAligned =
fbl::round_down<uintptr_t, uintptr_t>(MT8167_XO_BASE, PAGE_SIZE);
constexpr size_t kClkOffset = MT8167_XO_BASE - kClkBaseAligned;
constexpr size_t kClkSizeAligned =
fbl::round_up<size_t, size_t>(kClkOffset + MT8167_XO_SIZE, PAGE_SIZE);
constexpr uintptr_t kPllBaseAligned =
fbl::round_down<uintptr_t, uintptr_t>(MT8167_AP_MIXED_SYS_BASE, PAGE_SIZE);
constexpr size_t kPllOffset = MT8167_AP_MIXED_SYS_BASE - kPllBaseAligned;
constexpr size_t kPllSizeAligned =
fbl::round_up<size_t, size_t>(kPllOffset + MT8167_AP_MIXED_SYS_SIZE, PAGE_SIZE);
// MMPLL is derived from the 26 MHz crystal oscillator.
constexpr uint32_t kMmPllSrcClkFreq = 26000000;
constexpr uint32_t kFifoDepth = 128;
constexpr uint32_t kSrcClkFreq = 200000000;
} // namespace
namespace board_mt8167 {
class ClkMuxSel0 : public hwreg::RegisterBase<ClkMuxSel0, uint32_t> {
public:
static constexpr uint32_t kClkMmPllDiv2 = 7;
static auto Get() { return hwreg::RegisterAddr<ClkMuxSel0>(kClkOffset); }
DEF_FIELD(13, 11, msdc0_mux_sel);
};
class MmPllCon1 : public hwreg::RegisterBase<MmPllCon1, uint32_t> {
public:
static constexpr uint32_t kDiv1 = 0;
static constexpr uint32_t kDiv2 = 1;
static constexpr uint32_t kDiv4 = 2;
static constexpr uint32_t kDiv8 = 3;
static constexpr uint32_t kDiv16 = 4;
static constexpr uint32_t kPcwFracBits = 14;
static auto Get() { return hwreg::RegisterAddr<MmPllCon1>(kPllOffset + 0x164); }
DEF_BIT(31, change);
DEF_FIELD(26, 24, div);
DEF_FIELD(20, 0, pcw);
};
zx_status_t Mt8167::EmmcInit() {
static const pbus_mmio_t emmc_mmios[] = {
{
.base = MT8167_MSDC0_BASE,
.length = MT8167_MSDC0_SIZE,
}
};
static const pbus_bti_t emmc_btis[] = {
{
.iommu_index = 0,
.bti_id = BTI_EMMC,
}
};
static const MtkSdmmcConfig emmc_config = {
.fifo_depth = kFifoDepth,
.src_clk_freq = kSrcClkFreq
};
static const guid_map_t guid_map[] = {
{ "boot_a", GUID_ZIRCON_A_VALUE },
{ "boot_b", GUID_ZIRCON_B_VALUE },
{ "vbmeta_a", GUID_VBMETA_A_VALUE },
{ "vbmeta_b", GUID_VBMETA_B_VALUE },
{ "userdata", GUID_FVM_VALUE },
// For now, just give the paver a place to write Zircon-R,
// even though the bootloader won't support it.
{ "vendor_a", GUID_ZIRCON_R_VALUE },
};
static_assert(fbl::count_of(guid_map) <= DEVICE_METADATA_GUID_MAP_MAX_ENTRIES);
static const pbus_metadata_t emmc_metadata[] = {
{
.type = DEVICE_METADATA_PRIVATE,
.data_buffer = &emmc_config,
.data_size = sizeof(emmc_config)
},
{
.type = DEVICE_METADATA_GUID_MAP,
.data_buffer = guid_map,
.data_size = sizeof(guid_map)
}
};
static const pbus_irq_t emmc_irqs[] = {
{
.irq = MT8167_IRQ_MSDC0,
.mode = ZX_INTERRUPT_MODE_EDGE_HIGH
}
};
static const pbus_gpio_t emmc_gpios[] = {
{
.gpio = MT8167_GPIO_MSDC0_RST
}
};
pbus_dev_t emmc_dev = {};
emmc_dev.name = "emmc";
emmc_dev.vid = PDEV_VID_MEDIATEK;
emmc_dev.did = PDEV_DID_MEDIATEK_EMMC;
emmc_dev.mmio_list = emmc_mmios;
emmc_dev.mmio_count = countof(emmc_mmios);
emmc_dev.bti_list = emmc_btis;
emmc_dev.bti_count = countof(emmc_btis);
emmc_dev.metadata_list = emmc_metadata;
emmc_dev.metadata_count = countof(emmc_metadata);
emmc_dev.irq_list = emmc_irqs;
emmc_dev.irq_count = countof(emmc_irqs);
emmc_dev.gpio_list = emmc_gpios;
emmc_dev.gpio_count = countof(emmc_gpios);
// TODO(bradenkell): Have the clock driver do this once muxing is supported.
zx::unowned_resource root_resource(get_root_resource());
std::optional<ddk::MmioBuffer> clk_mmio;
zx_status_t status = ddk::MmioBuffer::Create(kClkBaseAligned, kClkSizeAligned, *root_resource,
ZX_CACHE_POLICY_UNCACHED_DEVICE, &clk_mmio);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: Failed to set MSDC0 clock: %d\n", __FUNCTION__, status);
return status;
}
std::optional<ddk::MmioBuffer> pll_mmio;
status = ddk::MmioBuffer::Create(kPllBaseAligned, kPllSizeAligned, *root_resource,
ZX_CACHE_POLICY_UNCACHED_DEVICE, &pll_mmio);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: Failed to set MSDC0 clock: %d\n", __FUNCTION__, status);
return status;
}
constexpr uint32_t div_value = MmPllCon1::kDiv4;
// The MSDC0 clock will be set to MMPLL/2, so shift an extra bit to get 400 MHz.
constexpr uint32_t src_clk_shift = 1 + MmPllCon1::kPcwFracBits + div_value;
constexpr uint64_t pcw =
(static_cast<uint64_t>(kSrcClkFreq) << src_clk_shift) / kMmPllSrcClkFreq;
MmPllCon1::Get()
.ReadFrom(&(*pll_mmio))
.set_change(1)
.set_div(div_value)
.set_pcw(pcw)
.WriteTo(&(*pll_mmio));
ClkMuxSel0::Get()
.ReadFrom(&(*clk_mmio))
.set_msdc0_mux_sel(ClkMuxSel0::kClkMmPllDiv2)
.WriteTo(&(*clk_mmio));
if ((status = pbus_.DeviceAdd(&emmc_dev)) != ZX_OK) {
zxlogf(ERROR, "%s: DeviceAdd MSDC0 failed: %d\n", __FUNCTION__, status);
}
return status;
}
} // namespace board_mt8167