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 <fbl/mutex.h>
9#include <fbl/type_support.h>
10#include <fbl/unique_ptr.h>
11#include <stdint.h>
12#include <lib/zx/handle.h>
13#include <zircon/assert.h>
14#include <zircon/thread_annotations.h>
15#include <zircon/types.h>
16
17#include "tpm.h"
18
19namespace tpm {
20
21class I2cCr50Interface : public HardwareInterface {
22public:
23    // Creates a new I2cCr50Interface from the given |i2c_dev|.  This will issue an
24    // I2C transaction to determine support.
25    static zx_status_t Create(zx_device_t* i2c_dev, zx::handle irq,
26                              fbl::unique_ptr<I2cCr50Interface>* out);
27    virtual ~I2cCr50Interface();
28
29    zx_status_t Validate() override;
30
31    zx_status_t ReadAccess(Locality loc, uint8_t* access) override;
32    zx_status_t WriteAccess(Locality loc, uint8_t access) override;
33
34    zx_status_t ReadStatus(Locality loc, uint32_t* sts) override;
35    zx_status_t WriteStatus(Locality loc, uint32_t sts) override;
36
37    zx_status_t ReadDidVid(uint16_t* vid, uint16_t* did) override;
38
39    zx_status_t ReadDataFifo(Locality loc, uint8_t* buf, size_t len) override;
40    zx_status_t WriteDataFifo(Locality loc, const uint8_t* buf, size_t len) override;
41
42private:
43    template <typename T> struct I2cRegister {
44        explicit constexpr I2cRegister(uint8_t addr) : addr(addr) { }
45        const uint8_t addr;
46    };
47
48    // Timeout to use if this device does not have an IRQ wired up.
49    static constexpr zx::duration kNoIrqTimeout = zx::msec(20);
50    // Delay to use between retries if an I2C operation errors.
51    static constexpr zx::duration kI2cRetryDelay = zx::usec(50);
52
53    I2cCr50Interface(zx_device_t* i2c_dev, zx::handle irq);
54
55    // Block until the controller signals it is ready.  May return spuriously,
56    // so the condition being waited on should be checked after return.
57    zx_status_t WaitForIrqLocked() TA_REQ(lock_);
58
59    // Template for enforcing correct access size for each register read
60    template <typename T>
61    zx_status_t RegisterRead(const I2cRegister<T>& reg, T* out) {
62        // TODO(teisenbe): If we ever support a big-endian host, we need to do
63        // endianness swapping here.
64        static_assert(fbl::is_integral<T>::value, "T must be integral");
65        return RegisterRead(I2cRegister<uint8_t[]>(reg.addr), reinterpret_cast<uint8_t*>(out),
66                            sizeof(T));
67    }
68
69    // Template for enforcing correct access size for each register write
70    template <typename T>
71    zx_status_t RegisterWrite(const I2cRegister<T>& reg, const T& val) {
72        static_assert(fbl::is_integral<T>::value, "T must be integral");
73        return RegisterWrite(I2cRegister<uint8_t[]>(reg.addr),
74                             reinterpret_cast<const uint8_t*>(&val), sizeof(T));
75    }
76
77    // Perform an I2C read cycle
78    zx_status_t I2cReadLocked(uint8_t* val, size_t len) TA_REQ(lock_);
79    // Perform an I2C write cycle
80    zx_status_t I2cWriteLocked(const uint8_t* val, size_t len) TA_REQ(lock_);
81
82    // Perform a register read/write for an unsized register (indicated
83    // by T=uint8_t[]).
84    zx_status_t RegisterWrite(const I2cRegister<uint8_t[]>& reg,
85                              const uint8_t* val, size_t len) TA_EXCL(lock_);
86    zx_status_t RegisterRead(const I2cRegister<uint8_t[]>& reg,
87                             uint8_t* out, size_t len) TA_EXCL(lock_);
88
89    // Compute the register address prefix for the given locality
90    static constexpr uint8_t LocToPrefix(Locality loc) {
91        ZX_DEBUG_ASSERT(loc <= 4);
92        return static_cast<uint8_t>(loc << 4);
93    }
94
95    // These methods return an object usable with RegisterRead/RegisterWrite representing
96    // the specified register and locality.
97    static constexpr I2cRegister<uint8_t> RegisterAccess(Locality loc) {
98        return I2cRegister<uint8_t>(static_cast<uint8_t>(LocToPrefix(loc) | 0x0u));
99    }
100    static constexpr I2cRegister<uint32_t> RegisterStatus(Locality loc) {
101        ZX_DEBUG_ASSERT(loc <= 4);
102        return I2cRegister<uint32_t>(static_cast<uint8_t>(LocToPrefix(loc) | 0x1u));
103    }
104    static constexpr I2cRegister<uint8_t[]> RegisterDataFifo(Locality loc) {
105        ZX_DEBUG_ASSERT(loc <= 4);
106        return I2cRegister<uint8_t[]>(static_cast<uint8_t>(LocToPrefix(loc) | 0x5u));
107    }
108    static constexpr I2cRegister<uint32_t> RegisterDidVid(Locality loc) {
109        ZX_DEBUG_ASSERT(loc <= 4);
110        return I2cRegister<uint32_t>(static_cast<uint8_t>(LocToPrefix(loc) | 0x6u));
111    }
112
113    fbl::Mutex lock_;
114
115    // The upstream i2c device
116    zx_device_t* i2c_ TA_GUARDED(lock_) = nullptr;
117    zx::handle irq_ TA_GUARDED(lock_);
118};
119
120} // namespace tpm
121