zephyr/drivers/gpio/gpio_pcal9535a.c
Daniel Leung dff5f6a038 i2c: make i2c_transfer() really generic
Make the i2c_transfer() to transact messages through the I2C bus.

It is useful for I2C storage devices, as now we can send one message
containing the destination byte/block address, then send the data
in another message. There is no need to construct one continuous
data buffer to send both address and data anymore.

The drivers and sample apps have been updated to utilize updated
API when appropriate. For i2c_dw, only master mode has been updated.
Slave mode will be updated once we can adequately test it.

Change-Id: I0a811d60567367817fcc8d15f5454e5c933722e2
Signed-off-by: Daniel Leung <daniel.leung@intel.com>
2016-02-05 20:25:15 -05:00

716 lines
18 KiB
C

/*
* Copyright (c) 2015 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Driver for PCAL9535A I2C-based GPIO driver.
*/
#include <nanokernel.h>
#include <gpio.h>
#include <i2c.h>
#include "gpio_pcal9535a.h"
#ifndef CONFIG_GPIO_PCAL9535A_DEBUG
#define DBG(...) { ; }
#else
#if defined(CONFIG_STDOUT_CONSOLE)
#include <stdio.h>
#define DBG printf
#else
#include <misc/printk.h>
#define DBG printk
#endif /* CONFIG_STDOUT_CONSOLE */
#endif /* CONFIG_GPIO_PCAL9535A_DEBUG */
/* Register definitions */
#define REG_INPUT_PORT0 0x00
#define REG_INPUT_PORT1 0x01
#define REG_OUTPUT_PORT0 0x02
#define REG_OUTPUT_PORT1 0x03
#define REG_POL_INV_PORT0 0x04
#define REG_POL_INV_PORT1 0x05
#define REG_CONF_PORT0 0x06
#define REG_CONG_PORT1 0x07
#define REG_OUT_DRV_STRENGTH_PORT0_L 0x40
#define REG_OUT_DRV_STRENGTH_PORT0_H 0x41
#define REG_OUT_DRV_STRENGTH_PORT1_L 0x42
#define REG_OUT_DRV_STRENGTH_PORT1_H 0x43
#define REG_INPUT_LATCH_PORT0 0x44
#define REG_INPUT_LATCH_PORT1 0x45
#define REG_PUD_EN_PORT0 0x46
#define REG_PUD_EN_PORT1 0x47
#define REG_PUD_SEL_PORT0 0x48
#define REG_PUD_SEL_PORT1 0x49
#define REG_INT_MASK_PORT0 0x4A
#define REG_INT_MASK_PORT1 0x4B
#define REG_INT_STATUS_PORT0 0x4C
#define REG_INT_STATUS_PORT1 0x4D
#define REG_OUTPUT_PORT_CONF 0x4F
/**
* @brief Check to see if a I2C master is identified for communication.
*
* @param dev Device struct.
* @return 1 if I2C master is identified, 0 if not.
*/
static inline int _has_i2c_master(struct device *dev)
{
struct gpio_pcal9535a_drv_data * const drv_data =
(struct gpio_pcal9535a_drv_data * const)dev->driver_data;
struct device * const i2c_master = drv_data->i2c_master;
if (i2c_master)
return 1;
else
return 0;
}
/**
* @brief Read both port 0 and port 1 registers of certain register function.
*
* Given the register in reg, read the pair of port 0 and port 1.
*
* @param dev Device struct of the PCAL9535A.
* @param reg Register to read (the PORT0 of the pair of registers).
* @param buf Buffer to read data into.
*
* @return DEV_OK if successful, failed otherwise.
*/
static int _read_port_regs(struct device *dev, uint8_t reg,
union gpio_pcal9535a_port_data *buf)
{
const struct gpio_pcal9535a_config * const config =
dev->config->config_info;
struct gpio_pcal9535a_drv_data * const drv_data =
(struct gpio_pcal9535a_drv_data * const)dev->driver_data;
struct device * const i2c_master = drv_data->i2c_master;
uint16_t i2c_addr = config->i2c_slave_addr;
int ret;
uint8_t cmd[] = { reg };
struct i2c_msg msgs[2];
msgs[0].buf = cmd;
msgs[0].len = 1;
msgs[0].flags = I2C_MSG_WRITE;
msgs[1].buf = buf->byte;
msgs[1].len = 2;
msgs[1].flags = I2C_MSG_READ | I2C_MSG_RESTART;
ret = i2c_transfer(i2c_master, msgs, 2, i2c_addr);
if (ret) {
DBG("PCAL9535A[0x%X]: error reading register 0x%X (%d)\n",
i2c_addr, reg, ret);
goto error;
}
DBG("PCAL9535A[0x%X]: Read: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X\n",
i2c_addr, reg, buf->byte[0], (reg + 1), buf->byte[1]);
error:
return ret;
}
/**
* @brief Write both port 0 and port 1 registers of certain register function.
*
* Given the register in reg, write the pair of port 0 and port 1.
*
* @param dev Device struct of the PCAL9535A.
* @param reg Register to write into (the PORT0 of the pair of registers).
* @param buf Buffer to write data from.
*
* @return DEV_OK if successful, failed otherwise.
*/
static int _write_port_regs(struct device *dev, uint8_t reg,
union gpio_pcal9535a_port_data *buf)
{
const struct gpio_pcal9535a_config * const config =
dev->config->config_info;
struct gpio_pcal9535a_drv_data * const drv_data =
(struct gpio_pcal9535a_drv_data * const)dev->driver_data;
struct device * const i2c_master = drv_data->i2c_master;
uint16_t i2c_addr = config->i2c_slave_addr;
uint8_t cmd[] = {reg, buf->byte[0], buf->byte[1]};
int ret;
DBG("PCAL9535A[0x%X]: Write: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X\n",
i2c_addr, reg, buf->byte[0], (reg + 1), buf->byte[1]);
ret = i2c_write(i2c_master, cmd, sizeof(cmd), i2c_addr);
if (ret) {
DBG("PCAL9535A[0x%X]: error writing from register 0x%X (%d)\n",
i2c_addr, reg, ret);
}
return ret;
}
/**
* @brief Setup the pin direction (input or output)
*
* @param dev Device struct of the PCAL9535A
* @param access_op Access operation (pin or port)
* @param pin The pin number
* @param flags Flags of pin or port
*
* @return DEV_OK if successful, failed otherwise
*/
static int _setup_pin_dir(struct device *dev, int access_op,
uint32_t pin, int flags)
{
struct gpio_pcal9535a_drv_data * const drv_data =
(struct gpio_pcal9535a_drv_data * const)dev->driver_data;
union gpio_pcal9535a_port_data *port = &drv_data->reg_cache.dir;
uint16_t bit_mask;
uint16_t new_value = 0;
int ret;
switch (access_op) {
case GPIO_ACCESS_BY_PIN:
bit_mask = 1 << pin;
/* Config 0 == output, 1 == input */
if ((flags & GPIO_DIR_MASK) == GPIO_DIR_IN) {
new_value = 1 << pin;
}
port->all &= ~bit_mask;
port->all |= new_value;
break;
case GPIO_ACCESS_BY_PORT:
/* Config 0 == output, 1 == input */
if ((flags & GPIO_DIR_MASK) == GPIO_DIR_IN) {
port->all = 0xFFFF;
} else {
port->all = 0x0;
}
break;
default:
ret = DEV_INVALID_OP;
goto done;
}
ret = _write_port_regs(dev, REG_CONF_PORT0, port);
done:
return ret;
}
/**
* @brief Setup the pin pull up/pull down status
*
* @param dev Device struct of the PCAL9535A
* @param access_op Access operation (pin or port)
* @param pin The pin number
* @param flags Flags of pin or port
*
* @return DEV_OK if successful, failed otherwise
*/
static int _setup_pin_pullupdown(struct device *dev, int access_op,
uint32_t pin, int flags)
{
struct gpio_pcal9535a_drv_data * const drv_data =
(struct gpio_pcal9535a_drv_data * const)dev->driver_data;
union gpio_pcal9535a_port_data *port;
uint16_t bit_mask;
uint16_t new_value = 0;
int ret;
/* If disabling pull up/down, there is no need to set the selection
* register. Just go straight to disabling.
*/
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_NORMAL) {
goto en_dis;
}
/* Setup pin pull up or pull down */
port = &drv_data->reg_cache.pud_sel;
switch (access_op) {
case GPIO_ACCESS_BY_PIN:
bit_mask = 1 << pin;
/* pull down == 0, pull up == 1*/
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_UP) {
new_value = 1 << pin;
}
port->all &= ~bit_mask;
port->all |= new_value;
break;
case GPIO_ACCESS_BY_PORT:
/* pull down == 0, pull up == 1*/
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_UP) {
port->all = 0xFFFF;
} else {
port->all = 0x0;
}
break;
default:
ret = DEV_INVALID_OP;
goto done;
}
ret = _write_port_regs(dev, REG_PUD_SEL_PORT0, port);
if (ret) {
goto done;
}
en_dis:
/* enable/disable pull up/down */
port = &drv_data->reg_cache.pud_en;
switch (access_op) {
case GPIO_ACCESS_BY_PIN:
bit_mask = 1 << pin;
if ((flags & GPIO_PUD_MASK) != GPIO_PUD_NORMAL) {
new_value = 1 << pin;
}
port->all &= ~bit_mask;
port->all |= new_value;
break;
case GPIO_ACCESS_BY_PORT:
if ((flags & GPIO_PUD_MASK) != GPIO_PUD_NORMAL) {
port->all = 0xFFFF;
} else {
port->all = 0x0;
}
break;
default:
ret = DEV_INVALID_OP;
goto done;
}
ret = _write_port_regs(dev, REG_PUD_EN_PORT0, port);
done:
return ret;
}
/**
* @brief Setup the polarity of pin or port
*
* @param dev Device struct of the PCAL9535A
* @param access_op Access operation (pin or port)
* @param pin The pin number
* @param flags Flags of pin or port
*
* @return DEV_OK if successful, failed otherwise
*/
static int _setup_pin_polarity(struct device *dev, int access_op,
uint32_t pin, int flags)
{
struct gpio_pcal9535a_drv_data * const drv_data =
(struct gpio_pcal9535a_drv_data * const)dev->driver_data;
union gpio_pcal9535a_port_data *port = &drv_data->reg_cache.pol_inv;
uint16_t bit_mask;
uint16_t new_value = 0;
int ret;
switch (access_op) {
case GPIO_ACCESS_BY_PIN:
bit_mask = 1 << pin;
/* normal == 0, invert == 1 */
if ((flags & GPIO_POL_MASK) == GPIO_POL_INV) {
new_value = 1 << pin;
}
port->all &= ~bit_mask;
port->all |= new_value;
break;
case GPIO_ACCESS_BY_PORT:
/* normal == 0, invert == 1 */
if ((flags & GPIO_POL_MASK) == GPIO_POL_INV) {
port->all = 0xFFFF;
} else {
port->all = 0x0;
}
break;
default:
ret = DEV_INVALID_OP;
goto done;
}
ret = _write_port_regs(dev, REG_POL_INV_PORT0, port);
if (!ret) {
drv_data->out_pol_inv = port->all;
}
done:
return ret;
}
/**
* @brief Configurate pin or port
*
* @param dev Device struct of the PCAL9535A
* @param access_op Access operation (pin or port)
* @param pin The pin number
* @param flags Flags of pin or port
*
* @return DEV_OK if successful, failed otherwise
*/
static int gpio_pcal9535a_config(struct device *dev, int access_op,
uint32_t pin, int flags)
{
int ret;
#ifdef CONFIG_GPIO_PCAL9535A_DEBUG
const struct gpio_pcal9535a_config * const config =
dev->config->config_info;
uint16_t i2c_addr = config->i2c_slave_addr;
#endif
if (!_has_i2c_master(dev)) {
return DEV_INVALID_CONF;
}
ret = _setup_pin_dir(dev, access_op, pin, flags);
if (ret) {
DBG("PCAL9535A[0x%X]: error setting pin direction (%d)\n",
i2c_addr, ret);
goto done;
}
ret = _setup_pin_polarity(dev, access_op, pin, flags);
if (ret) {
DBG("PCAL9535A[0x%X]: error setting pin polarity (%d)\n",
i2c_addr, ret);
goto done;
}
ret = _setup_pin_pullupdown(dev, access_op, pin, flags);
if (ret) {
DBG("PCAL9535A[0x%X]: error setting pin pull up/down (%d)\n",
i2c_addr, ret);
goto done;
}
done:
return ret;
}
/**
* @brief Set the pin or port output
*
* @param dev Device struct of the PCAL9535A
* @param access_op Access operation (pin or port)
* @param pin The pin number
* @param value Value to set (0 or 1)
*
* @return DEV_OK if successful, failed otherwise
*/
static int gpio_pcal9535a_write(struct device *dev, int access_op,
uint32_t pin, uint32_t value)
{
struct gpio_pcal9535a_drv_data * const drv_data =
(struct gpio_pcal9535a_drv_data * const)dev->driver_data;
union gpio_pcal9535a_port_data *port = &drv_data->reg_cache.output;
uint16_t bit_mask;
uint16_t new_value;
int ret;
if (!_has_i2c_master(dev)) {
return DEV_INVALID_CONF;
}
/* Invert input value for pins configurated as active low. */
switch (access_op) {
case GPIO_ACCESS_BY_PIN:
bit_mask = 1 << pin;
new_value = (value << pin) & bit_mask;
new_value ^= (drv_data->out_pol_inv & bit_mask);
new_value &= bit_mask;
port->all &= ~bit_mask;
port->all |= new_value;
break;
case GPIO_ACCESS_BY_PORT:
port->all = value;
bit_mask = drv_data->out_pol_inv;
new_value = value & bit_mask;
new_value ^= drv_data->out_pol_inv;
new_value &= bit_mask;
port->all &= ~bit_mask;
port->all |= new_value;
break;
default:
ret = DEV_INVALID_OP;
goto done;
}
ret = _write_port_regs(dev, REG_OUTPUT_PORT0, port);
done:
return ret;
}
/**
* @brief Read the pin or port status
*
* @param dev Device struct of the PCAL9535A
* @param access_op Access operation (pin or port)
* @param pin The pin number
* @param value Value of input pin(s)
*
* @return DEV_OK if successful, failed otherwise
*/
static int gpio_pcal9535a_read(struct device *dev, int access_op,
uint32_t pin, uint32_t *value)
{
union gpio_pcal9535a_port_data buf;
int ret;
if (!_has_i2c_master(dev)) {
return DEV_INVALID_CONF;
}
ret = _read_port_regs(dev, REG_INPUT_PORT0, &buf);
if (ret != DEV_OK) {
goto done;
}
switch (access_op) {
case GPIO_ACCESS_BY_PIN:
*value = (buf.all >> pin) & 0x01;
break;
case GPIO_ACCESS_BY_PORT:
*value = buf.all;
break;
default:
ret = DEV_INVALID_OP;
break;
}
done:
return ret;
}
static int gpio_pcal9535a_set_callback(struct device *dev,
gpio_callback_t callback)
{
ARG_UNUSED(dev);
ARG_UNUSED(callback);
return DEV_INVALID_OP;
}
static int gpio_pcal9535a_enable_callback(struct device *dev,
int access_op, uint32_t pin)
{
ARG_UNUSED(dev);
ARG_UNUSED(access_op);
ARG_UNUSED(pin);
return DEV_INVALID_OP;
}
static int gpio_pcal9535a_disable_callback(struct device *dev,
int access_op, uint32_t pin)
{
ARG_UNUSED(dev);
ARG_UNUSED(access_op);
ARG_UNUSED(pin);
return DEV_INVALID_OP;
}
static int gpio_pcal9535a_suspend_port(struct device *dev)
{
if (!_has_i2c_master(dev)) {
return DEV_INVALID_CONF;
}
return DEV_INVALID_OP;
}
static int gpio_pcal9535a_resume_port(struct device *dev)
{
if (!_has_i2c_master(dev)) {
return DEV_INVALID_CONF;
}
return DEV_INVALID_OP;
}
static struct gpio_driver_api gpio_pcal9535a_drv_api_funcs = {
.config = gpio_pcal9535a_config,
.write = gpio_pcal9535a_write,
.read = gpio_pcal9535a_read,
.set_callback = gpio_pcal9535a_set_callback,
.enable_callback = gpio_pcal9535a_enable_callback,
.disable_callback = gpio_pcal9535a_disable_callback,
.suspend = gpio_pcal9535a_suspend_port,
.resume = gpio_pcal9535a_resume_port,
};
/**
* @brief Initialization function of PCAL9535A
*
* @param dev Device struct
* @return DEV_OK if successful, failed otherwise.
*/
int gpio_pcal9535a_init(struct device *dev)
{
const struct gpio_pcal9535a_config * const config =
dev->config->config_info;
struct gpio_pcal9535a_drv_data * const drv_data =
(struct gpio_pcal9535a_drv_data * const)dev->driver_data;
struct device *i2c_master;
dev->driver_api = &gpio_pcal9535a_drv_api_funcs;
/* Find out the device struct of the I2C master */
i2c_master = device_get_binding((char *)config->i2c_master_dev_name);
if (!i2c_master) {
return DEV_INVALID_CONF;
}
drv_data->i2c_master = i2c_master;
nano_timer_init(&drv_data->timer, (void *) 0);
return DEV_OK;
}
/* Initialization for PCAL9535A_0 */
#ifdef CONFIG_GPIO_PCAL9535A_0
#include <device.h>
#include <init.h>
static struct gpio_pcal9535a_config gpio_pcal9535a_0_cfg = {
.i2c_master_dev_name = CONFIG_GPIO_PCAL9535A_0_I2C_MASTER_DEV_NAME,
.i2c_slave_addr = CONFIG_GPIO_PCAL9535A_0_I2C_ADDR,
};
static struct gpio_pcal9535a_drv_data gpio_pcal9535a_0_drvdata = {
/* Default for registers according to datasheet */
.reg_cache.output = { .all = 0xFFFF },
.reg_cache.pol_inv = { .all = 0x0 },
.reg_cache.dir = { .all = 0xFFFF },
.reg_cache.pud_en = { .all = 0x0 },
.reg_cache.pud_sel = { .all = 0xFFFF },
};
DECLARE_DEVICE_INIT_CONFIG(gpio_pcal9535a_0,
CONFIG_GPIO_PCAL9535A_0_DEV_NAME,
gpio_pcal9535a_init, &gpio_pcal9535a_0_cfg);
/* This has to init after I2C master */
SYS_DEFINE_DEVICE(gpio_pcal9535a_0, &gpio_pcal9535a_0_drvdata, SECONDARY,
CONFIG_GPIO_PCAL9535A_INIT_PRIORITY);
#endif /* CONFIG_GPIO_PCAL9535A_0 */
/* Initialization for PCAL9535A_1 */
#ifdef CONFIG_GPIO_PCAL9535A_1
#include <device.h>
#include <init.h>
static struct gpio_pcal9535a_config gpio_pcal9535a_1_cfg = {
.i2c_master_dev_name = CONFIG_GPIO_PCAL9535A_1_I2C_MASTER_DEV_NAME,
.i2c_slave_addr = CONFIG_GPIO_PCAL9535A_1_I2C_ADDR,
};
static struct gpio_pcal9535a_drv_data gpio_pcal9535a_1_drvdata = {
/* Default for registers according to datasheet */
.reg_cache.output = { .all = 0xFFFF },
.reg_cache.pol_inv = { .all = 0x0 },
.reg_cache.dir = { .all = 0xFFFF },
.reg_cache.pud_en = { .all = 0x0 },
.reg_cache.pud_sel = { .all = 0xFFFF },
};
DECLARE_DEVICE_INIT_CONFIG(gpio_pcal9535a_1,
CONFIG_GPIO_PCAL9535A_1_DEV_NAME,
gpio_pcal9535a_init, &gpio_pcal9535a_1_cfg);
/* This has to init after I2C master */
SYS_DEFINE_DEVICE(gpio_pcal9535a_1, &gpio_pcal9535a_1_drvdata, SECONDARY,
CONFIG_GPIO_PCAL9535A_INIT_PRIORITY);
#endif /* CONFIG_GPIO_PCAL9535A_1 */
/* Initialization for PCAL9535A_2 */
#ifdef CONFIG_GPIO_PCAL9535A_2
#include <device.h>
#include <init.h>
static struct gpio_pcal9535a_config gpio_pcal9535a_2_cfg = {
.i2c_master_dev_name = CONFIG_GPIO_PCAL9535A_2_I2C_MASTER_DEV_NAME,
.i2c_slave_addr = CONFIG_GPIO_PCAL9535A_2_I2C_ADDR,
};
static struct gpio_pcal9535a_drv_data gpio_pcal9535a_2_drvdata = {
/* Default for registers according to datasheet */
.reg_cache.output = { .all = 0xFFFF },
.reg_cache.pol_inv = { .all = 0x0 },
.reg_cache.dir = { .all = 0xFFFF },
.reg_cache.pud_en = { .all = 0x0 },
.reg_cache.pud_sel = { .all = 0xFFFF },
};
DECLARE_DEVICE_INIT_CONFIG(gpio_pcal9535a_2,
CONFIG_GPIO_PCAL9535A_2_DEV_NAME,
gpio_pcal9535a_init, &gpio_pcal9535a_2_cfg);
/* This has to init after I2C master */
SYS_DEFINE_DEVICE(gpio_pcal9535a_2, &gpio_pcal9535a_2_drvdata, SECONDARY,
CONFIG_GPIO_PCAL9535A_INIT_PRIORITY);
#endif /* CONFIG_GPIO_PCAL9535A_2 */
/* Initialization for PCAL9535A_3 */
#ifdef CONFIG_GPIO_PCAL9535A_3
#include <device.h>
#include <init.h>
static struct gpio_pcal9535a_config gpio_pcal9535a_3_cfg = {
.i2c_master_dev_name = CONFIG_GPIO_PCAL9535A_3_I2C_MASTER_DEV_NAME,
.i2c_slave_addr = CONFIG_GPIO_PCAL9535A_3_I2C_ADDR,
};
static struct gpio_pcal9535a_drv_data gpio_pcal9535a_3_drvdata = {
/* Default for registers according to datasheet */
.reg_cache.output = { .all = 0xFFFF },
.reg_cache.pol_inv = { .all = 0x0 },
.reg_cache.dir = { .all = 0xFFFF },
.reg_cache.pud_en = { .all = 0x0 },
.reg_cache.pud_sel = { .all = 0xFFFF },
};
DECLARE_DEVICE_INIT_CONFIG(gpio_pcal9535a_3,
CONFIG_GPIO_PCAL9535A_3_DEV_NAME,
gpio_pcal9535a_init, &gpio_pcal9535a_3_cfg);
/* This has to init after I2C master */
SYS_DEFINE_DEVICE(gpio_pcal9535a_3, &gpio_pcal9535a_3_drvdata, SECONDARY,
CONFIG_GPIO_PCAL9535A_INIT_PRIORITY);
#endif /* CONFIG_GPIO_PCAL9535A_3 */