| // Copyright 2019 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. |
| |
| #ifndef SRC_DEVICES_I2C_DRIVERS_DW_I2C_DW_I2C_H_ |
| #define SRC_DEVICES_I2C_DRIVERS_DW_I2C_DW_I2C_H_ |
| |
| #include <lib/device-protocol/pdev.h> |
| #include <lib/mmio/mmio.h> |
| #include <lib/zx/event.h> |
| #include <lib/zx/interrupt.h> |
| |
| #include <memory> |
| #include <thread> |
| |
| #include <ddktl/device.h> |
| #include <ddktl/protocol/i2cimpl.h> |
| #include <fbl/mutex.h> |
| #include <fbl/vector.h> |
| |
| #include "dw-i2c-regs.h" |
| |
| namespace dw_i2c { |
| |
| class DwI2c; |
| class DwI2cBus; |
| |
| using DeviceType = ddk::Device<DwI2c, ddk::Unbindable>; |
| |
| class DwI2c : public DeviceType, public ddk::I2cImplProtocol<DwI2c, ddk::base_protocol> { |
| public: |
| explicit DwI2c(zx_device_t* parent, fbl::Vector<std::unique_ptr<DwI2cBus>>&& bus_list) |
| : DeviceType(parent), buses_(std::move(bus_list)), bus_count_(buses_.size()) {} |
| ~DwI2c() = default; |
| |
| zx_status_t Bind(); |
| zx_status_t Init(); |
| static zx_status_t Create(void* ctx, zx_device_t* parent); |
| void ShutDown(); |
| |
| // Methods required by the ddk mixins. |
| void DdkUnbind(ddk::UnbindTxn txn); |
| void DdkRelease(); |
| |
| uint32_t I2cImplGetBusCount(); |
| zx_status_t I2cImplGetMaxTransferSize(uint32_t bus_id, size_t* out_size); |
| zx_status_t I2cImplSetBitrate(uint32_t bus_id, uint32_t bitrate); |
| zx_status_t I2cImplTransact(uint32_t bus_id, const i2c_impl_op_t* ops, size_t count); |
| |
| private: |
| int TestThread(); |
| |
| fbl::Vector<std::unique_ptr<DwI2cBus>> buses_; |
| size_t bus_count_ = 0; |
| }; |
| |
| class DwI2cBus { |
| public: |
| static constexpr uint32_t kDwCompTypeNum = 0x44570140; |
| // Local buffer for transfer and receive. Matches FIFO size. |
| uint32_t kMaxTransfer = 0; |
| |
| explicit DwI2cBus(ddk::MmioBuffer mmio, zx::interrupt& irq) |
| : mmio_(std::move(mmio)), irq_(std::move(irq)) {} |
| |
| DwI2cBus() = delete; |
| ~DwI2cBus() { ShutDown(); } |
| |
| zx_status_t Init(); |
| void ShutDown(); |
| zx_status_t Transact(const i2c_impl_op_t* rws, size_t count); |
| |
| private: |
| static constexpr uint32_t kErrorSignal = ZX_USER_SIGNAL_0; |
| static constexpr uint32_t kTransactionCompleteSignal = ZX_USER_SIGNAL_1; |
| static constexpr uint32_t kI2cDisable = 0; |
| static constexpr uint32_t kI2cEnable = 1; |
| static constexpr uint32_t kStandardMode = 1; |
| static constexpr uint32_t kFastMode = 2; |
| static constexpr uint32_t kHighSpeedMode = 3; |
| static constexpr uint32_t k7BitAddr = 0; |
| static constexpr uint16_t k7BitAddrMask = 0x7f; |
| static constexpr uint32_t k10BitAddr = 0; |
| static constexpr uint32_t kActive = 1; |
| static constexpr uint32_t kMaxPoll = 100; |
| static constexpr uint32_t kPollSleep = ZX_USEC(25); |
| static constexpr uint32_t kDefaultTimeout = ZX_MSEC(100); |
| uint32_t kI2cInterruptReadMask = InterruptMaskReg::Get() |
| .FromValue(0) |
| .set_rx_full(1) |
| .set_tx_abrt(1) |
| .set_stop_det(1) |
| .reg_value(); |
| uint32_t kI2cInterruptDefaultMask = InterruptMaskReg::Get() |
| .FromValue(0) |
| .set_rx_full(1) |
| .set_tx_abrt(1) |
| .set_stop_det(1) |
| .set_tx_empty(1) |
| .reg_value(); |
| |
| /* I2C timing parameters */ |
| static constexpr uint32_t kClkRateKHz = 100000; |
| static constexpr uint32_t kSclTFalling = 205; |
| static constexpr uint32_t kSdaTFalling = 425; |
| static constexpr uint32_t kSdaTHold = 449; |
| /* Standard speed parameters */ |
| static constexpr uint32_t kSclStandardSpeedTHold = 4000; // SCL hold time for start signal in ns |
| static constexpr uint32_t kSclStandardSpeedTLow = 4700; // SCL low time in ns |
| /* Fast speed parameters */ |
| static constexpr uint32_t kSclFastSpeedTHold = 600; // SCL hold time for start signal in ns |
| static constexpr uint32_t kSclFastSpeedTLow = 1300; // SCL low time in ns |
| |
| // IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) |
| static constexpr uint32_t kSclStandardSpeedHcnt = |
| ((kClkRateKHz * (kSclStandardSpeedTHold + kSdaTFalling)) + 500000) / 1000000 - 3; |
| static constexpr uint32_t kSclFastSpeedHcnt = |
| ((kClkRateKHz * (kSclFastSpeedTHold + kSdaTFalling)) + 500000) / 1000000 - 3; |
| |
| // IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) |
| static constexpr uint32_t kSclStandardSpeedLcnt = |
| ((kClkRateKHz * (kSclStandardSpeedTLow + kSclTFalling)) + 500000) / 1000000 - 1; |
| static constexpr uint32_t kSclFastSpeedLcnt = |
| ((kClkRateKHz * (kSclFastSpeedTLow + kSclTFalling)) + 500000) / 1000000 - 1; |
| |
| // IC_SDA_HOLD = (IC_CLK * tSDA;Hold + 500000 / 1000000) |
| static constexpr uint32_t kSdaHoldValue = ((kClkRateKHz * kSdaTHold) + 500000) / 1000000; |
| |
| zx_status_t HostInit(); |
| zx_status_t Receive() __TA_REQUIRES(ops_lock_); |
| zx_status_t Transmit() __TA_REQUIRES(ops_lock_); |
| zx_status_t SetSlaveAddress(uint16_t addr); |
| zx_status_t Dumpstate(); |
| zx_status_t EnableAndWait(bool enable); |
| zx_status_t Enable(); |
| void ClearInterrupts(); |
| void DisableInterrupts(); |
| void EnableInterrupts(uint32_t flag); |
| zx_status_t Disable(); |
| zx_status_t WaitEvent(uint32_t sig_mask); |
| InterruptStatusReg ReadAndClearIrq(); |
| int IrqThread(); |
| zx_status_t WaitBusBusy(); |
| void SetOpsHelper(const i2c_impl_op_t* ops, size_t count); |
| |
| ddk::MmioBuffer mmio_; |
| zx::interrupt irq_; |
| zx::event event_; |
| zx::duration timeout_; |
| std::thread irq_thread_; |
| uint32_t tx_fifo_depth_ = 0; |
| uint32_t rx_fifo_depth_ = 0; |
| |
| fbl::Mutex transact_lock_; /* used to serialize transactions */ |
| fbl::Mutex ops_lock_; /* used to set ops for irq thread */ |
| |
| const i2c_impl_op_t* ops_ __TA_GUARDED(ops_lock_) = nullptr; |
| size_t ops_count_ __TA_GUARDED(ops_lock_) = 0; |
| uint32_t rx_op_idx_ __TA_GUARDED(ops_lock_) = 0; |
| uint32_t tx_op_idx_ __TA_GUARDED(ops_lock_) = 0; |
| uint32_t rx_done_len_ __TA_GUARDED(ops_lock_) = 0; |
| uint32_t tx_done_len_ __TA_GUARDED(ops_lock_) = 0; |
| uint32_t rx_pending_ __TA_GUARDED(ops_lock_) = 0; |
| bool send_restart_ __TA_GUARDED(ops_lock_) = false; |
| }; |
| |
| } // namespace dw_i2c |
| |
| #endif // SRC_DEVICES_I2C_DRIVERS_DW_I2C_DW_I2C_H_ |