From 9d13a2463f3f655a786686066c6c4577422055a5 Mon Sep 17 00:00:00 2001 From: Juha Niskanen Date: Fri, 31 Mar 2017 06:35:36 -0600 Subject: [PATCH] drivers/usbmisc: Add driver for Fairchild FUSB301 USB type-C controller. From Harri Luhtala . Tested with earlier version of NuttX; with current version checked that it compiles. --- drivers/Kconfig | 10 + drivers/Makefile | 1 + drivers/sensors/lps25h.c | 2 +- drivers/usbmisc/Kconfig | 29 ++ drivers/usbmisc/Make.defs | 49 +++ drivers/usbmisc/fusb301.c | 855 ++++++++++++++++++++++++++++++++++++ include/nuttx/fs/ioctl.h | 7 + include/nuttx/usb/fusb301.h | 243 ++++++++++ 8 files changed, 1195 insertions(+), 1 deletion(-) create mode 100644 drivers/usbmisc/Kconfig create mode 100644 drivers/usbmisc/Make.defs create mode 100644 drivers/usbmisc/fusb301.c create mode 100644 include/nuttx/usb/fusb301.h diff --git a/drivers/Kconfig b/drivers/Kconfig index 511d63c5f6c..d7bc2653746 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -552,6 +552,16 @@ if USBHOST source drivers/usbhost/Kconfig endif # USBHOST +menuconfig USBMISC + bool "USB Miscellaneous drivers" + default n + ---help--- + USB Miscellaneous drivers. + +if USBMISC +source drivers/usbmisc/Kconfig +endif # USBMISC + config HAVE_USBTRACE bool default n diff --git a/drivers/Makefile b/drivers/Makefile index 94f027afbbf..5946cc123a7 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -72,6 +72,7 @@ include syslog$(DELIM)Make.defs include timers$(DELIM)Make.defs include usbdev$(DELIM)Make.defs include usbhost$(DELIM)Make.defs +include usbmisc$(DELIM)Make.defs include usbmonitor$(DELIM)Make.defs include video$(DELIM)Make.defs include wireless$(DELIM)Make.defs diff --git a/drivers/sensors/lps25h.c b/drivers/sensors/lps25h.c index e46b1c7c0a6..3ebba982857 100644 --- a/drivers/sensors/lps25h.c +++ b/drivers/sensors/lps25h.c @@ -220,7 +220,7 @@ static const struct file_operations g_lps25hops = lps25h_close, /* close */ lps25h_read, /* read */ lps25h_write, /* write */ - 0, /* seek */ + NULL, /* seek */ lps25h_ioctl /* ioctl */ #ifndef CONFIG_DISABLE_POLL , NULL /* poll */ diff --git a/drivers/usbmisc/Kconfig b/drivers/usbmisc/Kconfig new file mode 100644 index 00000000000..68bd619ee3a --- /dev/null +++ b/drivers/usbmisc/Kconfig @@ -0,0 +1,29 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +comment "USB Miscellaneous drivers" + +config FUSB301 + bool "Fairchild FUSB301 USB type-C controller support" + default n + select I2C + ---help--- + Enable device driver for Fairchild USB type-C controller + +if FUSB301 + +config DEBUG_FUSB301 + bool "Enable debug support for the FUSB301" + default n + ---help--- + Enables debug support for the FUSB301 + +config FUSB301_NPOLLWAITERS + int "Number of waiters to poll" + default 2 + ---help--- + Maximum number of threads that can be waiting on poll() + +endif diff --git a/drivers/usbmisc/Make.defs b/drivers/usbmisc/Make.defs new file mode 100644 index 00000000000..fd9dd6af37e --- /dev/null +++ b/drivers/usbmisc/Make.defs @@ -0,0 +1,49 @@ +############################################################################ +# drivers/usbmisc/Make.defs +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_USBMISC),y) + +# Include USB miscellaneous drivers + +ifeq ($(CONFIG_FUSB301),y) + CSRCS += fusb301.c +endif + +# Include USB miscellaneous build support + +DEPPATH += --dep-path usbmisc +VPATH += :usbmisc +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)usbmisc} +endif diff --git a/drivers/usbmisc/fusb301.c b/drivers/usbmisc/fusb301.c new file mode 100644 index 00000000000..c3627fd909d --- /dev/null +++ b/drivers/usbmisc/fusb301.c @@ -0,0 +1,855 @@ +/**************************************************************************** + * drivers/usbmisc/fusb301.c + * + * FUSB301 USB-C controller driver + * + * Copyright (C) 2016-2017 Haltian Ltd. All rights reserved. + * Authors: Harri Luhtala + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_FUSB301 +# define fusb301_err(x, ...) _err(x, ##__VA_ARGS__) +# define fusb301_info(x, ...) _info(x, ##__VA_ARGS__) +#else +# define fusb301_err(x, ...) uerr(x, ##__VA_ARGS__) +# define fusb301_info(x, ...) uinfo(x, ##__VA_ARGS__) +#endif + +/* Other macros */ + +#define FUSB301_I2C_RETRIES 10 + +/**************************************************************************** + * Private Data Types + ****************************************************************************/ + +struct fusb301_dev_s +{ + FAR struct i2c_master_s *i2c; /* I2C interface */ + uint8_t addr; /* I2C address */ + volatile bool int_pending; /* Interrupt received but handled */ + sem_t devsem; /* Manages exclusive access */ + FAR struct fusb301_config_s *config; /* Platform specific configuration */ +#ifndef CONFIG_DISABLE_POLL + FAR struct pollfd *fds[CONFIG_FUSB301_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Private Function prototypes + ****************************************************************************/ + +static int fusb301_open(FAR struct file *filep); +static int fusb301_close(FAR struct file *filep); +static ssize_t fusb301_read(FAR struct file *, FAR char *, size_t); +static ssize_t fusb301_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int fusb301_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +#ifndef CONFIG_DISABLE_POLL +static int fusb301_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); +static void fusb301_notify(FAR struct fusb301_dev_s *priv); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_fusb301ops = +{ + fusb301_open, /* open */ + fusb301_close, /* close */ + fusb301_read, /* read */ + fusb301_write, /* write */ + NULL, /* seek */ + fusb301_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , fusb301_poll /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ + #endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fusb301_getreg + * + * Description: + * Read from an 8-bit FUSB301 register + * + * Input Parameters: + * priv - pointer to FUSB301 Private Structure + * reg - register to read + * + * Returned Value: + * Returns positive register value in case of success, otherwise ERROR + ****************************************************************************/ + +static int fusb301_getreg(FAR struct fusb301_dev_s *priv, uint8_t reg) +{ + int ret = -EIO; + int retries; + uint8_t regval; + struct i2c_msg_s msg[2]; + + DEBUGASSERT(priv); + + msg[0].addr = priv->addr; + msg[0].flags = 0; + msg[0].buffer = ® + msg[0].length = 1; + + msg[1].addr = priv->addr; + msg[1].flags = I2C_M_READ; + msg[1].buffer = ®val; + msg[1].length = 1; + + /* Perform the transfer */ + + for (retries = 0; retries < FUSB301_I2C_RETRIES; retries++) + { + ret = I2C_TRANSFER(priv->i2c, msg, 2); + if (ret == OK) + { + fusb301_info("reg:%02X, value:%02X\n", reg, regval); + return regval; + } + else + { + /* Some error. Try to reset I2C bus and keep trying. */ + +#ifdef CONFIG_I2C_RESET + if (retries == FUSB301_I2C_RETRIES - 1) + { + break; + } + + ret = up_i2creset(priv->i2c); + if (ret < 0) + { + fusb301_err("ERROR: up_i2creset failed: %d\n", ret); + return ret; + } +#endif + } + } + + fusb301_info("reg:%02X, error:%d\n", reg, ret); + return ret; +} + +/**************************************************************************** + * Name: fusb301_putreg + * + * Description: + * Write a value to an 8-bit FUSB301 register + * + * Input Parameters: + * priv - pointer to FUSB301 Private Structure + * regaddr - register to read + * regval - value to be written + * + * Returned Value: + * None + ****************************************************************************/ + +static int fusb301_putreg(FAR struct fusb301_dev_s *priv, uint8_t regaddr, + uint8_t regval) +{ + int ret = -EIO; + int retries; + struct i2c_msg_s msg; + uint8_t txbuffer[2]; + + /* Setup to the data to be transferred (register address and data). */ + + txbuffer[0] = regaddr; + txbuffer[1] = regval; + + /* Setup 8-bit FUSB301 address write message */ + + msg.addr = priv->addr; + msg.flags = 0; + msg.buffer = txbuffer; + msg.length = 2; + + /* Perform the transfer */ + + for (retries = 0; retries < FUSB301_I2C_RETRIES; retries++) + { + ret = I2C_TRANSFER(priv->i2c, &msg, 1); + if (ret == OK) + { + fusb301_info("reg:%02X, value:%02X\n", regaddr, regval); + + return OK; + } + else + { + /* Some error. Try to reset I2C bus and keep trying. */ + +#ifdef CONFIG_I2C_RESET + if (retries == FUSB301_I2C_RETRIES - 1) + { + break; + } + + ret = up_i2creset(priv->i2c); + if (ret < 0) + { + fusb301_err("ERROR: up_i2creset failed: %d\n", ret); + return ret; + } +#endif + } + } + + fusb301_err("ERROR: failed reg:%02X, value:%02X, error:%d\n", + regaddr, regval, ret); + return ret; +} + +/**************************************************************************** + * Name: fusb301_read_device_id + * + * Description: + * Read device version and revision IDs + * + ****************************************************************************/ + +static int fusb301_read_device_id(FAR struct fusb301_dev_s * priv, + FAR uint8_t * arg) +{ + int ret; + + ret = fusb301_getreg(priv, FUSB301_DEV_ID_REG); + if (ret < 0) + { + fusb301_err("ERROR: Failed to read device ID\n"); + return -EIO; + } + + *arg = ret; + return OK; +} + +/**************************************************************************** + * Name: fusb301_clear_interrupts + * + * Description: + * Clear interrupts from FUSB301 chip + * + ****************************************************************************/ + +static int fusb301_clear_interrupts(FAR struct fusb301_dev_s *priv) +{ + int ret = OK; + + ret = fusb301_getreg(priv, FUSB301_INTERRUPT_REG); + if (ret < 0) + { + fusb301_err("ERROR: Failed to clear interrupts\n"); + return -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: fusb301_setup + * + * Description: + * Setup FUSB301 chip + * + ****************************************************************************/ + +static int fusb301_setup(FAR struct fusb301_dev_s *priv, + struct fusb301_setup_s *setup) +{ + int ret = OK; + + fusb301_info("drp_tgl:%02X, host_curr:%02X, global_int:%X, mask:%02X\n", + setup->drp_toggle_timing, setup->host_current, setup->global_int_mask, + setup->int_mask); + + ret = fusb301_putreg(priv, FUSB301_CONTROL_REG, setup->drp_toggle_timing | + setup->host_current | setup->global_int_mask); + + if (ret < 0) + { + fusb301_err("ERROR: Failed to write control register\n"); + goto err_out; + } + + ret = fusb301_putreg(priv, FUSB301_MASK_REG, setup->int_mask); + if (ret < 0) + { + fusb301_err("ERROR: Failed to write mask register\n"); + } + +err_out: + return ret; +} + +/**************************************************************************** + * Name: fusb301_set_mode + * + * Description: + * Configure supported device modes (sink, source, DRP, accessory) + * + ****************************************************************************/ + +static int fusb301_set_mode(FAR struct fusb301_dev_s *priv, + enum fusb301_mode_e mode) +{ + int ret = OK; + + if (mode > MODE_DRP_ACC) + { + return -EINVAL; + } + + ret = fusb301_putreg(priv, FUSB301_MODE_REG, mode); + if (ret < 0) + { + fusb301_err("ERROR: Failed to write mode register\n"); + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: fusb301_set_state + * + * Description: + * Force device in specified state + * + ****************************************************************************/ + +static int fusb301_set_state(FAR struct fusb301_dev_s *priv, + enum fusb301_manual_e state) +{ + int ret = OK; + + if (state > MANUAL_UNATT_SNK) + { + return -EINVAL; + } + + ret = fusb301_putreg(priv, FUSB301_MANUAL_REG, state); + if (ret < 0) + { + fusb301_err("ERROR: Failed to write manual register\n"); + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: fusb301_read_status + * + * Description: + * Clear read status register + * + ****************************************************************************/ + +static int fusb301_read_status(FAR struct fusb301_dev_s *priv, + FAR uint8_t *arg) +{ + int ret; + + ret = fusb301_getreg(priv, FUSB301_STATUS_REG); + if (ret < 0) + { + fusb301_err("ERROR: Failed to read status\n"); + return -EIO; + } + + *arg = ret; + return OK; +} + +/**************************************************************************** + * Name: fusb301_read_devtype + * + * Description: + * Read type of attached device + * + ****************************************************************************/ + +static int fusb301_read_devtype(FAR struct fusb301_dev_s *priv, + FAR uint8_t *arg) +{ + int ret; + + ret = fusb301_getreg(priv, FUSB301_TYPE_REG); + if (ret < 0) + { + fusb301_err("ERROR: Failed to read type\n"); + return -EIO; + } + + *arg = ret; + return OK; +} + +/**************************************************************************** + * Name: fusb301_reset + * + * Description: + * Reset FUSB301 HW and clear I2C registers + * + ****************************************************************************/ + +static int fusb301_reset(FAR struct fusb301_dev_s *priv) +{ + int ret = OK; + + ret = fusb301_putreg(priv, FUSB301_RESET_REG, RESET_SW_RES); + if (ret < 0) + { + fusb301_err("ERROR: Failed to write reset register\n"); + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: fusb301_open + * + * Description: + * This function is called whenever the FUSB301 device is opened. + * + ****************************************************************************/ + +static int fusb301_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct fusb301_dev_s *priv = inode->i_private; + int ret = OK; + + /* Probe device */ + + ret = fusb301_getreg(priv, FUSB301_DEV_ID_REG); + if (ret < 0) + { + fusb301_err("ERROR: No response at given address 0x%02X\n", priv->addr); + ret = -EFAULT; + } + else + { + fusb301_info("device id: 0x%02X\n", ret); + + (void)fusb301_clear_interrupts(priv); + priv->config->irq_enable(priv->config, true); + } + + /* Error exit */ + + return ret; +} + +/**************************************************************************** + * Name: fusb301_close + * + * Description: + * This routine is called when the FUSB301 device is closed. + * + ****************************************************************************/ + +static int fusb301_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct fusb301_dev_s *priv = inode->i_private; + + priv->config->irq_enable(priv->config, false); + + return OK; +} + +/**************************************************************************** + * Name: fusb301_read + * Description: + * This routine is called when the FUSB301 device is read. + ****************************************************************************/ + +static ssize_t fusb301_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct fusb301_dev_s *priv = inode->i_private; + FAR struct fusb301_result_s *ptr; + irqstate_t flags; + int ret; + + if (buflen < sizeof(struct fusb301_result_s)) + { + return 0; + } + + ptr = (struct fusb301_result_s *)buffer; + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + flags = enter_critical_section(); + priv->int_pending = false; + leave_critical_section(flags); + + (void)fusb301_clear_interrupts(priv); + + ptr->status = fusb301_getreg(priv, FUSB301_STATUS_REG); + ptr->dev_type = fusb301_getreg(priv, FUSB301_TYPE_REG); + + sem_post(&priv->devsem); + return sizeof(struct fusb301_result_s); +} + +/**************************************************************************** + * Name: fusb301_write + * Description: + * This routine is called when the FUSB301 device is written to. + ****************************************************************************/ + +static ssize_t fusb301_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + ssize_t length = 0; + + return length; +} + +/**************************************************************************** + * Name: fusb301_ioctl + * Description: + * This routine is called when ioctl function call is performed for + * the FUSB301 device. + ****************************************************************************/ + +static int fusb301_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct fusb301_dev_s *priv = inode->i_private; + int ret; + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + fusb301_info("cmd: 0x%02X, arg:%lu\n", cmd, arg); + + switch (cmd) + { + case USBCIOC_READ_DEVID: + { + ret = fusb301_read_device_id(priv, (uint8_t *)arg); + } + break; + + case USBCIOC_SETUP: + { + ret = fusb301_setup(priv, (struct fusb301_setup_s *)arg); + } + break; + + case USBCIOC_SET_MODE: + { + ret = fusb301_set_mode(priv, (uint8_t)arg); + } + break; + + case USBCIOC_SET_STATE: + { + ret = fusb301_set_state(priv, (uint8_t)arg); + } + break; + + case USBCIOC_READ_STATUS: + { + ret = fusb301_read_status(priv, (uint8_t *)arg); + } + break; + + case USBCIOC_READ_DEVTYPE: + { + ret = fusb301_read_devtype(priv, (uint8_t *)arg); + } + break; + + case USBCIOC_RESET: + { + ret = fusb301_reset(priv); + } + break; + + default: + { + fusb301_err("ERROR: Unrecognized cmd: %d\n", cmd); + ret = -ENOTTY; + } + break; + } + + sem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name: fusb301_poll + * Description: + * This routine is called during FUSB301 device poll + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int fusb301_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) +{ + FAR struct inode *inode; + FAR struct fusb301_dev_s *priv; + irqstate_t flags; + int ret = OK; + int i; + + DEBUGASSERT(filep && fds); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct fusb301_dev_s *)inode->i_private; + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + if (setup) + { + /* Ignore waits that do not include POLLIN */ + + if ((fds->events & POLLIN) == 0) + { + ret = -EDEADLK; + goto out; + } + + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference. + */ + + for (i = 0; i < CONFIG_FUSB301_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!priv->fds[i]) + { + /* Bind the poll structure and this slot */ + + priv->fds[i] = fds; + fds->priv = &priv->fds[i]; + break; + } + } + + if (i >= CONFIG_FUSB301_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + goto out; + } + + flags = enter_critical_section(); + if (priv->int_pending) + { + fusb301_notify(priv); + } + + leave_critical_section(flags); + } + else if (fds->priv) + { + /* This is a request to tear down the poll. */ + + struct pollfd **slot = (struct pollfd **)fds->priv; + DEBUGASSERT(slot != NULL); + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +out: + sem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name: fusb301_notify + * + * Description: + * Notify thread about data to be available + * + ****************************************************************************/ + +static void fusb301_notify(FAR struct fusb301_dev_s *priv) +{ + DEBUGASSERT(priv != NULL); + + int i; + + /* If there are threads waiting on poll() for FUSB301 data to become available, + * then wake them up now. NOTE: we wake up all waiting threads because we + * do not know that they are going to do. If they all try to read the data, + * then some make end up blocking after all. + */ + + for (i = 0; i < CONFIG_FUSB301_NPOLLWAITERS; i++) + { + struct pollfd *fds = priv->fds[i]; + if (fds) + { + fds->revents |= POLLIN; + fusb301_info("Report events: %02x\n", fds->revents); + sem_post(fds->sem); + } + } +} +#endif /* !CONFIG_DISABLE_POLL */ + +/**************************************************************************** + * Name: fusb301_callback + * + * Description: + * FUSB301 interrupt handler + * + ****************************************************************************/ + +static int fusb301_int_handler(int irq, FAR void *context, FAR void *arg) +{ + FAR struct fusb301_dev_s *priv = (FAR struct fusb301_dev_s *)arg; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + + flags = enter_critical_section(); + priv->int_pending = true; + +#ifndef CONFIG_DISABLE_POLL + fusb301_notify(priv); +#endif + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int fusb301_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR struct fusb301_config_s *config) +{ + FAR struct fusb301_dev_s *priv; + int ret; + + DEBUGASSERT(devpath != NULL && i2c != NULL && config != NULL); + + /* Initialize the FUSB301 device structure */ + + priv = (FAR struct fusb301_dev_s *)kmm_zalloc(sizeof(struct fusb301_dev_s)); + if (!priv) + { + fusb301_err("ERROR: Failed to allocate instance\n"); + return -ENOMEM; + } + + /* Initialize device structure semaphore */ + + sem_init(&priv->devsem, 0, 1); + + priv->int_pending = false; + priv->i2c = i2c; + priv->addr = addr; + priv->config = config; + + /* Register the character driver */ + + ret = register_driver(devpath, &g_fusb301ops, 0666, priv); + if (ret < 0) + { + fusb301_err("ERROR: Failed to register driver: %d\n", ret); + goto errout_with_priv; + } + + /* Prepare interrupt line and handler. */ + + priv->config->irq_clear(config); + priv->config->irq_attach(config, fusb301_int_handler, priv); + priv->config->irq_enable(config, false); + + return OK; + +errout_with_priv: + sem_destroy(&priv->devsem); + kmm_free(priv); + + return ret; +} diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 5582ccd0fee..9b000ceb05c 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -86,6 +86,7 @@ #define _SPIBASE (0x2100) /* SPI driver commands */ #define _GPIOBASE (0x2200) /* GPIO driver commands */ #define _CLIOCBASE (0x2300) /* Contactless modules ioctl commands */ +#define _USBCBASE (0x2400) /* USB-C controller ioctl commands */ /* boardctl() commands share the same number space */ @@ -401,6 +402,12 @@ #define _CLIOCVALID(c) (_IOC_TYPE(c)==_CLIOCBASE) #define _CLIOC(nr) _IOC(_CLIOCBASE,nr) +/* USB-C controller driver ioctl definitions ********************************/ +/* (see nuttx/include/usb/xxx.h */ + +#define _USBCIOCVALID(c) (_IOC_TYPE(c)==_USBCBASE) +#define _USBCIOC(nr) _IOC(_USBCBASE,nr) + /* boardctl() command definitions *******************************************/ #define _BOARDIOCVALID(c) (_IOC_TYPE(c)==_BOARDBASE) diff --git a/include/nuttx/usb/fusb301.h b/include/nuttx/usb/fusb301.h new file mode 100644 index 00000000000..deecff2b752 --- /dev/null +++ b/include/nuttx/usb/fusb301.h @@ -0,0 +1,243 @@ +/**************************************************************************** + * include/nuttx/usb/fusb301.h + * FUSB301 USB type-C controller driver + * + * Copyright (C) 2016-2017 Haltian Ltd. All rights reserved. + * Authors: Harri Luhtala + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_USB_FUSB301_H +#define __INCLUDE_NUTTX_USB_FUSB301_H + +#include + +/************************************************************************************ + * Pre-Processor Declarations + ************************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* IOCTL Commands ***********************************************************/ + +#define USBCIOC_READ_DEVID _USBCIOC(0x0001) /* Arg: uint8_t* pointer */ +#define USBCIOC_SETUP _USBCIOC(0x0002) /* Arg: uint8_t* pointer */ +#define USBCIOC_SET_MODE _USBCIOC(0x0003) /* Arg: uint8_t value */ +#define USBCIOC_SET_STATE _USBCIOC(0x0004) /* Arg: uint8_t value */ +#define USBCIOC_READ_STATUS _USBCIOC(0x0005) /* Arg: uint8_t* pointer*/ +#define USBCIOC_READ_DEVTYPE _USBCIOC(0x0006) /* Arg: uint8_t* pointer*/ +#define USBCIOC_RESET _USBCIOC(0x0007) /* Arg: None */ + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +enum fusb301_reg_address_e +{ + FUSB301_DEV_ID_REG = 0x01, + FUSB301_MODE_REG, + FUSB301_CONTROL_REG, + FUSB301_MANUAL_REG, + FUSB301_RESET_REG, + FUSB301_MASK_REG = 0x10, + FUSB301_STATUS_REG, + FUSB301_TYPE_REG, + FUSB301_INTERRUPT_REG +}; + +/* Device ID - 0x01 */ + +enum fusb301_devid_mask_e +{ + DEV_ID_REVISION_MASK = 0x0F, + DEV_ID_VERSION_MASK = 0xF0 +}; + +#define DEV_ID_VER_A 0x10 +#define DEV_ID_REV_C 0x02 + +/* Modes - 0x02 */ + +enum fusb301_mode_e +{ + MODE_SRC = (1 << 0), + MODE_SRC_ACC = (1 << 1), + MODE_SNK = (1 << 2), + MODE_SNK_ACC = (1 << 3), + MODE_DRP = (1 << 4), + MODE_DRP_ACC = (1 << 5) +}; + +/* Control - 0x03 */ + +enum fusb301_control_e +{ + CONTROL_INT_ENABLE = (0 << 0), + CONTROL_INT_DISABLE = (1 << 0), + CONTROL_CUR_DISABLED = (0 << 1), + CONTROL_CUR_DEFAULT = (1 << 1), + CONTROL_CUR_1500 = (2 << 1), + CONTROL_CUR_3000 = (3 << 1), + CONTROL_TGL_35_15MS = (0 << 4), + CONTROL_TGL_30_20MS = (1 << 4), + CONTROL_TGL_25_25MS = (2 << 4), + CONTROL_TGL_20_30MS = (3 << 4) +}; + +/* Manual - 0x04 */ + +enum fusb301_manual_e +{ + MANUAL_ERROR_REC = (1 << 0), + MANUAL_DISABLED = (1 << 1), + MANUAL_UNATT_SRC = (1 << 2), + MANUAL_UNATT_SNK = (1 << 3) +}; + +/* Reset - 0x05 */ + +enum fusb301_reset_e +{ + RESET_SW_RES = (1 << 0) +}; + +/* Interrupt mask - 0x10 */ + +enum fusb301_int_mask_e +{ + INT_MASK_ATTACK = (1 << 0), + INT_MASK_DETACH = (1 << 1), + INT_MASK_BC_LVL = (1 << 2), + INT_MASK_ACC_CH = (1 << 3) +}; + +/* Status - 0x11 */ + +enum fusb301_status_e +{ + STATUS_ATTACH = (1 << 0), + STATUS_BC_SINK_UNATT = (0 << 1), + STATUS_BC_SINK_DEF = (1 << 1), + STATUS_BC_SINK_1500 = (2 << 1), + STATUS_BC_SINK_3000 = (3 << 1), + STATUS_VBUS_OK = (1 << 3), + STATUS_CC_NO_CONN = (0 << 4), + STATUS_CC_1 = (1 << 4), + STATUS_CC_2 = (2 << 4), + STATUS_CC_FAULT = (3 << 4) +}; + +/* Type - 0x12 */ + +enum fusb301_type_e +{ + TYPE_AUDIOACC = (1 << 0), + TYPE_DEBUGACC = (1 << 1), + TYPE_SOURCE = (1 << 3), + TYPE_SINK = (1 << 4) +}; + +/* Interrupt - 0x13 */ + +enum fusb301_interrupt_e +{ + INTERRUPT_ATTACH = (1 << 0), + INTERRUPT_DETACH = (1 << 1), + INTERRUPT_BC_LVL = (1 << 2), + INTERRUPT_ACC_CH = (1 << 3) +}; + +struct fusb301_result_s +{ + uint8_t status; + uint8_t dev_type; +}; + +struct fusb301_setup_s +{ + uint8_t drp_toggle_timing; + uint8_t host_current; + uint8_t int_mask; + bool global_int_mask; +}; + +struct fusb301_config_s +{ + /* Device characterization */ + + int irq; + + CODE int (*irq_attach)(FAR struct fusb301_config_s *state, xcpt_t isr, + FAR void *arg); + CODE void (*irq_enable)(FAR struct fusb301_config_s *state, bool enable); + CODE void (*irq_clear)(FAR struct fusb301_config_s *state); +}; + +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +/**************************************************************************** + * Name: fusb301_register + * + * Description: + * Register the FUSB301 character device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/usbc0" + * i2c - An instance of the I2C interface to use to communicate with FUSB301 + * addr - The I2C address of the FUSB301. The I2C address of the FUSB301 is 0x25. + * config - Pointer to FUSB301 configuration + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int fusb301_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR struct fusb301_config_s *config); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_NUTTX_USB_FUSB301_H */