gpio: Add a driver for SX1509B

Adds a driver for SX1509B I2C GPIO chip. This driver only supports the
basic GPIO features and does not currently implement the LED driver and
keypad matrix features.

Signed-off-by: Aapo Vienamo <aapo.vienamo@iki.fi>
This commit is contained in:
Aapo Vienamo 2018-02-02 13:30:20 +02:00 committed by Maureen Helm
commit dd9719bf46
5 changed files with 440 additions and 0 deletions

View file

@ -20,5 +20,6 @@ zephyr_sources_ifdef(CONFIG_GPIO_SCH gpio_sch.c)
zephyr_sources_ifdef(CONFIG_GPIO_STM32 gpio_stm32.c)
zephyr_sources_ifdef(CONFIG_GPIO_SAM0 gpio_sam0.c)
zephyr_sources_ifdef(CONFIG_GPIO_SAM gpio_sam.c)
zephyr_sources_ifdef(CONFIG_GPIO_SX1509B gpio_sx1509b.c)
zephyr_sources_ifdef(CONFIG_USERSPACE gpio_handlers.c)

View file

@ -77,4 +77,6 @@ source "drivers/gpio/Kconfig.sam0"
source "drivers/gpio/Kconfig.sam"
source "drivers/gpio/Kconfig.sx1509b"
endif # GPIO

View file

@ -0,0 +1,44 @@
# Kconfig.sx1509b - SX1509B GPIO configuration options
#
#
# Copyright (c) 2018 Aapo Vienamo
#
# SPDX-License-Identifier: Apache-2.0
#
menuconfig GPIO_SX1509B
bool "SX1509B I2C GPIO chip"
depends on GPIO && I2C
default n
help
Enable driver for SX1509B I2C GPIO chip.
config GPIO_SX1509B_INIT_PRIORITY
int
default 70
prompt "Init priority"
help
Device driver initialization priority.
if !HAS_DTS_I2C_DEVICE
config GPIO_SX1509B_DEV_NAME
string "SX1509B GPIO chip Device Name"
default "GPIO_P0"
help
Specify the device name for the SX1509B I2C GPIO chip.
config GPIO_SX1509B_I2C_ADDR
hex "SX1509B GPIO chip I2C slave address"
default 0x3e
help
Specify the I2C slave address for the SX1509B I2C GPIO chip.
config GPIO_SX1509B_I2C_MASTER_DEV_NAME
string "I2C Master to which SX1509B GPIO chip is connected"
default ""
help
Specify the device name of the I2C master device to which SX1509B
chip is binded.
endif

372
drivers/gpio/gpio_sx1509b.c Normal file
View file

@ -0,0 +1,372 @@
/*
* Copyright (c) 2018 Aapo Vienamo
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <kernel.h>
#include <device.h>
#include <init.h>
#include <gpio.h>
#include <i2c.h>
#include <misc/byteorder.h>
#include <misc/util.h>
/** Cache of the output configuration and data of the pins */
struct gpio_sx1509b_pin_state {
u16_t input_disable;
u16_t pull_up;
u16_t pull_down;
u16_t open_drain;
u16_t polarity;
u16_t dir;
u16_t data;
};
/** Runtime driver data */
struct gpio_sx1509b_drv_data {
struct device *i2c_master;
struct gpio_sx1509b_pin_state pin_state;
struct k_sem lock;
};
/** Configuration data */
struct gpio_sx1509b_config {
const char *i2c_master_dev_name;
u16_t i2c_slave_addr;
};
/* General configuration register addresses */
enum {
/* TODO: Add rest of the regs */
SX1509B_REG_CLOCK = 0x1e,
SX1509B_REG_RESET = 0x7d,
};
/* Magic values for softreset */
enum {
SX1509B_REG_RESET_MAGIC0 = 0x12,
SX1509B_REG_RESET_MAGIC1 = 0x34,
};
/* Register bits for SX1509B_REG_CLOCK */
enum {
SX1509B_REG_CLOCK_FOSC_OFF = 0 << 5,
SX1509B_REG_CLOCK_FOSC_EXT = 1 << 5,
SX1509B_REG_CLOCK_FOSC_INT_2MHZ = 2 << 5,
};
/* Pin configuration register addresses */
enum {
SX1509B_REG_INPUT_DISABLE = 0x00,
SX1509B_REG_PULL_UP = 0x06,
SX1509B_REG_PULL_DOWN = 0x08,
SX1509B_REG_OPEN_DRAIN = 0x0a,
SX1509B_REG_DIR = 0x0e,
SX1509B_REG_DATA = 0x10,
SX1509B_REG_LED_DRIVER_ENABLE = 0x20,
};
/**
* @brief Write a big-endian word to an internal address of an I2C slave.
*
* @param dev Pointer to the device structure for the driver instance.
* @param dev_addr Address of the I2C device for writing.
* @param reg_addr Address of the internal register being written.
* @param value Value to be written to internal register.
*
* @retval 0 If successful.
* @retval -EIO General input / output error.
*/
static inline int i2c_reg_write_word_be(struct device *dev, u16_t dev_addr,
u8_t reg_addr, u16_t value)
{
u8_t tx_buf[3] = { reg_addr, value >> 8, value & 0xff };
return i2c_write(dev, tx_buf, 3, dev_addr);
}
/**
* @brief Configure pin or port
*
* @param dev Device struct of the SX1509B
* @param access_op Access operation (pin or port)
* @param pin The pin number
* @param flags Flags of pin or port
*
* @return 0 if successful, failed otherwise
*/
static int gpio_sx1509b_config(struct device *dev, int access_op, u32_t pin,
int flags)
{
const struct gpio_sx1509b_config *cfg = dev->config->config_info;
struct gpio_sx1509b_drv_data *drv_data = dev->driver_data;
struct gpio_sx1509b_pin_state *pins = &drv_data->pin_state;
int ret = 0;
if (flags & GPIO_INT) {
return -ENOTSUP;
}
k_sem_take(&drv_data->lock, K_FOREVER);
switch (access_op) {
case GPIO_ACCESS_BY_PIN:
if ((flags & GPIO_DIR_MASK) == GPIO_DIR_IN) {
pins->dir |= BIT(pin);
pins->input_disable &= ~BIT(pin);
} else {
pins->dir &= ~BIT(pin);
pins->input_disable |= BIT(pin);
}
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_UP) {
pins->pull_up |= BIT(pin);
} else {
pins->pull_up &= ~BIT(pin);
}
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_DOWN) {
pins->pull_down |= BIT(pin);
} else {
pins->pull_down &= ~BIT(pin);
}
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_NORMAL) {
pins->pull_up &= ~BIT(pin);
pins->pull_down &= ~BIT(pin);
}
if (flags & GPIO_DS_DISCONNECT_HIGH) {
pins->open_drain |= BIT(pin);
} else {
pins->open_drain &= ~BIT(pin);
}
if (flags & GPIO_POL_INV) {
pins->polarity |= BIT(pin);
} else {
pins->polarity &= ~BIT(pin);
}
break;
case GPIO_ACCESS_BY_PORT:
if ((flags & GPIO_DIR_MASK) == GPIO_DIR_IN) {
pins->dir = 0xffff;
pins->input_disable = 0x0000;
} else {
pins->dir = 0x0000;
pins->input_disable = 0xffff;
}
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_UP) {
pins->pull_up = 0xffff;
} else {
pins->pull_up = 0x0000;
}
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_DOWN) {
pins->pull_down = 0xffff;
} else {
pins->pull_down = 0x0000;
}
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_NORMAL) {
pins->pull_up = 0x0000;
pins->pull_down = 0x0000;
}
if (flags & GPIO_DS_DISCONNECT_HIGH) {
pins->open_drain = 0xffff;
} else {
pins->open_drain = 0x0000;
}
if (flags & GPIO_POL_INV) {
pins->polarity = 0xffff;
} else {
pins->polarity = 0x0000;
}
break;
default:
ret = -ENOTSUP;
goto out;
}
ret = i2c_reg_write_word_be(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_DIR, pins->dir);
if (ret)
goto out;
ret = i2c_reg_write_word_be(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_INPUT_DISABLE,
pins->input_disable);
if (ret)
goto out;
ret = i2c_reg_write_word_be(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_PULL_UP,
pins->pull_up);
if (ret)
goto out;
ret = i2c_reg_write_word_be(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_PULL_DOWN,
pins->pull_down);
if (ret)
goto out;
ret = i2c_reg_write_word_be(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_OPEN_DRAIN,
pins->open_drain);
out:
k_sem_give(&drv_data->lock);
return ret;
}
/**
* @brief Set the pin or port output
*
* @param dev Device struct of the SX1509B
* @param access_op Access operation (pin or port)
* @param pin The pin number
* @param value Value to set (0 or 1)
*
* @return 0 if successful, failed otherwise
*/
static int gpio_sx1509b_write(struct device *dev, int access_op, u32_t pin,
u32_t value)
{
const struct gpio_sx1509b_config *cfg = dev->config->config_info;
struct gpio_sx1509b_drv_data *drv_data = dev->driver_data;
u16_t *pin_data = &drv_data->pin_state.data;
int ret = 0;
k_sem_take(&drv_data->lock, K_FOREVER);
switch (access_op) {
case GPIO_ACCESS_BY_PIN:
if (value) {
*pin_data |= BIT(pin);
} else {
*pin_data &= ~BIT(pin);
}
break;
case GPIO_ACCESS_BY_PORT:
*pin_data = value;
break;
default:
ret = -ENOTSUP;
goto out;
}
ret = i2c_reg_write_word_be(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_DATA, *pin_data);
out:
k_sem_give(&drv_data->lock);
return ret;
}
/**
* @brief Read the pin or port data
*
* @param dev Device struct of the SX1509B
* @param access_op Access operation (pin or port)
* @param pin The pin number
* @param value Value of input pin(s)
*
* @return 0 if successful, failed otherwise
*/
static int gpio_sx1509b_read(struct device *dev, int access_op, u32_t pin,
u32_t *value)
{
const struct gpio_sx1509b_config *cfg = dev->config->config_info;
struct gpio_sx1509b_drv_data *drv_data = dev->driver_data;
u16_t pin_data;
int ret;
k_sem_take(&drv_data->lock, K_FOREVER);
ret = i2c_burst_read(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_DATA, (u8_t *)&pin_data,
sizeof(pin_data));
if (ret)
goto out;
pin_data = sys_be16_to_cpu(pin_data);
switch (access_op) {
case GPIO_ACCESS_BY_PIN:
*value = !!(pin_data & (BIT(pin)));
break;
case GPIO_ACCESS_BY_PORT:
*value = pin_data;
break;
default:
ret = -ENOTSUP;
}
out:
k_sem_give(&drv_data->lock);
return ret;
}
/**
* @brief Initialization function of SX1509B
*
* @param dev Device struct
* @return 0 if successful, failed otherwise.
*/
static int gpio_sx1509b_init(struct device *dev)
{
const struct gpio_sx1509b_config *cfg = dev->config->config_info;
struct gpio_sx1509b_drv_data *drv_data = dev->driver_data;
int ret;
drv_data->i2c_master = device_get_binding(cfg->i2c_master_dev_name);
if (!drv_data->i2c_master) {
ret = -EINVAL;
goto out;
}
/* Reset state */
drv_data->pin_state = (struct gpio_sx1509b_pin_state) {
.input_disable = 0x0000,
.pull_up = 0x0000,
.pull_down = 0x0000,
.open_drain = 0x0000,
.dir = 0xffff,
.data = 0xffff,
};
ret = i2c_reg_write_byte(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_RESET, SX1509B_REG_RESET_MAGIC0);
if (ret)
goto out;
ret = i2c_reg_write_byte(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_RESET, SX1509B_REG_RESET_MAGIC1);
if (ret)
goto out;
ret = i2c_reg_write_byte(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_CLOCK,
SX1509B_REG_CLOCK_FOSC_INT_2MHZ);
if (ret)
goto out;
out:
k_sem_give(&drv_data->lock);
return ret;
}
static const struct gpio_sx1509b_config gpio_sx1509b_cfg = {
.i2c_master_dev_name = CONFIG_GPIO_SX1509B_I2C_MASTER_DEV_NAME,
.i2c_slave_addr = CONFIG_GPIO_SX1509B_I2C_ADDR,
};
static struct gpio_sx1509b_drv_data gpio_sx1509b_drvdata = {
.lock = _K_SEM_INITIALIZER(gpio_sx1509b_drvdata.lock, 1, 1),
};
static const struct gpio_driver_api gpio_sx1509b_drv_api_funcs = {
.config = gpio_sx1509b_config,
.write = gpio_sx1509b_write,
.read = gpio_sx1509b_read,
};
DEVICE_AND_API_INIT(gpio_sx1509b, CONFIG_GPIO_SX1509B_DEV_NAME,
gpio_sx1509b_init, &gpio_sx1509b_drvdata, &gpio_sx1509b_cfg,
POST_KERNEL, CONFIG_GPIO_SX1509B_INIT_PRIORITY,
&gpio_sx1509b_drv_api_funcs);

View file

@ -0,0 +1,21 @@
#
# Copyright (c) 2018, Aapo Vienamo
#
# SPDX-License-Identifier: Apache-2.0
#
---
title: Semtech SX1509B I2C GPIO
id: semtech,sx1509b
version: 0.1
description: >
This is a representation of the SX1509B GPIO node
inherits:
!include i2c-device.yaml
properties:
compatible:
constraint: "semtech,sx1509b"
...