From 2447f5ea1a682b1e46cb7916a468a875282d18fe Mon Sep 17 00:00:00 2001 From: dron0gus Date: Fri, 12 Apr 2019 23:14:25 +0300 Subject: [PATCH] gpio chips (#744) * tle8888: fix building for stm32f765 * gpio-chips: add mc33972 driver --- firmware/hw_layer/drivers/drivers.mk | 3 +- firmware/hw_layer/drivers/gpio/mc33972.c | 380 +++++++++++++++++++++++ firmware/hw_layer/drivers/gpio/mc33972.h | 36 +++ firmware/hw_layer/drivers/gpio/tle8888.c | 2 +- 4 files changed, 419 insertions(+), 2 deletions(-) create mode 100644 firmware/hw_layer/drivers/gpio/mc33972.c create mode 100644 firmware/hw_layer/drivers/gpio/mc33972.h diff --git a/firmware/hw_layer/drivers/drivers.mk b/firmware/hw_layer/drivers/drivers.mk index 320359b84a..c43a008048 100644 --- a/firmware/hw_layer/drivers/drivers.mk +++ b/firmware/hw_layer/drivers/drivers.mk @@ -9,7 +9,8 @@ HW_LAYER_DRIVERS_CORE = \ HW_LAYER_DRIVERS = \ $(DRIVERS_DIR)/gpio/tle6240.c \ - $(DRIVERS_DIR)/gpio/tle8888.c + $(DRIVERS_DIR)/gpio/tle8888.c \ + $(DRIVERS_DIR)/gpio/mc33972.c HW_LAYER_DRIVERS_CPP = diff --git a/firmware/hw_layer/drivers/gpio/mc33972.c b/firmware/hw_layer/drivers/gpio/mc33972.c new file mode 100644 index 0000000000..005abf6a55 --- /dev/null +++ b/firmware/hw_layer/drivers/gpio/mc33972.c @@ -0,0 +1,380 @@ +/* + * mc33972.c + * + * Multiple Switch Detection Interface with Suppressed Wake-up + * + * The 33972 Multiple Switch Detection Interface with suppressed + * wake-up is designed to detect the closing and opening of up to 22 + * switch contacts: 14 switch to ground detection and 8 switch to + * ground or battery detection, + * + * SPI protocol 3.3/5.0V + * + * @date Apr 07, 2019 + * @author Andrey Gusakov , (c) 2019 + */ + +#include "global.h" +#include "gpio/gpio_ext.h" +#include "gpio/mc33972.h" +#include "pin_repository.h" + +#ifndef BOARD_MC33972_COUNT + #define BOARD_MC33972_COUNT 0 +#endif + +#if (BOARD_MC33972_COUNT > 0) + +/* + * TODO list: + * - add irq support with fallback to polling mode (now polling mode only) + */ + +/*==========================================================================*/ +/* Driver local definitions. */ +/*==========================================================================*/ + +#define DRIVER_NAME "mc33972" + +static bool drv_task_ready = false; + +typedef enum { + MC33972_DISABLED = 0, + MC33972_WAIT_INIT, + MC33972_READY, + MC33972_FAILED +} mc33972_drv_state; + +/* all commands and reply data are 24 bit */ +#define CMD(cmd, data) (((cmd) << 16) | ((data) << 0)) + +#define CMD_STATUS CMD(0x00, 0x00) +#define CMD_SETTINGS(mask) CMD(0x01, (mask)) +#define CMD_WAKEUPEN(i, mask) CMD((i) ? 0x03 : 0x02, (mask)) +#define CMD_METALLIC(i, mask) CMD((i) ? 0x05 : 0x04, (mask)) +#define CMD_ANALOG(cur, ch) CMD(0x06, (((curr) & 0x3) << 5) | (((d) & 0x1f) << 0)) +#define CMD_WETTING_TMR(i, mask) CMD((i) ? 0x08 : 0x07, (mask)) +#define CMD_TRI_STATE(i, mask) CMD((i) ? 0x0a : 0x09, (mask)) +#define CMD_CALIB CMD(0x0b, 0x00) +#define CMD_SLEEP(int_t, scan_t) CMD(0x0c, (((int_t) & 0x7) << 3) | (((scan_t) & 0x7) << 0)) +#define CMD_RST CMD(0x7f, 0x00) + +/* reply is allways same 24 status bits */ +#define FLAG_THERM (1 << 23) +#define FLAG_INT (1 << 22) +/* from LSB to MSB: SG0..SG13, SP0..SP7 */ +#define FLAG_PIN(pin) (1 << (pin)) + +/*==========================================================================*/ +/* Driver exported variables. */ +/*==========================================================================*/ + +/*==========================================================================*/ +/* Driver local variables and types. */ +/*==========================================================================*/ + +/* OS */ +SEMAPHORE_DECL(mc33972_wake, 10 /* or BOARD_MC33972_COUNT ? */); +static THD_WORKING_AREA(mc33972_thread_1_wa, 256); + +/* Driver */ +struct mc33972_priv { + const struct mc33972_config *cfg; + /* last input state from chip */ + uint32_t i_state; + /* currently selected analog input */ + uint8_t analog_ch; + + mc33972_drv_state drv_state; +}; + +static struct mc33972_priv chips[BOARD_MC33972_COUNT]; + +/*==========================================================================*/ +/* Driver local functions. */ +/*==========================================================================*/ + +static SPIDriver *get_bus(struct mc33972_priv *chip) +{ + /* return non-const SPIDriver* from const struct cfg */ + return chip->cfg->spi_bus; +} + +/** + * @brief MC33972 send cmd routine. + * @details Sends 24 bits of data. CS asserted before and released + * after transaction. Chip allways reply with input state + 2 bits + * of diagnostic. This routine save it to chip->i_state + */ + +static int mc33972_spi_w(struct mc33972_priv *chip, uint32_t tx) +{ + int i; + uint8_t rxb[3]; + uint8_t txb[3]; + SPIDriver *spi = get_bus(chip); + + txb[0] = (tx >> 16) & 0xff; + txb[1] = (tx >> 8) & 0xff; + txb[2] = (tx >> 0) & 0xff; + /* Acquire ownership of the bus. */ + spiAcquireBus(spi); + /* Setup transfer parameters. */ + spiStart(spi, &chip->cfg->spi_config); + /* Slave Select assertion. */ + spiSelect(spi); + /* Atomic transfer operations. */ + /* TODO: check why spiExchange transfers invalid data on STM32F7xx, DMA issue? */ + //spiExchange(spi, 3, txb, rxb); + for (i = 0; i < 3; i++) + rxb[i] = spiPolledExchange(spi, txb[i]); + /* Slave Select de-assertion. */ + spiUnselect(spi); + /* Ownership release. */ + spiReleaseBus(spi); + + /* save received data */ + chip->i_state = (rxb[0] << 16) | (rxb[1] << 8) | (rxb[2] << 0); + + /* no errors for now */ + return 0; +} + +/** + * @brief MC33972 update status + * @details Chip reply with input data and two bits of diag + */ + +static int mc33972_update_status(struct mc33972_priv *chip) +{ + int ret; + + /* TODO: lock? */ + + ret = mc33972_spi_w(chip, CMD_STATUS); + + /* TODO: unlock? */ + + return ret; +} + +/** + * @brief MC33972 chip init. + * @details There is no way to check communication. + * Performs reset. + */ + +static int mc33972_chip_init(struct mc33972_priv *chip) +{ + int ret; + + ret = mc33972_spi_w(chip, CMD_RST); + if (ret) + goto err; + + /* is it enought? */ + chThdSleepMilliseconds(3); + + /* + * Default settings from Power-ON Reset via V PWR or the + * Reset Command are as follows: + * * Programmable switch – set to switch to battery + * * All inputs set as wake-up + * * Wetting current on (16 mA) + * * Wetting current timer on (20 ms) + * * All inputs tri-state + * * Analog select 00000 (no input channel selected) + */ + + /* disable tri-state */ + ret = mc33972_spi_w(chip, CMD_TRI_STATE(0, 0)); + ret |= mc33972_spi_w(chip, CMD_TRI_STATE(1, 0)); + if (ret) + goto err; + + /* Set wetting current to 2 mA */ + ret = mc33972_spi_w(chip, CMD_METALLIC(0, 0)); + ret |= mc33972_spi_w(chip, CMD_METALLIC(1, 0)); + if (ret) + goto err; + +err: + return ret; +} + +/** + * @brief MC33972 chip driver wakeup. + * @details Wake up driver. Will cause input and diagnostic + * update + */ + +static int mc33972_wake_driver(struct mc33972_priv *chip) +{ + (void)chip; + + chSemSignal(&mc33972_wake); + + return 0; +} + +/*==========================================================================*/ +/* Driver thread. */ +/*==========================================================================*/ + +static THD_FUNCTION(mc33972_driver_thread, p) +{ + int i; + msg_t msg; + + (void)p; + + chRegSetThreadName(DRIVER_NAME); + + while(1) { + msg = chSemWaitTimeout(&mc33972_wake, TIME_MS2I(MC33972_POLL_INTERVAL_MS)); + + /* should we care about msg == MSG_TIMEOUT? */ + (void)msg; + + for (i = 0; i < BOARD_MC33972_COUNT; i++) { + int ret; + struct mc33972_priv *chip; + + chip = &chips[i]; + if ((chip->cfg == NULL) || + (chip->drv_state == MC33972_DISABLED) || + (chip->drv_state == MC33972_FAILED)) + continue; + + ret = mc33972_update_status(chip); + if (ret) { + /* set state to MC33972_FAILED? */ + } + } + } +} + +/*==========================================================================*/ +/* Driver interrupt handlers. */ +/*==========================================================================*/ + +/* TODO: add IRQ support */ + +/*==========================================================================*/ +/* Driver exported functions. */ +/*==========================================================================*/ + +int mc33972_readPad(void *data, unsigned int pin) +{ + struct mc33972_priv *chip; + + if ((pin >= MC33972_INPUTS) || (data == NULL)) + return -1; + + chip = (struct mc33972_priv *)data; + + /* convert to some common enum? */ + return !!(chip->i_state & FLAG_PIN(pin)); +} + +int mc33972_getDiag(void *data, unsigned int pin) +{ + int diag; + struct mc33972_priv *chip; + + if ((pin >= MC33972_INPUTS) || (data == NULL)) + return -1; + + chip = (struct mc33972_priv *)data; + + /* one diag for all pins */ + diag = !!(chip->i_state & FLAG_THERM); + + /* convert to some common enum? */ + return diag; +} + +int mc33972_init(void * data) +{ + int ret; + struct mc33972_priv *chip; + const struct mc33972_config *cfg; + + chip = (struct mc33972_priv *)data; + cfg = chip->cfg; + + /* mark pins used */ + ret = gpio_pin_markUsed(cfg->spi_config.ssport, cfg->spi_config.sspad, DRIVER_NAME " CS"); + + ret = mc33972_chip_init(chip); + if (ret) + return ret; + + chip->drv_state = MC33972_READY; + + if (!drv_task_ready) { + chThdCreateStatic(mc33972_thread_1_wa, sizeof(mc33972_thread_1_wa), + NORMALPRIO + 1, mc33972_driver_thread, NULL); + drv_task_ready = true; + } + + return 0; +} + +int mc33972_deinit(void *data) +{ + (void)data; + + /* TODO: set all pins to inactive state, stop task? */ + return 0; +} + +struct gpiochip_ops mc33972_ops = { + .writePad = NULL, /* chip input only */ + .readPad = mc33972_readPad, + .getDiag = mc33972_getDiag, + .init = mc33972_init, + .deinit = mc33972_deinit, +}; + +/** + * @brief MC33972 driver add. + * @details Checks for valid config + */ + +int mc33972_add(unsigned int index, const struct mc33972_config *cfg) +{ + struct mc33972_priv *chip; + + /* no config or no such chip */ + osalDbgCheck((cfg != NULL) && (cfg->spi_bus != NULL) && (index < BOARD_MC33972_COUNT)); + + /* check for valid cs. + * DOTO: remove this check? CS can be driven by SPI */ + if (cfg->spi_config.ssport == NULL) + return -1; + + chip = &chips[index]; + + /* already initted? */ + if (chip->cfg != NULL) + return -1; + + chip->cfg = cfg; + chip->i_state = 0; + chip->drv_state = MC33972_WAIT_INIT; + + /* register, return gpio chip base */ + return gpiochip_register(DRIVER_NAME, &mc33972_ops, MC33972_INPUTS, chip); +} + +#else /* BOARD_MC33972_COUNT > 0 */ + +int mc33972_add(unsigned int index, const struct mc33972_config *cfg) +{ + (void)index; (void)cfg; + + return -1; +} + +#endif /* BOARD_MC33972_COUNT */ diff --git a/firmware/hw_layer/drivers/gpio/mc33972.h b/firmware/hw_layer/drivers/gpio/mc33972.h new file mode 100644 index 0000000000..d9f92fca8a --- /dev/null +++ b/firmware/hw_layer/drivers/gpio/mc33972.h @@ -0,0 +1,36 @@ +/* + * mc33972.h + * + * Multiple Switch Detection Interface with Suppressed Wake-up + * + * @date Apr 07, 2019 + * @author Andrey Gusakov , (c) 2019 + */ + +#ifndef HW_LAYER_MC33972_H_ +#define HW_LAYER_MC33972_H_ + +#include + +#define MC33972_INPUTS 22 + +/* DOTO: add irq support */ +#define MC33972_POLL_INTERVAL_MS 100 + +struct mc33972_config { + SPIDriver *spi_bus; + const SPIConfig spi_config; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +int mc33972_add(unsigned int index, const struct mc33972_config *cfg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* HW_LAYER_MC33972_H_ */ diff --git a/firmware/hw_layer/drivers/gpio/tle8888.c b/firmware/hw_layer/drivers/gpio/tle8888.c index 57bdd5607b..5f62b77ffc 100644 --- a/firmware/hw_layer/drivers/gpio/tle8888.c +++ b/firmware/hw_layer/drivers/gpio/tle8888.c @@ -306,7 +306,7 @@ static struct tle8888_config tle8888_cfg = { SPI_CR1_CPHA | 0, .cr2 = 0 -#elif defined(STM32F767xx) || defined(STM32F746xx) +#elif defined (STM32F765xx) || defined(STM32F767xx) || defined(STM32F746xx) .cr1 = SPI_CR1_SSM | SPI_CR1_SSI |