1 // Copyright 2017 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 #pragma once
6 
7 #include <ddk/device.h>
8 #include <ddktl/device.h>
9 #include <ddktl/protocol/clk.h>
10 #include <ddktl/protocol/gpioimpl.h>
11 #include <ddktl/protocol/i2cimpl.h>
12 #include <ddktl/protocol/iommu.h>
13 #include <ddktl/protocol/platform/bus.h>
14 #include <fbl/array.h>
15 #include <fbl/intrusive_wavl_tree.h>
16 #include <fbl/mutex.h>
17 #include <fbl/unique_ptr.h>
18 #include <fbl/vector.h>
19 #include <lib/sync/completion.h>
20 #include <lib/zx/iommu.h>
21 #include <lib/zx/resource.h>
22 #include <lib/zx/vmo.h>
23 #include <stdint.h>
24 #include <threads.h>
25 #include <zircon/types.h>
26 
27 #include <optional>
28 
29 #include "platform-device.h"
30 #include "platform-protocol-device.h"
31 #include "platform-i2c.h"
32 #include "proxy-protocol.h"
33 
34 namespace platform_bus {
35 
36 class PlatformBus;
37 using PlatformBusType = ddk::Device<PlatformBus, ddk::GetProtocolable>;
38 
39 // This is the main class for the platform bus driver.
40 class PlatformBus : public PlatformBusType,
41                     public ddk::PBusProtocol<PlatformBus, ddk::base_protocol>,
42                     public ddk::IommuProtocol<PlatformBus> {
43 public:
44     static zx_status_t Create(zx_device_t* parent, const char* name, zx::vmo zbi);
45 
46     zx_status_t Proxy(
47          const void* req_buffer, size_t req_size, const zx_handle_t* req_handle_list,
48          size_t req_handle_count, void* out_resp_buffer, size_t resp_size, size_t* out_resp_actual,
49          zx_handle_t* out_resp_handle_list, size_t resp_handle_count,
50          size_t* out_resp_handle_actual);
51 
52     // Device protocol implementation.
53     zx_status_t DdkGetProtocol(uint32_t proto_id, void* out);
54     void DdkRelease();
55 
56     // Platform bus protocol implementation.
57     zx_status_t PBusDeviceAdd(const pbus_dev_t* dev);
58     zx_status_t PBusProtocolDeviceAdd(uint32_t proto_id, const pbus_dev_t* dev);
59     zx_status_t PBusRegisterProtocol(uint32_t proto_id, const void* protocol, size_t protocol_size,
60                                      const platform_proxy_cb_t* proxy_cb);
61     zx_status_t PBusGetBoardInfo(pdev_board_info_t* out_info);
62     zx_status_t PBusSetBoardInfo(const pbus_board_info_t* info);
63 
64     // IOMMU protocol implementation.
65     zx_status_t IommuGetBti(uint32_t iommu_index, uint32_t bti_id, zx::bti* out_bti);
66 
67     // Returns the resource handle to be used for creating MMIO regions, IRQs, and SMC ranges.
68     // Currently this just returns the root resource, but we may change this to a more
69     // limited resource in the future.
GetResource()70     zx::unowned_resource GetResource() const { return zx::unowned_resource(get_root_resource()); }
71 
72     // Used by PlatformDevice to queue I2C transactions on an I2C bus.
73     zx_status_t I2cTransact(uint32_t txid, rpc_i2c_req_t* req, const pbus_i2c_channel_t* channel,
74                             zx_handle_t channel_handle);
75 
76     zx_status_t GetZbiMetadata(uint32_t type, uint32_t extra, const void** out_metadata,
77                                uint32_t* out_size);
78 
79     // Protocol accessors for PlatformDevice.
clk()80     inline ddk::ClkProtocolClient* clk() { return &*clk_; }
gpio()81     inline ddk::GpioImplProtocolClient* gpio() { return &*gpio_; }
i2c()82     inline ddk::I2cImplProtocolClient* i2c() { return &*i2c_; }
83 
84 private:
85     // This class is a wrapper for a platform_proxy_cb_t added via pbus_register_protocol().
86     // It also is the element type for the proto_proxys_ WAVL tree.
87     class ProtoProxy : public fbl::WAVLTreeContainable<fbl::unique_ptr<ProtoProxy>> {
88     public:
ProtoProxy(uint32_t proto_id,const ddk::AnyProtocol * protocol,const platform_proxy_cb_t & proxy_cb)89         ProtoProxy(uint32_t proto_id, const ddk::AnyProtocol* protocol,
90                    const platform_proxy_cb_t& proxy_cb)
91             : proto_id_(proto_id), protocol_(*protocol), proxy_cb_(proxy_cb) {}
92 
GetKey()93         inline uint32_t GetKey() const { return proto_id_; }
GetProtocol(void * out)94         inline void GetProtocol(void* out) const { memcpy(out, &protocol_, sizeof(protocol_)); }
95 
Proxy(const void * req_buffer,size_t req_size,const zx_handle_t * req_handle_list,size_t req_handle_count,void * out_resp_buffer,size_t resp_size,size_t * out_resp_actual,zx_handle_t * out_resp_handle_list,size_t resp_handle_count,size_t * out_resp_handle_actual)96         inline void Proxy(const void* req_buffer, size_t req_size,
97                           const zx_handle_t* req_handle_list, size_t req_handle_count,
98                           void* out_resp_buffer, size_t resp_size, size_t* out_resp_actual,
99                           zx_handle_t* out_resp_handle_list, size_t resp_handle_count,
100                           size_t* out_resp_handle_actual) {
101             proxy_cb_.callback(proxy_cb_.ctx, req_buffer, req_size,
102                                req_handle_list, req_handle_count,
103                                out_resp_buffer, resp_size, out_resp_actual,
104                                out_resp_handle_list, resp_handle_count,
105                                out_resp_handle_actual);
106         }
107 
108     private:
109         const uint32_t proto_id_;
110         const ddk::AnyProtocol protocol_;
111         platform_proxy_cb_t proxy_cb_;
112     };
113 
114 
115     explicit PlatformBus(zx_device_t* parent);
116 
117     DISALLOW_COPY_ASSIGN_AND_MOVE(PlatformBus);
118 
119     zx_status_t Init(zx::vmo zbi);
120 
121     // Reads the platform ID and driver metadata records from the boot image.
122     zx_status_t ReadZbi(zx::vmo zbi);
123 
124     zx_status_t I2cInit(const i2c_impl_protocol_t* i2c);
125 
126     pdev_board_info_t board_info_;
127 
128     // Protocols that are optionally provided by the board driver.
129     std::optional<ddk::ClkProtocolClient> clk_;
130     std::optional<ddk::GpioImplProtocolClient> gpio_;
131     std::optional<ddk::IommuProtocolClient> iommu_;
132     std::optional<ddk::I2cImplProtocolClient> i2c_;
133 
134     // Completion used by WaitProtocol().
135     sync_completion_t proto_completion_ __TA_GUARDED(proto_completion_mutex_);
136     // Protects proto_completion_.
137     fbl::Mutex proto_completion_mutex_;
138 
139     // Metadata extracted from ZBI.
140     fbl::Array<uint8_t> metadata_;
141 
142     // List of I2C buses.
143     fbl::Vector<fbl::unique_ptr<PlatformI2cBus>> i2c_buses_;
144 
145     // Dummy IOMMU.
146     zx::iommu iommu_handle_;
147 
148     fbl::WAVLTree<uint32_t, fbl::unique_ptr<ProtoProxy>> proto_proxys_
149                                                 __TA_GUARDED(proto_proxys_mutex_);
150     // Protects proto_proxys_.
151     fbl::Mutex proto_proxys_mutex_;
152 };
153 
154 } // namespace platform_bus
155 
156 __BEGIN_CDECLS
157 zx_status_t platform_bus_create(void* ctx, zx_device_t* parent, const char* name,
158                                 const char* args, zx_handle_t rpc_channel);
159 __END_CDECLS
160