123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- /****************************************************************************
- * drivers/input/stmpe811_base.c
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership. The
- * ASF licenses this file to you 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.
- *
- ****************************************************************************/
- /* References:
- * "STMPE811 S-Touch advanced resistive touchscreen controller with 8-bit
- * GPIO expander," Doc ID 14489 Rev 6, CD00186725, STMicroelectronics"
- */
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <unistd.h>
- #include <assert.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/signal.h>
- #include <nuttx/input/stmpe811.h>
- #include "stmpe811.h"
- #if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_STMPE811)
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- /* If only a single STMPE811 device is supported, then the driver state
- * structure may as well be pre-allocated.
- */
- #ifndef CONFIG_STMPE811_MULTIPLE
- static struct stmpe811_dev_s g_stmpe811;
- /* Otherwise, we will need to maintain allocated driver instances in a list */
- #else
- static struct stmpe811_dev_s *g_stmpe811list;
- #endif
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: stmpe811_worker
- *
- * Description:
- * This is the "bottom half" of the STMPE811 interrupt handler
- *
- ****************************************************************************/
- static void stmpe811_worker(FAR void *arg)
- {
- FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)arg;
- uint8_t regval;
- DEBUGASSERT(priv && priv->config);
- /* Get the global interrupt status */
- regval = stmpe811_getreg8(priv, STMPE811_INT_STA);
- /* Check for a touchscreen interrupt */
- #ifndef CONFIG_STMPE811_TSC_DISABLE
- if ((regval & (INT_TOUCH_DET | INT_FIFO_TH | INT_FIFO_OFLOW)) != 0)
- {
- /* Dispatch the touchscreen interrupt if it was brought into the link */
- #ifdef CONFIG_HAVE_WEAKFUNCTIONS
- if (stmpe811_tscworker)
- #endif
- {
- stmpe811_tscworker(priv, regval);
- }
- stmpe811_putreg8(priv, STMPE811_INT_STA,
- (INT_TOUCH_DET | INT_FIFO_TH | INT_FIFO_OFLOW));
- regval &= ~(INT_TOUCH_DET | INT_FIFO_TH | INT_FIFO_OFLOW);
- }
- #endif
- #if !defined(CONFIG_STMPE811_GPIO_DISABLE) && !defined(CONFIG_STMPE811_GPIOINT_DISABLE)
- if ((regval & INT_GPIO) != 0)
- {
- /* Dispatch the GPIO interrupt if it was brought into the link */
- #ifdef CONFIG_HAVE_WEAKFUNCTIONS
- if (stmpe811_gpioworker)
- #endif
- {
- stmpe811_gpioworker(priv);
- }
- stmpe811_putreg8(priv, STMPE811_INT_STA, INT_GPIO);
- regval &= ~INT_GPIO;
- }
- #endif
- /* Clear any other residual, unhandled pending interrupt */
- if (regval != 0)
- {
- stmpe811_putreg8(priv, STMPE811_INT_STA, regval);
- }
- /* Re-enable the STMPE811 GPIO interrupt */
- priv->config->enable(priv->config, true);
- }
- /****************************************************************************
- * Name: stmpe811_interrupt
- *
- * Description:
- * The STMPE811 interrupt handler
- *
- ****************************************************************************/
- static int stmpe811_interrupt(int irq, FAR void *context, FAR void *arg)
- {
- FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)arg;
- FAR struct stmpe811_config_s *config;
- int ret;
- DEBUGASSERT(priv != NULL && priv->config != NULL);
- config = priv->config;
- /* Disable further interrupts */
- config->enable(config, false);
- /* Check if interrupt work is already queue. If it is already busy, then
- * we already have interrupt processing in the pipeline and we need to do
- * nothing more.
- */
- if (work_available(&priv->work))
- {
- /* Yes.. Transfer processing to the worker thread. Since STMPE811
- * interrupts are disabled while the work is pending, no special
- * action should be required to protect the work queue.
- */
- ret = work_queue(HPWORK, &priv->work, stmpe811_worker, priv, 0);
- if (ret != 0)
- {
- ierr("ERROR: Failed to queue work: %d\n", ret);
- }
- }
- /* Clear any pending interrupts and return success */
- config->clear(config);
- return OK;
- }
- /****************************************************************************
- * Name: stmpe811_checkid
- *
- * Description:
- * Read and verify the STMPE811 chip ID
- *
- ****************************************************************************/
- static int stmpe811_checkid(FAR struct stmpe811_dev_s *priv)
- {
- uint16_t devid = 0;
- /* Read device ID */
- devid = stmpe811_getreg8(priv, STMPE811_CHIP_ID);
- devid = (uint32_t)(devid << 8);
- devid |= (uint32_t)stmpe811_getreg8(priv, STMPE811_CHIP_ID + 1);
- iinfo("devid: %04x\n", devid);
- if (devid != (uint16_t)CHIP_ID)
- {
- /* ID is not Correct */
- return -ENODEV;
- }
- return OK;
- }
- /****************************************************************************
- * Name: stmpe811_reset
- *
- * Description:
- * Reset the STMPE811
- *
- ****************************************************************************/
- static void stmpe811_reset(FAR struct stmpe811_dev_s *priv)
- {
- /* Power Down the STMPE811 */
- stmpe811_putreg8(priv, STMPE811_SYS_CTRL1, SYS_CTRL1_SOFTRESET);
- /* Wait a bit */
- nxsig_usleep(20 * 1000);
- /* Then power on again. All registers will be in their reset state. */
- stmpe811_putreg8(priv, STMPE811_SYS_CTRL1, 0);
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: stmpe811_instantiate
- *
- * Description:
- * Instantiate and configure the STMPE811 device driver to use the provided
- * I2C or SPIdevice instance.
- *
- * Input Parameters:
- * dev - An I2C or SPI driver instance
- * config - Persistent board configuration data
- *
- * Returned Value:
- * A non-zero handle is returned on success. This handle may then be used
- * to configure the STMPE811 driver as necessary. A NULL handle value is
- * returned on failure.
- *
- ****************************************************************************/
- #ifdef CONFIG_STMPE811_SPI
- STMPE811_HANDLE stmpe811_instantiate(FAR struct spi_dev_s *dev,
- FAR struct stmpe811_config_s *config)
- #else
- STMPE811_HANDLE stmpe811_instantiate(FAR struct i2c_master_s *dev,
- FAR struct stmpe811_config_s *config)
- #endif
- {
- FAR struct stmpe811_dev_s *priv;
- uint8_t regval;
- int ret;
- /* Allocate the device state structure */
- #ifdef CONFIG_STMPE811_MULTIPLE
- priv = (FAR struct stmpe811_dev_s *)kmm_zalloc(
- sizeof(struct stmpe811_dev_s));
- if (!priv)
- {
- return NULL;
- }
- /* And save the device structure in the list of STMPE811 so that we can
- * find it later.
- */
- priv->flink = g_stmpe811list;
- g_stmpe811list = priv;
- #else
- /* Use the one-and-only STMPE811 driver instance */
- priv = &g_stmpe811;
- #endif
- /* Initialize the device state structure */
- nxsem_init(&priv->exclsem, 0, 1);
- priv->config = config;
- #ifdef CONFIG_STMPE811_SPI
- priv->spi = dev;
- #else
- priv->i2c = dev;
- #endif
- /* Read and verify the STMPE811 chip ID */
- ret = stmpe811_checkid(priv);
- if (ret < 0)
- {
- #ifdef CONFIG_STMPE811_MULTIPLE
- g_stmpe811list = priv->flink;
- kmm_free(priv);
- #endif
- return NULL;
- }
- /* Generate STMPE811 Software reset */
- stmpe811_reset(priv);
- /* Configure the interrupt output pin to generate interrupts on high or
- * low level.
- */
- regval = stmpe811_getreg8(priv, STMPE811_INT_CTRL);
- #ifdef CONFIG_STMPE811_ACTIVELOW
- regval &= ~INT_CTRL_INT_POLARITY; /* Pin polarity: Active low / falling edge */
- #else
- regval |= INT_CTRL_INT_POLARITY; /* Pin polarity: Active high / rising edge */
- #endif
- #ifdef CONFIG_STMPE811_EDGE
- regval |= INT_CTRL_INT_TYPE; /* Edge interrupt */
- #else
- regval &= ~INT_CTRL_INT_TYPE; /* Level interrupt */
- #endif
- stmpe811_putreg8(priv, STMPE811_INT_CTRL, regval);
- /* Attach the STMPE811 interrupt handler. */
- config->attach(config, stmpe811_interrupt, priv);
- /* Clear any pending interrupts */
- stmpe811_putreg8(priv, STMPE811_INT_STA, INT_ALL);
- config->clear(config);
- config->enable(config, true);
- /* Enable global interrupts */
- regval = stmpe811_getreg8(priv, STMPE811_INT_CTRL);
- regval |= INT_CTRL_GLOBAL_INT;
- stmpe811_putreg8(priv, STMPE811_INT_CTRL, regval);
- /* Return our private data structure as an opaque handle */
- return (STMPE811_HANDLE)priv;
- }
- /****************************************************************************
- * Name: stmpe811_getreg8
- *
- * Description:
- * Read from an 8-bit STMPE811 register
- *
- ****************************************************************************/
- uint8_t stmpe811_getreg8(FAR struct stmpe811_dev_s *priv, uint8_t regaddr)
- {
- /* 8-bit data read sequence:
- * i2c:
- * Start - I2C_Write_Address - STMPE811_Reg_Address -
- * Repeated_Start - I2C_Read_Address - STMPE811_Read_Data - STOP
- * spi:
- * [STMPE811_Reg_Address | 0x80] - Dummy Address - Read Register
- */
- uint8_t regval;
- #ifdef CONFIG_STMPE811_I2C
- int ret;
- struct i2c_msg_s msg[2];
- /* Setup 8-bit STMPE811 address write message */
- msg[0].frequency = priv->config->frequency; /* I2C frequency */
- msg[0].addr = priv->config->address; /* 7-bit address */
- msg[0].flags = 0; /* Write transaction, beginning with START */
- msg[0].buffer = ®addr; /* Transfer from this address */
- msg[0].length = 1; /* Send one byte following the address
- * (no STOP) */
- /* Set up the 8-bit STMPE811 data read message */
- msg[1].frequency = priv->config->frequency; /* I2C frequency */
- msg[1].addr = priv->config->address; /* 7-bit address */
- msg[1].flags = I2C_M_READ; /* Read transaction, beginning with Re-START */
- msg[1].buffer = ®val; /* Transfer to this address */
- msg[1].length = 1; /* Receive one byte following the address
- * (then STOP) */
- /* Perform the transfer */
- ret = I2C_TRANSFER(priv->i2c, msg, 2);
- if (ret < 0)
- {
- ierr("ERROR: I2C_TRANSFER failed: %d\n", ret);
- return 0;
- }
- #else /* CONFIG_STMPE811_SPI */
- SPI_LOCK(priv->spi, true);
- SPI_SETMODE(priv->spi, SPIDEV_MODE0);
- SPI_SETBITS(priv->spi, 8);
- SPI_HWFEATURES(priv->spi, 0);
- SPI_SETFREQUENCY(priv->spi, priv->config->frequency);
- SPI_SELECT(priv->spi, SPIDEV_TOUCHSCREEN(0), true);
- SPI_SEND(priv->spi, regaddr | 0x80); /* Issue a read on the address */
- SPI_SEND(priv->spi, 0); /* Next address (not used) */
- regval = SPI_SEND(priv->spi, 0); /* Read register */
- SPI_SELECT(priv->spi, SPIDEV_TOUCHSCREEN(0), false);
- SPI_LOCK(priv->spi, false);
- #endif
- #ifdef CONFIG_STMPE811_REGDEBUG
- _err("%02x->%02x\n", regaddr, regval);
- #endif
- return regval;
- }
- /****************************************************************************
- * Name: stmpe811_putreg8
- *
- * Description:
- * Write a value to an 8-bit STMPE811 register
- *
- ****************************************************************************/
- void stmpe811_putreg8(FAR struct stmpe811_dev_s *priv,
- uint8_t regaddr, uint8_t regval)
- {
- /* 8-bit data read sequence:
- *
- * Start - I2C_Write_Address - STMPE811_Reg_Address -
- * STMPE811_Write_Data - STOP
- * spi:
- * STMPE811_Reg_Address - Dummy Address - Write Data
- */
- #ifdef CONFIG_STMPE811_I2C
- int ret;
- struct i2c_msg_s msg;
- uint8_t txbuffer[2];
- #ifdef CONFIG_STMPE811_REGDEBUG
- _err("%02x<-%02x\n", regaddr, regval);
- #endif
- /* Setup to the data to be transferred. Two bytes: The STMPE811 register
- * address followed by one byte of data.
- */
- txbuffer[0] = regaddr;
- txbuffer[1] = regval;
- /* Setup 8-bit STMPE811 address write message */
- msg.frequency = priv->config->frequency; /* I2C frequency */
- msg.addr = priv->config->address; /* 7-bit address */
- msg.flags = 0; /* Write transaction, beginning with START */
- msg.buffer = txbuffer; /* Transfer from this address */
- msg.length = 2; /* Send two byte following the address
- * (then STOP) */
- /* Perform the transfer */
- ret = I2C_TRANSFER(priv->i2c, &msg, 1);
- if (ret < 0)
- {
- ierr("ERROR: I2C_TRANSFER failed: %d\n", ret);
- }
- #else /* CONFIG_STMPE811_SPI */
- SPI_LOCK(priv->spi, true);
- SPI_SETMODE(priv->spi, SPIDEV_MODE0);
- SPI_SETBITS(priv->spi, 8);
- SPI_HWFEATURES(priv->spi, 0);
- SPI_SETFREQUENCY(priv->spi, priv->config->frequency);
- SPI_SELECT(priv->spi, SPIDEV_TOUCHSCREEN(0), true);
- SPI_SEND(priv->spi, regaddr); /* Issue a read on the address */
- SPI_SEND(priv->spi, 0); /* Next address (not used) */
- SPI_SEND(priv->spi, regval); /* write register */
- SPI_SELECT(priv->spi, SPIDEV_TOUCHSCREEN(0), false);
- SPI_LOCK(priv->spi, false);
- #endif
- }
- /****************************************************************************
- * Name: stmpe811_getreg16
- *
- * Description:
- * Read 16-bits of data from an STMPE-11 register
- *
- ****************************************************************************/
- uint16_t stmpe811_getreg16(FAR struct stmpe811_dev_s *priv, uint8_t regaddr)
- {
- /* 16-bit data read sequence:
- * i2c:
- * Start - I2C_Write_Address - STMPE811_Reg_Address -
- * Repeated_Start - I2C_Read_Address - STMPE811_Read_Data_1 -
- * STMPE811_Read_Data_2 - STOP
- * spi:
- * 16 bit registers are MSB.
- * [STMPE811_Reg_Address | 0x80] - [STMPE811_Reg_Address + 1 | 0x80] -
- * Read Register - Read Register + 1
- */
- uint8_t rxbuffer[2];
- #ifdef CONFIG_STMPE811_I2C
- int ret;
- struct i2c_msg_s msg[2];
- /* Setup 8-bit STMPE811 address write message */
- msg[0].frequency = priv->config->frequency; /* I2C frequency */
- msg[0].addr = priv->config->address; /* 7-bit address */
- msg[0].flags = 0; /* Write transaction, beginning with START */
- msg[0].buffer = ®addr; /* Transfer from this address */
- msg[0].length = 1; /* Send one byte following the address
- * (no STOP) */
- /* Set up the 8-bit STMPE811 data read message */
- msg[1].frequency = priv->config->frequency; /* I2C frequency */
- msg[1].addr = priv->config->address; /* 7-bit address */
- msg[1].flags = I2C_M_READ; /* Read transaction, beginning with Re-START */
- msg[1].buffer = rxbuffer; /* Transfer to this address */
- msg[1].length = 2; /* Receive two bytes following the address
- * (then STOP) */
- /* Perform the transfer */
- ret = I2C_TRANSFER(priv->i2c, msg, 2);
- if (ret < 0)
- {
- ierr("ERROR: I2C_TRANSFER failed: %d\n", ret);
- return 0;
- }
- #else /* CONFIG_STMPE811_SPI */
- SPI_LOCK(priv->spi, true);
- SPI_SETMODE(priv->spi, SPIDEV_MODE0);
- SPI_SETBITS(priv->spi, 8);
- SPI_HWFEATURES(priv->spi, 0);
- SPI_SETFREQUENCY(priv->spi, priv->config->frequency);
- SPI_SELECT(priv->spi, SPIDEV_TOUCHSCREEN(0), true);
- SPI_SEND(priv->spi, regaddr | 0x80); /* Issue a read on the address */
- SPI_SEND(priv->spi, regaddr + 1); /* Next address */
- rxbuffer[0] = SPI_SEND(priv->spi, 0); /* Read MSB */
- rxbuffer[1] = SPI_SEND(priv->spi, 0); /* Read LSB */
- SPI_SELECT(priv->spi, SPIDEV_TOUCHSCREEN(0), true);
- SPI_LOCK(priv->spi, false);
- #endif
- #ifdef CONFIG_STMPE811_REGDEBUG
- _err("%02x->%02x%02x\n", regaddr, rxbuffer[0], rxbuffer[1]);
- #endif
- return (uint16_t)rxbuffer[0] << 8 | (uint16_t)rxbuffer[1];
- }
- #endif /* CONFIG_INPUT && CONFIG_INPUT_STMPE811 */
|