blob: d1d17fd6e83bcba17f4468df896967e765afb1d4 [file] [log] [blame]
// 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 "gpio.h"
#include <zircon/types.h>
#include <memory>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/metadata.h>
#include <ddk/metadata/gpio.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_call.h>
#include <fbl/auto_lock.h>
namespace gpio {
zx_status_t GpioDevice::GpioConfigIn(uint32_t flags) {
fbl::AutoLock lock(&lock_);
return gpio_.ConfigIn(pin_, flags);
}
zx_status_t GpioDevice::GpioConfigOut(uint8_t initial_value) {
fbl::AutoLock lock(&lock_);
return gpio_.ConfigOut(pin_, initial_value);
}
zx_status_t GpioDevice::GpioSetAltFunction(uint64_t function) {
fbl::AutoLock lock(&lock_);
return gpio_.SetAltFunction(pin_, function);
}
zx_status_t GpioDevice::GpioRead(uint8_t* out_value) {
fbl::AutoLock lock(&lock_);
return gpio_.Read(pin_, out_value);
}
zx_status_t GpioDevice::GpioWrite(uint8_t value) {
fbl::AutoLock lock(&lock_);
return gpio_.Write(pin_, value);
}
zx_status_t GpioDevice::GpioGetInterrupt(uint32_t flags, zx::interrupt* out_irq) {
fbl::AutoLock lock(&lock_);
return gpio_.GetInterrupt(pin_, flags, out_irq);
}
zx_status_t GpioDevice::GpioReleaseInterrupt() {
fbl::AutoLock lock(&lock_);
return gpio_.ReleaseInterrupt(pin_);
}
zx_status_t GpioDevice::GpioSetPolarity(gpio_polarity_t polarity) {
fbl::AutoLock lock(&lock_);
return gpio_.SetPolarity(pin_, polarity);
}
zx_status_t GpioDevice::GpioSetDriveStrength(uint64_t ds_ua, uint64_t* out_actual_ds_ua) {
fbl::AutoLock lock(&lock_);
return gpio_.SetDriveStrength(pin_, ds_ua, out_actual_ds_ua);
}
void GpioDevice::DdkUnbind(ddk::UnbindTxn txn) { txn.Reply(); }
void GpioDevice::DdkRelease() { delete this; }
zx_status_t GpioDevice::Create(void* ctx, zx_device_t* parent) {
gpio_impl_protocol_t gpio;
auto status = device_get_protocol(parent, ZX_PROTOCOL_GPIO_IMPL, &gpio);
if (status != ZX_OK) {
return status;
}
size_t metadata_size;
status = device_get_metadata_size(parent, DEVICE_METADATA_GPIO_PINS, &metadata_size);
if (status != ZX_OK) {
return status;
}
auto pin_count = metadata_size / sizeof(gpio_pin_t);
fbl::AllocChecker ac;
std::unique_ptr<gpio_pin_t[]> pins(new (&ac) gpio_pin_t[pin_count]);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
size_t actual;
status =
device_get_metadata(parent, DEVICE_METADATA_GPIO_PINS, pins.get(), metadata_size, &actual);
if (status != ZX_OK) {
return status;
}
if (actual != metadata_size) {
return ZX_ERR_INTERNAL;
}
for (uint32_t i = 0; i < pin_count; i++) {
auto pin = pins[i].pin;
fbl::AllocChecker ac;
std::unique_ptr<GpioDevice> dev(new (&ac) GpioDevice(parent, &gpio, pin));
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
char name[20];
snprintf(name, sizeof(name), "gpio-%u", pin);
zx_device_prop_t props[] = {
{BIND_GPIO_PIN, 0, pin},
};
status = dev->DdkAdd(ddk::DeviceAddArgs(name).set_props(props));
if (status != ZX_OK) {
return status;
}
// dev is now owned by devmgr.
__UNUSED auto ptr = dev.release();
}
return ZX_OK;
}
static constexpr zx_driver_ops_t driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = GpioDevice::Create;
return ops;
}();
} // namespace gpio
ZIRCON_DRIVER_BEGIN(gpio, gpio::driver_ops, "zircon", "0.1", 1)
BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_GPIO_IMPL), ZIRCON_DRIVER_END(gpio)