checkpoint separate i2c driver

Change-Id: I2a33806ecd65a165b458c82ece0d36b4bb967cc3
diff --git a/system/dev/board/aml-s905d2/aml-i2c.c b/system/dev/board/aml-s905d2/aml-i2c.c
index 7967fa9..790134d 100644
--- a/system/dev/board/aml-s905d2/aml-i2c.c
+++ b/system/dev/board/aml-s905d2/aml-i2c.c
@@ -84,7 +84,7 @@
         return status;
     }
 
-    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C_IMPL);
+    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C);
     if (status != ZX_OK) {
         zxlogf(ERROR, "aml_i2c_init: pbus_wait_protocol failed: %d\n", status);
         return status;
diff --git a/system/dev/board/astro/astro-i2c.c b/system/dev/board/astro/astro-i2c.c
index ade999f..ba7d07f 100644
--- a/system/dev/board/astro/astro-i2c.c
+++ b/system/dev/board/astro/astro-i2c.c
@@ -72,7 +72,7 @@
         return status;
     }
 
-    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C_IMPL);
+    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C);
     if (status != ZX_OK) {
         zxlogf(ERROR, "aml_i2c_init: pbus_wait_protocol failed: %d\n", status);
         return status;
diff --git a/system/dev/board/gauss/gauss-clk.c b/system/dev/board/gauss/gauss-clk.c
index 30cc885..3a8ae27 100644
--- a/system/dev/board/gauss/gauss-clk.c
+++ b/system/dev/board/gauss/gauss-clk.c
@@ -29,7 +29,7 @@
 };
 
 zx_status_t gauss_clk_init(gauss_bus_t* bus) {
-    zxlogf(INFO, "gauss_clk_init");
+    zxlogf(INFO, "gauss_clk_init\n");
     zx_status_t st;
 
     st = pbus_device_add(&bus->pbus, &clk_dev, PDEV_ADD_PBUS_DEVHOST);
diff --git a/system/dev/board/gauss/gauss-i2c.c b/system/dev/board/gauss/gauss-i2c.c
index 125be06..a307373 100644
--- a/system/dev/board/gauss/gauss-i2c.c
+++ b/system/dev/board/gauss/gauss-i2c.c
@@ -77,7 +77,7 @@
         return status;
     }
 
-    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C_IMPL);
+    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C);
     if (status != ZX_OK) {
         zxlogf(ERROR, "gauss_i2c_init: pbus_wait_protocol failed: %d\n", status);
         return status;
diff --git a/system/dev/board/hikey960/hikey960-i2c.c b/system/dev/board/hikey960/hikey960-i2c.c
index 55dd60f..1c19c14 100644
--- a/system/dev/board/hikey960/hikey960-i2c.c
+++ b/system/dev/board/hikey960/hikey960-i2c.c
@@ -59,7 +59,7 @@
         return status;
     }
 
-    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C_IMPL);
+    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C);
     if (status != ZX_OK) {
         zxlogf(ERROR, "hikey960_i2c_init: pbus_wait_protocol failed: %d\n", status);
         return status;
diff --git a/system/dev/board/vim/vim-i2c.c b/system/dev/board/vim/vim-i2c.c
index cce4c90..0fa6d65 100644
--- a/system/dev/board/vim/vim-i2c.c
+++ b/system/dev/board/vim/vim-i2c.c
@@ -81,7 +81,7 @@
         return status;
     }
 
-    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C_IMPL);
+    status = pbus_wait_protocol(&bus->pbus, ZX_PROTOCOL_I2C);
     if (status != ZX_OK) {
         zxlogf(ERROR, "vim_i2c_init: pbus_wait_protocol failed: %d\n", status);
         return status;
diff --git a/system/dev/bus/platform/platform-bus.cpp b/system/dev/bus/platform/platform-bus.cpp
index 55c88cd..3d25473 100644
--- a/system/dev/bus/platform/platform-bus.cpp
+++ b/system/dev/bus/platform/platform-bus.cpp
@@ -55,14 +55,8 @@
         }
         break;
     }
-    case ZX_PROTOCOL_I2C_IMPL: {
-        auto proto = static_cast<i2c_impl_protocol_t*>(protocol);
-        auto status = I2cInit(proto);
-        if (status != ZX_OK) {
-            return status;
-         }
-
-        i2c_impl_.reset(new (&ac) ddk::I2cImplProtocolProxy(proto));
+    case ZX_PROTOCOL_I2C: {
+        i2c_.reset(new (&ac) ddk::I2cProtocolProxy(static_cast<i2c_protocol_t*>(protocol)));
         if (!ac.check()) {
             return ZX_ERR_NO_MEMORY;
         }
@@ -217,9 +211,9 @@
             return ZX_OK;
         }
         break;
-    case ZX_PROTOCOL_I2C_IMPL:
-        if (i2c_impl_ != nullptr) {
-            i2c_impl_->GetProto(static_cast<i2c_impl_protocol_t*>(protocol));
+    case ZX_PROTOCOL_I2C:
+        if (i2c_ != nullptr) {
+            i2c_->GetProto(static_cast<i2c_protocol_t*>(protocol));
             return ZX_OK;
         }
         break;
@@ -362,49 +356,6 @@
     return ZX_OK;
 }
 
-zx_status_t PlatformBus::I2cInit(i2c_impl_protocol_t* i2c) {
-    if (!i2c_buses_.is_empty()) {
-        // already initialized
-        return ZX_ERR_BAD_STATE;
-    }
-
-    uint32_t bus_count = i2c_impl_get_bus_count(i2c);
-    if (!bus_count) {
-        return ZX_ERR_NOT_SUPPORTED;
-    }
-
-    fbl::AllocChecker ac;
-    i2c_buses_.reserve(bus_count, &ac);
-    if (!ac.check()) {
-        return ZX_ERR_NO_MEMORY;
-    }
-
-    for (uint32_t i = 0; i < bus_count; i++) {
-        fbl::unique_ptr<PlatformI2cBus> i2c_bus(new (&ac) PlatformI2cBus(i2c, i));
-        if (!ac.check()) {
-            return ZX_ERR_NO_MEMORY;
-        }
-
-        auto status = i2c_bus->Start();
-        if (status != ZX_OK) {
-            return status;
-        }
-
-        i2c_buses_.push_back(fbl::move(i2c_bus));
-    }
-
-    return ZX_OK;
-}
-
-zx_status_t PlatformBus::I2cTransact(uint32_t txid, rpc_i2c_req_t* req, pbus_i2c_channel_t* channel,
-                                     const void* write_buf, zx_handle_t channel_handle) {
-    if (channel->bus_id >= i2c_buses_.size()) {
-        return ZX_ERR_OUT_OF_RANGE;
-    }
-    auto i2c_bus = i2c_buses_[channel->bus_id].get();
-    return i2c_bus->Transact(txid, req, channel->address, write_buf, channel_handle);
-}
-
 void PlatformBus::DdkRelease() {
     delete this;
 }
diff --git a/system/dev/bus/platform/platform-bus.h b/system/dev/bus/platform/platform-bus.h
index 0e88992..152f49b 100644
--- a/system/dev/bus/platform/platform-bus.h
+++ b/system/dev/bus/platform/platform-bus.h
@@ -11,7 +11,7 @@
 #include <ddktl/protocol/canvas.h>
 #include <ddktl/protocol/clk.h>
 #include <ddktl/protocol/gpio.h>
-#include <ddktl/protocol/i2c-impl.h>
+#include <ddktl/protocol/i2c.h>
 #include <ddktl/protocol/iommu.h>
 #include <ddktl/protocol/mailbox.h>
 #include <ddktl/protocol/platform-bus.h>
@@ -27,7 +27,6 @@
 #include <zircon/types.h>
 
 #include "platform-device.h"
-#include "platform-i2c.h"
 #include "proxy-protocol.h"
 
 namespace platform_bus {
@@ -61,10 +60,6 @@
     // limited resource in the future.
     zx_handle_t GetResource() const { return get_root_resource(); }
 
-    // Used by PlatformDevice to queue I2C transactions on an I2C bus.
-    zx_status_t I2cTransact(uint32_t txid, rpc_i2c_req_t* req, pbus_i2c_channel_t* channel,
-                            const void* write_buf, zx_handle_t channel_handle);
-
     // Helper for PlatformDevice.
     zx_status_t GetBoardInfo(pdev_board_info_t* out_info);
 
@@ -72,7 +67,7 @@
     inline ddk::CanvasProtocolProxy* canvas() const { return canvas_.get(); }
     inline ddk::ClkProtocolProxy* clk() const { return clk_.get(); }
     inline ddk::GpioProtocolProxy* gpio() const { return gpio_.get(); }
-    inline ddk::I2cImplProtocolProxy* i2c_impl() const { return i2c_impl_.get(); }
+    inline ddk::I2cProtocolProxy* i2c() const { return i2c_.get(); }
     inline ddk::ScpiProtocolProxy* scpi() const { return scpi_.get(); }
     inline ddk::UmsProtocolProxy* ums() const { return ums_.get(); }
     inline const uint8_t* metadata() const { return metadata_.get(); }
@@ -88,8 +83,6 @@
     // Reads the platform ID and driver metadata records from the boot image.
     zx_status_t ReadZbi(zx::vmo zbi);
 
-    zx_status_t I2cInit(i2c_impl_protocol_t* i2c);
-
     pdev_board_info_t board_info_;
 
     // Protocols that are optionally provided by the board driver.
@@ -97,7 +90,7 @@
     fbl::unique_ptr<ddk::ClkProtocolProxy> clk_;
     fbl::unique_ptr<ddk::GpioProtocolProxy> gpio_;
     fbl::unique_ptr<ddk::IommuProtocolProxy> iommu_;
-    fbl::unique_ptr<ddk::I2cImplProtocolProxy> i2c_impl_;
+    fbl::unique_ptr<ddk::I2cProtocolProxy> i2c_;
     fbl::unique_ptr<ddk::ScpiProtocolProxy> scpi_;
     fbl::unique_ptr<ddk::MailboxProtocolProxy> mailbox_;
     fbl::unique_ptr<ddk::UmsProtocolProxy> ums_;
@@ -112,8 +105,6 @@
 
     // List of platform devices.
     fbl::Vector<fbl::unique_ptr<PlatformDevice>> devices_;
-    // List of I2C buses.
-    fbl::Vector<fbl::unique_ptr<PlatformI2cBus>> i2c_buses_;
 
     // Dummy IOMMU.
     zx::handle iommu_handle_;
diff --git a/system/dev/bus/platform/platform-device.cpp b/system/dev/bus/platform/platform-device.cpp
index a01f11c..5762bbd 100644
--- a/system/dev/bus/platform/platform-device.cpp
+++ b/system/dev/bus/platform/platform-device.cpp
@@ -417,20 +417,21 @@
 
 zx_status_t PlatformDevice::RpcI2cTransact(uint32_t txid, rpc_i2c_req_t* req, uint8_t* data,
                                            zx_handle_t channel) {
-    if (bus_->i2c_impl() == nullptr) {
+    if (bus_->i2c() == nullptr) {
         return ZX_ERR_NOT_SUPPORTED;
     }
     uint32_t index = req->index;
     if (index >= i2c_channels_.size()) {
         return ZX_ERR_OUT_OF_RANGE;
     }
-    pbus_i2c_channel_t* pdev_channel = &i2c_channels_[index];
+//    pbus_i2c_channel_t* pdev_channel = &i2c_channels_[index];
 
-    return bus_->I2cTransact(txid, req, pdev_channel, data, channel);
+//FIXME    return bus_->i2c()->Transact(pdev_channel->bus_id, out_size);
+return 0;
 }
 
 zx_status_t PlatformDevice::RpcI2cGetMaxTransferSize(uint32_t index, size_t* out_size) {
-    if (bus_->i2c_impl() == nullptr) {
+    if (bus_->i2c() == nullptr) {
         return ZX_ERR_NOT_SUPPORTED;
     }
     if (index >= i2c_channels_.size()) {
@@ -438,7 +439,7 @@
     }
     pbus_i2c_channel_t* pdev_channel = &i2c_channels_[index];
 
-    return bus_->i2c_impl()->GetMaxTransferSize(pdev_channel->bus_id, out_size);
+    return bus_->i2c()->GetMaxTransferSize(pdev_channel->bus_id, out_size);
 }
 
 zx_status_t PlatformDevice::RpcClkEnable(uint32_t index) {
diff --git a/system/dev/bus/platform/rules.mk b/system/dev/bus/platform/rules.mk
index 7aaffbe..4a13c23 100644
--- a/system/dev/bus/platform/rules.mk
+++ b/system/dev/bus/platform/rules.mk
@@ -14,7 +14,6 @@
     $(LOCAL_DIR)/platform-bus.cpp \
     $(LOCAL_DIR)/platform-bus-bind.c \
     $(LOCAL_DIR)/platform-device.cpp \
-    $(LOCAL_DIR)/platform-i2c.cpp \
 
 MODULE_STATIC_LIBS := \
     system/ulib/ddk \
diff --git a/system/dev/i2c/aml-i2c/aml-i2c.c b/system/dev/i2c/aml-i2c/aml-i2c.c
index 1861e69..c9751ea 100644
--- a/system/dev/i2c/aml-i2c/aml-i2c.c
+++ b/system/dev/i2c/aml-i2c/aml-i2c.c
@@ -12,7 +12,6 @@
 #include <ddk/debug.h>
 #include <ddk/device.h>
 #include <ddk/protocol/i2c-impl.h>
-#include <ddk/protocol/platform-bus.h>
 #include <ddk/protocol/platform-defs.h>
 #include <ddk/protocol/platform-device.h>
 #include <hw/reg.h>
@@ -61,7 +60,6 @@
 
 typedef struct {
     platform_device_protocol_t pdev;
-    i2c_impl_protocol_t i2c;
     zx_device_t* zxdev;
     aml_i2c_dev_t* i2c_devs;
     size_t dev_count;
@@ -361,12 +359,6 @@
         goto fail;
     }
 
-    platform_bus_protocol_t pbus;
-    if ((status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_BUS, &pbus)) != ZX_OK) {
-        zxlogf(ERROR, "aml_i2c_bind: ZX_PROTOCOL_PLATFORM_BUS not available\n");
-        goto fail;
-    }
-
     pdev_device_info_t info;
     status = pdev_get_device_info(&i2c->pdev, &info);
     if (status != ZX_OK) {
@@ -399,7 +391,8 @@
         .name = "aml-i2c",
         .ctx = i2c,
         .ops = &i2c_device_proto,
-        .flags = DEVICE_ADD_NON_BINDABLE,
+        .proto_id = ZX_PROTOCOL_I2C_IMPL,
+        .proto_ops = &i2c_ops,
     };
 
     status = device_add(parent, &args, &i2c->zxdev);
@@ -408,10 +401,6 @@
         goto fail;
     }
 
-    i2c->i2c.ops = &i2c_ops;
-    i2c->i2c.ctx = i2c;
-    pbus_set_protocol(&pbus, ZX_PROTOCOL_I2C_IMPL, &i2c->i2c);
-
     return ZX_OK;
 
 fail:
diff --git a/system/dev/i2c/dw-i2c/dw-i2c.c b/system/dev/i2c/dw-i2c/dw-i2c.c
index 4175f2a..5ae43e9 100644
--- a/system/dev/i2c/dw-i2c/dw-i2c.c
+++ b/system/dev/i2c/dw-i2c/dw-i2c.c
@@ -8,7 +8,6 @@
 #include <ddk/io-buffer.h>
 #include <ddk/protocol/i2c-impl.h>
 #include <ddk/protocol/platform-defs.h>
-#include <ddk/protocol/platform-bus.h>
 #include <ddk/protocol/platform-device.h>
 #include <hw/reg.h>
 #include <lib/sync/completion.h>
@@ -36,7 +35,6 @@
 
 typedef struct {
     platform_device_protocol_t pdev;
-    i2c_impl_protocol_t i2c;
     zx_device_t* zxdev;
     i2c_dw_dev_t* i2c_devs;
     size_t i2c_dev_count;
@@ -440,12 +438,6 @@
         goto fail;
     }
 
-    platform_bus_protocol_t pbus;
-    if ((status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_BUS, &pbus)) != ZX_OK) {
-        zxlogf(ERROR, "dw_i2c_bind: ZX_PROTOCOL_PLATFORM_BUS not available\n");
-        goto fail;
-    }
-
     pdev_device_info_t info;
     status = pdev_get_device_info(&i2c->pdev, &info);
     if (status != ZX_OK) {
@@ -480,7 +472,8 @@
         .name = "dw-i2c",
         .ctx = i2c,
         .ops = &i2c_device_proto,
-        .flags = DEVICE_ADD_NON_BINDABLE,
+        .proto_id = ZX_PROTOCOL_I2C_IMPL,
+        .proto_ops = &i2c_ops,
     };
 
     status = device_add(parent, &args, &i2c->zxdev);
@@ -489,10 +482,6 @@
         goto fail;
     }
 
-    i2c->i2c.ops = &i2c_ops;
-    i2c->i2c.ctx = i2c;
-    pbus_set_protocol(&pbus, ZX_PROTOCOL_I2C_IMPL, &i2c->i2c);
-
     return ZX_OK;
 
 fail:
diff --git a/system/dev/i2c/i2c/binding.c b/system/dev/i2c/i2c/binding.c
new file mode 100644
index 0000000..8fbbfb4
--- /dev/null
+++ b/system/dev/i2c/i2c/binding.c
@@ -0,0 +1,19 @@
+// 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/binding.h>
+#include <ddk/device.h>
+#include <ddk/driver.h>
+#include <ddk/protocol/platform-defs.h>
+
+extern zx_status_t i2c_bind(void* ctx, zx_device_t* parent);
+
+static zx_driver_ops_t i2c_driver_ops = {
+    .version = DRIVER_OPS_VERSION,
+    .bind = i2c_bind,
+};
+
+ZIRCON_DRIVER_BEGIN(i2c, i2c_driver_ops, "zircon", "0.1", 1)
+    BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_I2C_IMPL),
+ZIRCON_DRIVER_END(i2c)
diff --git a/system/dev/bus/platform/platform-i2c.cpp b/system/dev/i2c/i2c/i2c-bus.cpp
similarity index 71%
rename from system/dev/bus/platform/platform-i2c.cpp
rename to system/dev/i2c/i2c/i2c-bus.cpp
index a047ce4..f5bf571 100644
--- a/system/dev/bus/platform/platform-i2c.cpp
+++ b/system/dev/i2c/i2c/i2c-bus.cpp
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "platform-i2c.h"
+#include "i2c-bus.h"
 
 #include <stdlib.h>
 #include <threads.h>
@@ -12,35 +12,32 @@
 #include <zircon/listnode.h>
 #include <zircon/threads.h>
 
-namespace platform_bus {
+namespace i2c {
 
-PlatformI2cBus::PlatformI2cBus(i2c_impl_protocol_t* i2c, uint32_t bus_id)
-    : i2c_(i2c), bus_id_(bus_id) {
-
+I2cBus::I2cBus(ddk::I2cImplProtocolProxy i2c_impl, uint32_t bus_id)
+    : i2c_impl_(i2c_impl), bus_id_(bus_id) {
     list_initialize(&queued_txns_);
     list_initialize(&free_txns_);
     sync_completion_reset(&txn_signal_);
 }
 
-zx_status_t PlatformI2cBus::Start() {
-    auto status = i2c_.GetMaxTransferSize(bus_id_, &max_transfer_);
+zx_status_t I2cBus::Start() {
+    auto status = i2c_impl_.GetMaxTransferSize(bus_id_, &max_transfer_size_);
     if (status != ZX_OK) {
         return status;
     }
-    if (max_transfer_ > I2C_MAX_TRANSFER_SIZE) {
-        max_transfer_ = I2C_MAX_TRANSFER_SIZE;
-    }
 
     char name[32];
-    snprintf(name, sizeof(name), "PlatformI2cBus[%u]", bus_id_);
-    auto thunk = [](void* arg) -> int { return static_cast<PlatformI2cBus*>(arg)->I2cThread(); };
+    snprintf(name, sizeof(name), "I2cBus[%u]", bus_id_);
+    auto thunk = [](void* arg) -> int { return static_cast<I2cBus*>(arg)->I2cThread(); };
     thrd_create_with_name(&thread_, thunk, this, name);
 
     return ZX_OK;
 }
 
-void PlatformI2cBus::Complete(I2cTxn* txn, zx_status_t status, const uint8_t* data,
+void I2cBus::Complete(I2cTxn* txn, zx_status_t status, const uint8_t* data,
                                  size_t data_length) {
+/*
     struct {
         rpc_i2c_rsp_t i2c;
         uint8_t data[I2C_MAX_TRANSFER_SIZE] = {};
@@ -65,11 +62,12 @@
     if (status != ZX_OK) {
         zxlogf(ERROR, "platform_i2c_read_complete: zx_channel_write failed %d\n", status);
     }
+*/
 }
 
-int PlatformI2cBus::I2cThread() {
+int I2cBus::I2cThread() {
     fbl::AllocChecker ac;
-    fbl::unique_ptr<uint8_t> read_buffer(new (&ac) uint8_t[max_transfer_]);
+    fbl::unique_ptr<uint8_t> read_buffer(new (&ac) uint8_t[max_transfer_size_]);
     if (!ac.check()) {
         zxlogf(ERROR, "%s could not allocate read_buffer\n", __FUNCTION__);
         return 0;
@@ -85,8 +83,8 @@
         while ((txn = list_remove_head_type(&queued_txns_, I2cTxn, node)) != nullptr) {
             mutex_.Release();
 
-            auto status = i2c_.Transact(bus_id_, txn->address,  txn->write_buffer,
-                                        txn->write_length, read_buffer.get(), txn->read_length);
+            auto status = i2c_impl_.Transact(bus_id_, txn->address,  txn->write_buffer,
+                                             txn->write_length, read_buffer.get(), txn->read_length);
             size_t actual = (status == ZX_OK ? txn->read_length : 0);
             Complete(txn, status, read_buffer.get(), actual);
 
@@ -98,11 +96,12 @@
     return 0;
 }
 
- zx_status_t PlatformI2cBus::Transact(uint32_t txid, rpc_i2c_req_t* req, uint16_t address,
-                                      const void* write_buf, zx_handle_t channel_handle) {
+zx_status_t I2cBus::Transact(const void* write_buf, size_t write_length, size_t read_length,
+                             i2c_complete_cb complete_cb, void* cookie) {
+/*
     const size_t write_length = req->write_length;
     const size_t read_length = req->read_length;
-    if (write_length > max_transfer_ || read_length > max_transfer_) {
+    if (write_length > max_transfer_size_ || read_length > max_transfer_size_) {
         return ZX_ERR_INVALID_ARGS;
     }
 
@@ -111,7 +110,7 @@
     I2cTxn* txn = list_remove_head_type(&free_txns_, I2cTxn, node);
     if (!txn) {
         // add space for write buffer
-        txn = static_cast<I2cTxn*>(calloc(1, sizeof(I2cTxn) + max_transfer_));
+        txn = static_cast<I2cTxn*>(calloc(1, sizeof(I2cTxn) + max_transfer_size_));
     }
     if (!txn) {
         return ZX_ERR_NO_MEMORY;
@@ -130,6 +129,8 @@
     sync_completion_signal(&txn_signal_);
 
     return ZX_OK;
+*/
+return -1;
 }
 
-} // namespace platform_bus
+} // namespace i2c
diff --git a/system/dev/bus/platform/platform-i2c.h b/system/dev/i2c/i2c/i2c-bus.h
similarity index 66%
rename from system/dev/bus/platform/platform-i2c.h
rename to system/dev/i2c/i2c/i2c-bus.h
index c24b5de..e734100 100644
--- a/system/dev/bus/platform/platform-i2c.h
+++ b/system/dev/i2c/i2c/i2c-bus.h
@@ -6,20 +6,23 @@
 
 #include <threads.h>
 
+#include <ddk/protocol/i2c.h>
 #include <ddktl/protocol/i2c-impl.h>
 #include <fbl/mutex.h>
+#include <zircon/listnode.h>
 
-#include "proxy-protocol.h"
+namespace i2c {
 
-namespace platform_bus {
-
-class PlatformI2cBus {
+class I2cBus {
 public:
-    explicit PlatformI2cBus(i2c_impl_protocol_t* i2c, uint32_t bus_id);
+    explicit I2cBus(ddk::I2cImplProtocolProxy i2c_impl, uint32_t bus_id);
     zx_status_t Start();
 
-    zx_status_t Transact(uint32_t txid, rpc_i2c_req_t* req, uint16_t address, const void* write_buf,
-                         zx_handle_t channel_handle);
+    zx_status_t Transact(const void* write_buf, size_t write_length, size_t read_length,
+                         i2c_complete_cb complete_cb, void* cookie);
+
+    inline size_t GetMaxTransferSize() const { return max_transfer_size_; }
+
 private:
     // struct representing an I2C transaction.
     struct I2cTxn {
@@ -39,9 +42,9 @@
                   size_t data_length);
     int I2cThread();
 
-    ddk::I2cImplProtocolProxy i2c_;
+    ddk::I2cImplProtocolProxy i2c_impl_;
     const uint32_t bus_id_;
-    size_t max_transfer_;
+    size_t max_transfer_size_;
 
     list_node_t queued_txns_ __TA_GUARDED(mutex_);
     list_node_t free_txns_ __TA_GUARDED(mutex_);
@@ -51,4 +54,4 @@
     fbl::Mutex mutex_;
 };
 
-} // namespace platform_bus
+} // namespace i2c
diff --git a/system/dev/i2c/i2c/i2c.cpp b/system/dev/i2c/i2c/i2c.cpp
new file mode 100644
index 0000000..5f018ca
--- /dev/null
+++ b/system/dev/i2c/i2c/i2c.cpp
@@ -0,0 +1,114 @@
+// 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 "i2c.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <ddk/debug.h>
+#include <ddk/device.h>
+#include <ddk/protocol/i2c-impl.h>
+#include <ddk/protocol/platform-bus.h>
+#include <ddktl/device.h>
+#include <fbl/alloc_checker.h>
+
+#include "i2c-bus.h"
+
+namespace i2c {
+
+zx_status_t I2cDevice::Create(zx_device_t* parent) {
+    i2c_impl_protocol_t proto;
+
+    auto status = device_get_protocol(parent, ZX_PROTOCOL_I2C_IMPL, &proto);
+    if (status != ZX_OK) {
+        zxlogf(ERROR, "%s: ZX_PROTOCOL_I2C_IMPL not available\n", __func__);
+        return status;
+    }
+
+    fbl::AllocChecker ac;
+    fbl::unique_ptr<i2c::I2cDevice> i2c(new (&ac) I2cDevice(parent, &proto));
+    if (!ac.check()) {
+        return ZX_ERR_NO_MEMORY;
+    }
+
+    status = i2c->Init();
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    // devmgr is now in charge of the device.
+    __UNUSED auto* dummy = i2c.release();
+    return ZX_OK;
+}
+
+zx_status_t I2cDevice::Init() {
+    uint32_t bus_count = i2c_impl_.GetBusCount();
+    if (!bus_count) {
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+
+    fbl::AllocChecker ac;
+    i2c_buses_.reserve(bus_count, &ac);
+    if (!ac.check()) {
+        return ZX_ERR_NO_MEMORY;
+    }
+
+    for (uint32_t i = 0; i < bus_count; i++) {
+        fbl::unique_ptr<I2cBus> i2c_bus(new (&ac) I2cBus(i2c_impl_, i));
+        if (!ac.check()) {
+            return ZX_ERR_NO_MEMORY;
+        }
+
+        auto status = i2c_bus->Start();
+        if (status != ZX_OK) {
+            return status;
+        }
+
+        i2c_buses_.push_back(fbl::move(i2c_bus));
+    }
+
+    // If our grand parent supports ZX_PROTOCOL_PLATFORM_BUS, then we need to register our protocol
+    // with the platform bus.
+    zx_device_t* grand_parent = device_get_parent(parent());
+    if (grand_parent) {
+        platform_bus_protocol_t pbus;
+        if (device_get_protocol(grand_parent, ZX_PROTOCOL_PLATFORM_BUS, &pbus) == ZX_OK) {
+            i2c_protocol_t i2c;
+            i2c.ctx = this;
+            i2c.ops = &i2c_proto_ops_;
+            pbus_set_protocol(&pbus, ZX_PROTOCOL_I2C, &i2c);
+
+            // If we are implementing our protocol for the platform bus, we should not be bindable.
+            return DdkAdd("i2c", DEVICE_ADD_NON_BINDABLE);
+        }
+    }
+
+    // Otherwise, publish a normal device.
+    return DdkAdd("i2c");
+}
+
+zx_status_t I2cDevice::I2cTransact(uint32_t index, const void* write_buf, size_t write_length,
+                                   size_t read_length, i2c_complete_cb complete_cb, void* cookie) {
+    if (index >= i2c_buses_.size()) {
+        return ZX_ERR_OUT_OF_RANGE;
+    }
+
+    return i2c_buses_[index]->Transact(write_buf, write_length, read_length, complete_cb, cookie);
+}
+
+zx_status_t I2cDevice::I2cGetMaxTransferSize(uint32_t index, size_t* out_size) {
+    if (index >= i2c_buses_.size()) {
+        return ZX_ERR_OUT_OF_RANGE;
+    }
+
+    *out_size = i2c_buses_[index]->GetMaxTransferSize();
+    return ZX_OK;
+}
+
+} // namespace i2c
+
+extern "C" zx_status_t i2c_bind(void* ctx, zx_device_t* parent) {
+    return i2c::I2cDevice::Create(parent);
+}
diff --git a/system/dev/i2c/i2c/i2c.h b/system/dev/i2c/i2c/i2c.h
new file mode 100644
index 0000000..3811587
--- /dev/null
+++ b/system/dev/i2c/i2c/i2c.h
@@ -0,0 +1,50 @@
+// 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 <threads.h>
+
+#include <ddktl/device.h>
+#include <ddktl/protocol/i2c.h>
+#include <ddktl/protocol/i2c-impl.h>
+#include <fbl/vector.h>
+#include <fbl/unique_ptr.h>
+
+namespace i2c {
+
+class I2cBus;
+class I2cDevice;
+using I2cDeviceType = ddk::Device<I2cDevice, ddk::Unbindable>;
+
+class I2cDevice : public I2cDeviceType, public ddk::I2cProtocol<I2cDevice> {
+public:
+    static zx_status_t Create(zx_device_t* parent);
+
+    // Device protocol implementation.
+    void DdkUnbind() {
+        DdkRemove();
+    }
+    void DdkRelease() {
+        delete this;
+    }
+
+    // I2c protocol implementation.
+    zx_status_t I2cTransact(uint32_t index, const void* write_buf, size_t write_length,
+                            size_t read_length, i2c_complete_cb complete_cb, void* cookie);
+    zx_status_t I2cGetMaxTransferSize(uint32_t index, size_t* out_size);
+
+private:
+    explicit I2cDevice(zx_device_t* parent, i2c_impl_protocol_t* i2c_impl)
+        : I2cDeviceType(parent), i2c_impl_(i2c_impl) {}
+
+
+    zx_status_t Init();
+
+    // Lower level I2C protocol.
+    ddk::I2cImplProtocolProxy i2c_impl_;
+
+    // List of I2C buses.
+    fbl::Vector<fbl::unique_ptr<I2cBus>> i2c_buses_;
+};
+
+} // namespace i2c
diff --git a/system/dev/i2c/i2c/rules.mk b/system/dev/i2c/i2c/rules.mk
new file mode 100644
index 0000000..588abfa
--- /dev/null
+++ b/system/dev/i2c/i2c/rules.mk
@@ -0,0 +1,29 @@
+# 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.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := driver
+
+MODULE_SRCS += \
+    $(LOCAL_DIR)/i2c.cpp \
+    $(LOCAL_DIR)/i2c-bus.cpp \
+    $(LOCAL_DIR)/binding.c \
+
+MODULE_STATIC_LIBS := \
+    system/ulib/ddk \
+    system/ulib/ddktl \
+    system/ulib/fbl \
+    system/ulib/sync \
+    system/ulib/zx \
+    system/ulib/zxcpp \
+
+MODULE_LIBS := \
+    system/ulib/driver \
+    system/ulib/c \
+    system/ulib/zircon \
+
+include make/module.mk
diff --git a/system/ulib/ddktl/include/ddktl/protocol/i2c-impl.h b/system/ulib/ddktl/include/ddktl/protocol/i2c-impl.h
index 4a9cb53..9c11998 100644
--- a/system/ulib/ddktl/include/ddktl/protocol/i2c-impl.h
+++ b/system/ulib/ddktl/include/ddktl/protocol/i2c-impl.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <ddk/driver.h>
 #include <ddk/protocol/i2c-impl.h>
 #include <ddktl/device-internal.h>
 #include <zircon/assert.h>
@@ -43,7 +44,7 @@
 namespace ddk {
 
 template <typename D>
-class I2cImplProtocol {
+class I2cImplProtocol : public internal::base_protocol {
 public:
     I2cImplProtocol() {
         internal::CheckI2cImplProtocolSubclass<D>();
@@ -51,6 +52,11 @@
         i2c_impl_proto_ops_.get_max_transfer_size = I2cImplGetMaxTransferSize;
         i2c_impl_proto_ops_.set_bitrate = I2cImplSetBitRate;
         i2c_impl_proto_ops_.transact = I2cImplTransact;
+
+        // Can only inherit from one base_protocol implemenation
+        ZX_ASSERT(ddk_proto_id_ == 0);
+        ddk_proto_id_ = ZX_PROTOCOL_I2C_IMPL;
+        ddk_proto_ops_ = &i2c_impl_proto_ops_;
     }
 
 protected:
diff --git a/system/ulib/ddktl/include/ddktl/protocol/i2c.h b/system/ulib/ddktl/include/ddktl/protocol/i2c.h
index 7862429..538c3fb 100644
--- a/system/ulib/ddktl/include/ddktl/protocol/i2c.h
+++ b/system/ulib/ddktl/include/ddktl/protocol/i2c.h
@@ -71,6 +71,11 @@
     I2cProtocolProxy(i2c_protocol_t* proto)
         : ops_(proto->ops), ctx_(proto->ctx) {}
 
+    void GetProto(i2c_protocol_t* proto) {
+        proto->ctx = ctx_;
+        proto->ops = ops_;
+    }
+
     zx_status_t Transact(uint32_t index, const void* write_buf, size_t write_length,
                             size_t read_length, i2c_complete_cb complete_cb, void* cookie) {
         return ops_->transact(ctx_, index, write_buf, write_length, read_length, complete_cb,