1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <ddk/debug.h>
6 #include <ddk/device.h>
7 #include <ddk/metadata.h>
8 #include <ddk/metadata/gpt.h>
9 #include <ddk/platform-defs.h>
10 #include <ddk/protocol/platform/bus.h>
11 #include <fbl/algorithm.h>
12 #include <hwreg/bitfields.h>
13 #include <soc/mt8167/mt8167-hw.h>
14 #include <soc/mt8167/mt8167-sdmmc.h>
15 
16 #include "mt8167.h"
17 
18 namespace {
19 
20 constexpr uintptr_t kClkBaseAligned =
21     fbl::round_down<uintptr_t, uintptr_t>(MT8167_XO_BASE, PAGE_SIZE);
22 constexpr size_t kClkOffset = MT8167_XO_BASE - kClkBaseAligned;
23 constexpr size_t kClkSizeAligned =
24     fbl::round_up<size_t, size_t>(kClkOffset + MT8167_XO_SIZE, PAGE_SIZE);
25 
26 constexpr uintptr_t kPllBaseAligned =
27     fbl::round_down<uintptr_t, uintptr_t>(MT8167_AP_MIXED_SYS_BASE, PAGE_SIZE);
28 constexpr size_t kPllOffset = MT8167_AP_MIXED_SYS_BASE - kPllBaseAligned;
29 constexpr size_t kPllSizeAligned =
30     fbl::round_up<size_t, size_t>(kPllOffset + MT8167_AP_MIXED_SYS_SIZE, PAGE_SIZE);
31 
32 // MMPLL is derived from the 26 MHz crystal oscillator.
33 constexpr uint32_t kMmPllSrcClkFreq = 26000000;
34 
35 constexpr uint32_t kFifoDepth = 128;
36 constexpr uint32_t kSrcClkFreq = 200000000;
37 
38 }  // namespace
39 
40 namespace board_mt8167 {
41 
42 class ClkMuxSel0 : public hwreg::RegisterBase<ClkMuxSel0, uint32_t> {
43 public:
44     static constexpr uint32_t kClkMmPllDiv2 = 7;
45 
Get()46     static auto Get() { return hwreg::RegisterAddr<ClkMuxSel0>(kClkOffset); }
47 
48     DEF_FIELD(13, 11, msdc0_mux_sel);
49 };
50 
51 class MmPllCon1 : public hwreg::RegisterBase<MmPllCon1, uint32_t> {
52 public:
53     static constexpr uint32_t kDiv1  = 0;
54     static constexpr uint32_t kDiv2  = 1;
55     static constexpr uint32_t kDiv4  = 2;
56     static constexpr uint32_t kDiv8  = 3;
57     static constexpr uint32_t kDiv16 = 4;
58 
59     static constexpr uint32_t kPcwFracBits = 14;
60 
Get()61     static auto Get() { return hwreg::RegisterAddr<MmPllCon1>(kPllOffset + 0x164); }
62 
63     DEF_BIT(31, change);
64     DEF_FIELD(26, 24, div);
65     DEF_FIELD(20, 0, pcw);
66 };
67 
EmmcInit()68 zx_status_t Mt8167::EmmcInit() {
69     static const pbus_mmio_t emmc_mmios[] = {
70         {
71             .base = MT8167_MSDC0_BASE,
72             .length = MT8167_MSDC0_SIZE,
73         }
74     };
75 
76     static const pbus_bti_t emmc_btis[] = {
77         {
78             .iommu_index = 0,
79             .bti_id = BTI_EMMC,
80         }
81     };
82 
83     static const MtkSdmmcConfig emmc_config = {
84         .fifo_depth = kFifoDepth,
85         .src_clk_freq = kSrcClkFreq
86     };
87 
88     static const guid_map_t guid_map[] = {
89         { "boot_a", GUID_ZIRCON_A_VALUE },
90         { "boot_b", GUID_ZIRCON_B_VALUE },
91         { "vbmeta_a", GUID_VBMETA_A_VALUE },
92         { "vbmeta_b", GUID_VBMETA_B_VALUE },
93         { "userdata", GUID_FVM_VALUE },
94         // For now, just give the paver a place to write Zircon-R,
95         // even though the bootloader won't support it.
96         { "vendor_a", GUID_ZIRCON_R_VALUE },
97     };
98     static_assert(fbl::count_of(guid_map) <= DEVICE_METADATA_GUID_MAP_MAX_ENTRIES);
99 
100     static const pbus_metadata_t emmc_metadata[] = {
101         {
102             .type = DEVICE_METADATA_PRIVATE,
103             .data_buffer = &emmc_config,
104             .data_size = sizeof(emmc_config)
105         },
106         {
107             .type = DEVICE_METADATA_GUID_MAP,
108             .data_buffer = guid_map,
109             .data_size = sizeof(guid_map)
110         }
111     };
112 
113     static const pbus_irq_t emmc_irqs[] = {
114         {
115             .irq = MT8167_IRQ_MSDC0,
116             .mode = ZX_INTERRUPT_MODE_EDGE_HIGH
117         }
118     };
119 
120     static const pbus_gpio_t emmc_gpios[] = {
121         {
122             .gpio = MT8167_GPIO_MSDC0_RST
123         }
124     };
125 
126     pbus_dev_t emmc_dev = {};
127     emmc_dev.name = "emmc";
128     emmc_dev.vid = PDEV_VID_MEDIATEK;
129     emmc_dev.did = PDEV_DID_MEDIATEK_EMMC;
130     emmc_dev.mmio_list = emmc_mmios;
131     emmc_dev.mmio_count = countof(emmc_mmios);
132     emmc_dev.bti_list = emmc_btis;
133     emmc_dev.bti_count = countof(emmc_btis);
134     emmc_dev.metadata_list = emmc_metadata;
135     emmc_dev.metadata_count = countof(emmc_metadata);
136     emmc_dev.irq_list = emmc_irqs;
137     emmc_dev.irq_count = countof(emmc_irqs);
138     emmc_dev.gpio_list = emmc_gpios;
139     emmc_dev.gpio_count = countof(emmc_gpios);
140 
141     // TODO(bradenkell): Have the clock driver do this once muxing is supported.
142     zx::unowned_resource root_resource(get_root_resource());
143     std::optional<ddk::MmioBuffer> clk_mmio;
144     zx_status_t status = ddk::MmioBuffer::Create(kClkBaseAligned, kClkSizeAligned, *root_resource,
145                                                  ZX_CACHE_POLICY_UNCACHED_DEVICE, &clk_mmio);
146     if (status != ZX_OK) {
147         zxlogf(ERROR, "%s: Failed to set MSDC0 clock: %d\n", __FUNCTION__, status);
148         return status;
149     }
150 
151     std::optional<ddk::MmioBuffer> pll_mmio;
152     status = ddk::MmioBuffer::Create(kPllBaseAligned, kPllSizeAligned, *root_resource,
153                                      ZX_CACHE_POLICY_UNCACHED_DEVICE, &pll_mmio);
154     if (status != ZX_OK) {
155         zxlogf(ERROR, "%s: Failed to set MSDC0 clock: %d\n", __FUNCTION__, status);
156         return status;
157     }
158 
159     constexpr uint32_t div_value = MmPllCon1::kDiv4;
160     // The MSDC0 clock will be set to MMPLL/2, so shift an extra bit to get 400 MHz.
161     constexpr uint32_t src_clk_shift = 1 + MmPllCon1::kPcwFracBits + div_value;
162     constexpr uint64_t pcw =
163         (static_cast<uint64_t>(kSrcClkFreq) << src_clk_shift) / kMmPllSrcClkFreq;
164     MmPllCon1::Get()
165         .ReadFrom(&(*pll_mmio))
166         .set_change(1)
167         .set_div(div_value)
168         .set_pcw(pcw)
169         .WriteTo(&(*pll_mmio));
170 
171     ClkMuxSel0::Get()
172         .ReadFrom(&(*clk_mmio))
173         .set_msdc0_mux_sel(ClkMuxSel0::kClkMmPllDiv2)
174         .WriteTo(&(*clk_mmio));
175 
176     if ((status = pbus_.DeviceAdd(&emmc_dev)) != ZX_OK) {
177         zxlogf(ERROR, "%s: DeviceAdd MSDC0 failed: %d\n", __FUNCTION__, status);
178     }
179 
180     return status;
181 }
182 
183 } // namespace board_mt8167
184