mirror of
https://github.com/apache/nuttx.git
synced 2026-05-30 13:27:01 +08:00
SAMA5 ADC/Touchscreen: A little more progress. Still not complete
This commit is contained in:
@@ -289,6 +289,8 @@
|
|||||||
#define ADC_INT_EOC7 (1 << 9) /* Bit 9: End of Conversion 9 */
|
#define ADC_INT_EOC7 (1 << 9) /* Bit 9: End of Conversion 9 */
|
||||||
#define ADC_INT_EOC7 (1 << 10) /* Bit 10: End of Conversion 10 */
|
#define ADC_INT_EOC7 (1 << 10) /* Bit 10: End of Conversion 10 */
|
||||||
#define ADC_INT_EOC7 (1 << 11) /* Bit 11: End of Conversion 11 */
|
#define ADC_INT_EOC7 (1 << 11) /* Bit 11: End of Conversion 11 */
|
||||||
|
#define ADC_INT_EOCALL (0x00000fff)
|
||||||
|
|
||||||
#define ADC_INT_XRDY (1 << 20) /* Bit 20: TS Measure XPOS Ready Interrupt */
|
#define ADC_INT_XRDY (1 << 20) /* Bit 20: TS Measure XPOS Ready Interrupt */
|
||||||
#define ADC_INT_YRDY (1 << 21) /* Bit 21: TS Measure YPOS Ready Interrupt */
|
#define ADC_INT_YRDY (1 << 21) /* Bit 21: TS Measure YPOS Ready Interrupt */
|
||||||
#define ADC_INT_PRDY (1 << 22) /* Bit 22: TS Measure Pressure Ready Interrupt */
|
#define ADC_INT_PRDY (1 << 22) /* Bit 22: TS Measure Pressure Ready Interrupt */
|
||||||
|
|||||||
+266
-81
@@ -1,7 +1,6 @@
|
|||||||
/************************************************************************************
|
/************************************************************************************
|
||||||
* arch/arm/src/sama5/sam_adc.c
|
* arch/arm/src/sama5/sam_adc.c
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* Copyright (C) 2013 Gregory Nutt. All rights reserved.
|
* Copyright (C) 2013 Gregory Nutt. All rights reserved.
|
||||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||||
*
|
*
|
||||||
@@ -65,6 +64,64 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Pre-processor Definitions
|
* Pre-processor Definitions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
/* Get the set of channel interrupts to enable */
|
||||||
|
|
||||||
|
#define SAMA5_CHAN0_ENABLE 0
|
||||||
|
#define SAMA5_CHAN1_ENABLE 0
|
||||||
|
#define SAMA5_CHAN2_ENABLE 0
|
||||||
|
#define SAMA5_CHAN3_ENABLE 0
|
||||||
|
#define SAMA5_CHAN4_ENABLE 0
|
||||||
|
#define SAMA5_CHAN5_ENABLE 0
|
||||||
|
#define SAMA5_CHAN6_ENABLE 0
|
||||||
|
#define SAMA5_CHAN7_ENABLE 0
|
||||||
|
#define SAMA5_CHAN8_ENABLE 0
|
||||||
|
#define SAMA5_CHAN9_ENABLE 0
|
||||||
|
#define SAMA5_CHAN10_ENABLE 0
|
||||||
|
#define SAMA5_CHAN11_ENABLE 0
|
||||||
|
|
||||||
|
#if defined(CONFIG_SAMA5_ADC_CHAN0)
|
||||||
|
# undef SAMA5_CHAN0_ENABLE
|
||||||
|
# define SAMA5_CHAN0_ENABLE ADC_INT_EOC0
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN1)
|
||||||
|
# undef SAMA5_CHAN1_ENABLE
|
||||||
|
# define SAMA5_CHAN1_ENABLE ADC_INT_EOC1
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN2)
|
||||||
|
# undef SAMA5_CHAN2_ENABLE
|
||||||
|
# define SAMA5_CHAN2_ENABLE ADC_INT_EOC2
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN3)
|
||||||
|
# undef SAMA5_CHAN3_ENABLE
|
||||||
|
# define SAMA5_CHAN3_ENABLE ADC_INT_EOC3
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN4)
|
||||||
|
# undef SAMA5_CHAN4_ENABLE
|
||||||
|
# define SAMA5_CHAN4_ENABLE ADC_INT_EOC4
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN5)
|
||||||
|
# undef SAMA5_CHAN5_ENABLE
|
||||||
|
# define SAMA5_CHAN5_ENABLE ADC_INT_EOC5
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN6)
|
||||||
|
# undef SAMA5_CHAN6_ENABLE
|
||||||
|
# define SAMA5_CHAN6_ENABLE ADC_INT_EOC6
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN7)
|
||||||
|
# undef SAMA5_CHAN7_ENABLE
|
||||||
|
# define SAMA5_CHAN7_ENABLE ADC_INT_EOC7
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN8)
|
||||||
|
# undef SAMA5_CHAN8_ENABLE
|
||||||
|
# define SAMA5_CHAN8_ENABLE ADC_INT_EOC8
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN9)
|
||||||
|
# undef SAMA5_CHAN9_ENABLE
|
||||||
|
# define SAMA5_CHAN9_ENABLE ADC_INT_EOC9
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN10)
|
||||||
|
# undef SAMA5_CHAN10_ENABLE
|
||||||
|
# define SAMA5_CHAN10_ENABLE ADC_INT_EOC10
|
||||||
|
#elif defined(CONFIG_SAMA5_ADC_CHAN11)
|
||||||
|
# undef SAMA5_CHAN11_ENABLE
|
||||||
|
# define SAMA5_CHAN11_ENABLE ADC_INT_EOC11
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SAMA5_CHAN_ENABLE \
|
||||||
|
(SAMA5_CHAN0_ENABLE || SAMA5_CHAN1_ENABLE || SAMA5_CHAN2_ENABLE || \
|
||||||
|
SAMA5_CHAN3_ENABLE || SAMA5_CHAN4_ENABLE || SAMA5_CHAN5_ENABLE || \
|
||||||
|
SAMA5_CHAN6_ENABLE || SAMA5_CHAN7_ENABLE || SAMA5_CHAN8_ENABLE || \
|
||||||
|
SAMA5_CHAN9_ENABLE || SAMA5_CHAN10_ENABLE || SAMA5_CHAN11_ENABLE)
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Types
|
* Private Types
|
||||||
@@ -74,13 +131,17 @@
|
|||||||
|
|
||||||
struct sam_adc_s
|
struct sam_adc_s
|
||||||
{
|
{
|
||||||
|
#ifdef SAMA5_ADC_HAVE_CHANNELS
|
||||||
|
struct adc_dev_s dev; /* The external via of the ADC device */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Debug stuff */
|
/* Debug stuff */
|
||||||
|
|
||||||
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
||||||
bool wrlast; /* Last was a write */
|
bool wrlast; /* Last was a write */
|
||||||
uintptr_t addrlast; /* Last address */
|
uintptr_t addrlast; /* Last address */
|
||||||
uint32_t vallast; /* Last value */
|
uint32_t vallast; /* Last value */
|
||||||
int ntimes; /* Number of times */
|
int ntimes; /* Number of times */
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,31 +151,32 @@ struct sam_adc_s
|
|||||||
/* Register operations ******************************************************/
|
/* Register operations ******************************************************/
|
||||||
|
|
||||||
#if defined(CONFIG_SAMA5_ADC_REGDEBUG) && defined(CONFIG_DEBUG)
|
#if defined(CONFIG_SAMA5_ADC_REGDEBUG) && defined(CONFIG_DEBUG)
|
||||||
static bool sam_adc_checkreg(struct sam_gmac_s *priv, bool wr,
|
static bool sam_adc_checkreg(struct sam_adc_s *priv, bool wr,
|
||||||
uint32_t regval, uintptr_t address);
|
uint32_t regval, uintptr_t address);
|
||||||
static uint32_t sam_adc_getreg(struct sam_gmac_s *priv, uintptr_t addr);
|
|
||||||
static void sam_adc_putreg(struct sam_gmac_s *priv, uintptr_t addr, uint32_t val);
|
|
||||||
#else
|
|
||||||
# define sam_adc_getreg(priv,addr) getreg32(addr)
|
|
||||||
# define sam_adc_putreg(priv,addr,val) putreg32(val,addr)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ADC interrupt handling */
|
/* ADC interrupt handling */
|
||||||
|
|
||||||
|
#ifdef SAMA5_ADC_HAVE_CHANNELS
|
||||||
|
static void sam_adc_endconversion(struct sam_adc_s *priv, uint32_t pending);
|
||||||
|
#endif
|
||||||
static int sam_adc_interrupt(int irq, void *context);
|
static int sam_adc_interrupt(int irq, void *context);
|
||||||
|
|
||||||
/* ADC methods */
|
/* ADC methods */
|
||||||
|
|
||||||
static void sam_adc_reset(FAR struct adc_dev_s *dev);
|
#ifdef SAMA5_ADC_HAVE_CHANNELS
|
||||||
static int sam_adc_setup(FAR struct adc_dev_s *dev);
|
static void sam_adc_reset(struct adc_dev_s *dev);
|
||||||
static void sam_adc_shutdown(FAR struct adc_dev_s *dev);
|
static int sam_adc_setup(struct adc_dev_s *dev);
|
||||||
static void sam_adc_rxint(FAR struct adc_dev_s *dev, bool enable);
|
static void sam_adc_shutdown(struct adc_dev_s *dev);
|
||||||
static int sam_adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg);
|
static void sam_adc_rxint(struct adc_dev_s *dev, bool enable);
|
||||||
|
static int sam_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Data
|
* Private Data
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef SAMA5_ADC_HAVE_CHANNELS
|
||||||
/* ADC lower half device operations */
|
/* ADC lower half device operations */
|
||||||
|
|
||||||
static const struct adc_ops_s g_adcops =
|
static const struct adc_ops_s g_adcops =
|
||||||
@@ -125,20 +187,21 @@ static const struct adc_ops_s g_adcops =
|
|||||||
.ao_rxint = sam_adc_rxint,
|
.ao_rxint = sam_adc_rxint,
|
||||||
.ao_ioctl = sam_adc_ioctl,
|
.ao_ioctl = sam_adc_ioctl,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ADC internal state */
|
/* ADC internal state */
|
||||||
|
|
||||||
static struct sam_adc_s g_adcpriv =
|
static struct sam_adc_s g_adcpriv;
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
|
#ifdef SAMA5_ADC_HAVE_CHANNELS
|
||||||
/* ADC device instance */
|
/* ADC device instance */
|
||||||
|
|
||||||
static struct adc_dev_s g_adcdev =
|
static struct adc_dev_s g_adcdev =
|
||||||
{
|
{
|
||||||
.ad_ops = &g_adcops,
|
.ad_ops = &g_adcops,
|
||||||
.ad_priv = &g_adcpriv,
|
.ad_priv = &g_adcpriv.dev,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Functions
|
* Private Functions
|
||||||
@@ -161,7 +224,7 @@ static struct adc_dev_s g_adcdev =
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
||||||
static bool sam_adc_checkreg(struct sam_gmac_s *priv, bool wr,
|
static bool sam_adc_checkreg(struct sam_adc_s *priv, bool wr,
|
||||||
uint32_t regval, uintptr_t address)
|
uint32_t regval, uintptr_t address)
|
||||||
{
|
{
|
||||||
if (wr == priv->wrlast && /* Same kind of access? */
|
if (wr == priv->wrlast && /* Same kind of access? */
|
||||||
@@ -198,48 +261,7 @@ static bool sam_adc_checkreg(struct sam_gmac_s *priv, bool wr,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
#ifdef SAMA5_ADC_HAVE_CHANNELS
|
||||||
* Name: sam_adc_getreg
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* Read any 32-bit register using an absolute address.
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
|
||||||
static uint32_t sam_adc_getreg(struct sam_gmac_s *priv, uintptr_t address)
|
|
||||||
{
|
|
||||||
uint32_t regval = getreg32(address);
|
|
||||||
|
|
||||||
if (sam_adc_checkreg(priv, false, regval, address))
|
|
||||||
{
|
|
||||||
lldbg("%08x->%08x\n", address, regval);
|
|
||||||
}
|
|
||||||
|
|
||||||
return regval;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Name: sam_adc_putreg
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* Write to any 32-bit register using an absolute address.
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
|
||||||
static void sam_adc_putreg(struct sam_gmac_s *priv, uintptr_t address,
|
|
||||||
uint32_t regval)
|
|
||||||
{
|
|
||||||
if (sam_adc_checkreg(priv, true, regval, address))
|
|
||||||
{
|
|
||||||
lldbg("%08x<-%08x\n", address, regval);
|
|
||||||
}
|
|
||||||
|
|
||||||
putreg32(regval, address);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: sam_adc_reset
|
* Name: sam_adc_reset
|
||||||
@@ -250,15 +272,20 @@ static void sam_adc_putreg(struct sam_gmac_s *priv, uintptr_t address,
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void sam_adc_reset(FAR struct adc_dev_s *dev)
|
static void sam_adc_reset(struct adc_dev_s *dev)
|
||||||
{
|
{
|
||||||
FAR struct sam_adc_s *priv = (FAR struct sam_adc_s *)dev->ad_priv;
|
struct sam_adc_s *priv = (struct sam_adc_s *)dev->ad_priv;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
|
|
||||||
flags = irqsave();
|
/* Reset the ADC controller */
|
||||||
#warning Missing logic
|
|
||||||
|
|
||||||
|
flags = irqsave();
|
||||||
|
sam_adc_putreg(priv, SAM_ADC_CR, ADC_CR_SWRST);
|
||||||
|
|
||||||
|
/* Reset Mode Register */
|
||||||
|
|
||||||
|
sam_adc_putreg(priv, SAM_ADC_MR, 0);
|
||||||
irqrestore(flags);
|
irqrestore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,9 +300,9 @@ static void sam_adc_reset(FAR struct adc_dev_s *dev)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static int sam_adc_setup(FAR struct adc_dev_s *dev)
|
static int sam_adc_setup(struct adc_dev_s *dev)
|
||||||
{
|
{
|
||||||
FAR struct sam_adc_s *priv = (FAR struct sam_adc_s *)dev->ad_priv;
|
struct sam_adc_s *priv = (struct sam_adc_s *)dev->ad_priv;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Attach the ADC interrupt */
|
/* Attach the ADC interrupt */
|
||||||
@@ -302,15 +329,15 @@ static int sam_adc_setup(FAR struct adc_dev_s *dev)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void sam_adc_shutdown(FAR struct adc_dev_s *dev)
|
static void sam_adc_shutdown(struct adc_dev_s *dev)
|
||||||
{
|
{
|
||||||
FAR struct sam_adc_s *priv = (FAR struct sam_adc_s *)dev->ad_priv;
|
struct sam_adc_s *priv = (struct sam_adc_s *)dev->ad_priv;
|
||||||
|
|
||||||
/* Disable ADC interrupts, both at the level of the ADC device and at the
|
/* Disable ADC interrupts, both at the level of the ADC device and at the
|
||||||
* level of the NVIC.
|
* level of the NVIC.
|
||||||
*/
|
*/
|
||||||
#warning Missing logic
|
|
||||||
|
|
||||||
|
sam_adc_putreg32(priv, SAM_ADC_IDR, ADC_TSD_INTS);
|
||||||
up_disable_irq(SAM_IRQ_ADC);
|
up_disable_irq(SAM_IRQ_ADC);
|
||||||
|
|
||||||
/* Then detach the ADC interrupt handler. */
|
/* Then detach the ADC interrupt handler. */
|
||||||
@@ -326,18 +353,23 @@ static void sam_adc_shutdown(FAR struct adc_dev_s *dev)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void sam_adc_rxint(FAR struct adc_dev_s *dev, bool enable)
|
static void sam_adc_rxint(struct adc_dev_s *dev, bool enable)
|
||||||
{
|
{
|
||||||
FAR struct sam_adc_s *priv = (FAR struct sam_adc_s *)dev->ad_priv;
|
struct sam_adc_s *priv = (struct sam_adc_s *)dev->ad_priv;
|
||||||
|
|
||||||
/* Are we enabling or disabling? */
|
/* Are we enabling or disabling? */
|
||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
#warning Missing logic
|
/* Enable channel interrupts */
|
||||||
|
|
||||||
|
sam_adc_putreg32(priv, SAM_ADC_IER, SAMA5_CHAN_ENABLE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#warning Missing logic
|
/* Disable channel interrupts */
|
||||||
|
|
||||||
|
sam_adc_putreg32(priv, SAM_ADC_IDR, ADC_INT_EOCALL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,13 +381,44 @@ static void sam_adc_rxint(FAR struct adc_dev_s *dev, bool enable)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static int sam_adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg)
|
static int sam_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
/* No ioctl commands supported */
|
/* No ioctl commands supported */
|
||||||
|
|
||||||
return -ENOTTY;
|
return -ENOTTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_adc_endconversion
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* End of conversion interrupt handler
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static void sam_adc_endconversion(struct sam_adc_s *priv, uint32_t pending)
|
||||||
|
{
|
||||||
|
uint32_t regval;
|
||||||
|
int chan;
|
||||||
|
|
||||||
|
/* Check for the end of conversion event on each channel */
|
||||||
|
|
||||||
|
for (chan = 0; chan < SAM_ADC_NCHANNELS && pending != 0; chan++)
|
||||||
|
{
|
||||||
|
uint32_t bit = ADC_INT_EOC(chan);
|
||||||
|
if ((pending & bit) != 0)
|
||||||
|
{
|
||||||
|
/* Read the ADC sample and pass it to the upper half */
|
||||||
|
|
||||||
|
regval = sam_adc_getreg(priv, SAM_ADC_CDR(chan));
|
||||||
|
ret = adc_receive(&priv->dev, chan, regval & ADC_CDR_DATA_MASK);
|
||||||
|
pending &= ~bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* SAMA5_ADC_HAVE_CHANNELS */
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: sam_adc_interrupt
|
* Name: sam_adc_interrupt
|
||||||
*
|
*
|
||||||
@@ -366,11 +429,46 @@ static int sam_adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg)
|
|||||||
|
|
||||||
static int sam_adc_interrupt(int irq, void *context)
|
static int sam_adc_interrupt(int irq, void *context)
|
||||||
{
|
{
|
||||||
FAR struct sam_adc_s *priv = (FAR struct sam_adc_s *)g_adcdev.ad_priv;
|
struct sam_adc_s *priv = (struct sam_adc_s *)g_adcdev.ad_priv;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
|
struct sam_adc_s *priv = &g_adcpriv;
|
||||||
|
uint32_t isr;
|
||||||
|
uint32_t imr;
|
||||||
|
uint32_t pending;
|
||||||
|
|
||||||
#warning Missing logic
|
/* Get the set of unmasked, pending ADC interrupts */
|
||||||
|
|
||||||
|
isr = sam_adc_getreg(priv, SAM_ADC_ISR);
|
||||||
|
imr = sam_adc_getreg(priv, SAM_ADC_IMR);
|
||||||
|
pending = isr & imr;
|
||||||
|
|
||||||
|
/* Handle pending touchscreen interrupts */
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_TOUCHSCREEN
|
||||||
|
if ((pending & ADC_TSD_INTS) != 0)
|
||||||
|
{
|
||||||
|
/* Let the touchscreen handle its interrupts */
|
||||||
|
|
||||||
|
sam_tsd_interrupt(pending);
|
||||||
|
pending &= ~ADC_TSD_INTS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SAMA5_ADC_HAVE_CHANNELS
|
||||||
|
/* Check for end-of-conversion interrupts */
|
||||||
|
|
||||||
|
if ((pending & ADC_INT_EOCALL) != 0)
|
||||||
|
{
|
||||||
|
/* Let the touchscreen handle its interrupts */
|
||||||
|
|
||||||
|
sam_adc_endconversion(pending);
|
||||||
|
pending &= ~ADC_INT_EOCALL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Make sure that all interrupts were handled */
|
||||||
|
|
||||||
|
DEBUGASSERT(pending == 0);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,12 +487,54 @@ static int sam_adc_interrupt(int irq, void *context)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
FAR struct adc_dev_s *sam_adc_initialize(void)
|
struct adc_dev_s *sam_adc_initialize(void)
|
||||||
{
|
{
|
||||||
/* Enable the ADC peripheral clock*/
|
/* Enable the ADC peripheral clock*/
|
||||||
|
|
||||||
sam_adc_enableclk();
|
sam_adc_enableclk();
|
||||||
|
|
||||||
|
/* Configure ADC pins */
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN0
|
||||||
|
sam_configpio(PIO_ADC_AD0);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN1
|
||||||
|
sam_configpio(PIO_ADC_AD1);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN2
|
||||||
|
sam_configpio(PIO_ADC_AD2);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN3
|
||||||
|
sam_configpio(PIO_ADC_AD3);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN4
|
||||||
|
sam_configpio(PIO_ADC_AD4);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN5
|
||||||
|
sam_configpio(PIO_ADC_AD5);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN6
|
||||||
|
sam_configpio(PIO_ADC_AD6);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN7
|
||||||
|
sam_configpio(PIO_ADC_AD7);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN8
|
||||||
|
sam_configpio(PIO_ADC_AD8);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN9
|
||||||
|
sam_configpio(PIO_ADC_AD9);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN10
|
||||||
|
sam_configpio(PIO_ADC_AD10);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_CHAN11
|
||||||
|
sam_configpio(PIO_ADC_AD11);
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
sam_configpio(PIO_ADC_TRG);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Reset the ADC controller */
|
/* Reset the ADC controller */
|
||||||
|
|
||||||
sam_adc_putreg(priv, SAM_ADC_CR, ADC_CR_SWRST);
|
sam_adc_putreg(priv, SAM_ADC_CR, ADC_CR_SWRST);
|
||||||
@@ -408,4 +548,49 @@ FAR struct adc_dev_s *sam_adc_initialize(void)
|
|||||||
return &g_adcdev;
|
return &g_adcdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_adc_getreg
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Read any 32-bit register using an absolute address.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
||||||
|
static uint32_t sam_adc_getreg(ADC_HANDLE handle, uintptr_t address)
|
||||||
|
{
|
||||||
|
struct sam_adc_s *priv = (struct sam_adc_s *)handle;
|
||||||
|
uint32_t regval = getreg32(address);
|
||||||
|
|
||||||
|
if (sam_adc_checkreg(priv, false, regval, address))
|
||||||
|
{
|
||||||
|
lldbg("%08x->%08x\n", address, regval);
|
||||||
|
}
|
||||||
|
|
||||||
|
return regval;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_adc_putreg
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Write to any 32-bit register using an absolute address.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
||||||
|
void sam_adc_putreg(ADC_HANDLE handle, uintptr_t address, uint32_t regval)
|
||||||
|
{
|
||||||
|
struct sam_adc_s *priv = (struct sam_adc_s *)handle;
|
||||||
|
|
||||||
|
if (sam_adc_checkreg(priv, true, regval, address))
|
||||||
|
{
|
||||||
|
lldbg("%08x<-%08x\n", address, regval);
|
||||||
|
}
|
||||||
|
|
||||||
|
putreg32(regval, address);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* CONFIG_SAMA5_ADC */
|
#endif /* CONFIG_SAMA5_ADC */
|
||||||
|
|||||||
@@ -50,6 +50,40 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
/* Configuration ************************************************************/
|
/* Configuration ************************************************************/
|
||||||
|
|
||||||
|
#ifndef CONFIG_DEBUG
|
||||||
|
# undef CONFIG_SAMA5_ADC_REGDEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ADC channels 0-3 or 0-4 are not available to the ADC driver if touchscreen
|
||||||
|
* support is enabled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_TOUCHSCREEN
|
||||||
|
# undef CONFIG_SAMA5_ADC_CHAN0
|
||||||
|
# undef CONFIG_SAMA5_ADC_CHAN1
|
||||||
|
# undef CONFIG_SAMA5_ADC_CHAN2
|
||||||
|
# undef CONFIG_SAMA5_ADC_CHAN3
|
||||||
|
# ifdef CONFIG_SAMA5_TOUCHSCREEN_5WIRE
|
||||||
|
# undef CONFIG_SAMA5_ADC_CHAN4
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Do we have any ADC channels enabled? If not, then the ADC driver may
|
||||||
|
* still need to exist to support the touchscreen.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef SAMA5_ADC_HAVE_CHANNELS
|
||||||
|
#if defined(CONFIG_SAMA5_ADC_CHAN0) || defined(CONFIG_SAMA5_ADC_CHAN1) || \
|
||||||
|
defined(CONFIG_SAMA5_ADC_CHAN2) || defined(CONFIG_SAMA5_ADC_CHAN3) || \
|
||||||
|
defined(CONFIG_SAMA5_ADC_CHAN4) || defined(CONFIG_SAMA5_ADC_CHAN5) || \
|
||||||
|
defined(CONFIG_SAMA5_ADC_CHAN6) || defined(CONFIG_SAMA5_ADC_CHAN7) || \
|
||||||
|
defined(CONFIG_SAMA5_ADC_CHAN8) || defined(CONFIG_SAMA5_ADC_CHAN9) || \
|
||||||
|
defined(CONFIG_SAMA5_ADC_CHAN10) || defined(CONFIG_SAMA5_ADC_CHAN11)
|
||||||
|
# define SAMA5_ADC_HAVE_CHANNELS 1
|
||||||
|
#elif !defined(CONFIG_SAMA5_TOUCHSCREEN)
|
||||||
|
# error "No ADC channels nor touchscreen"
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Types
|
* Public Types
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@@ -88,6 +122,35 @@ FAR struct adc_dev_s *sam_adcinitialize(void);
|
|||||||
* Interfaces exported from the ADC to the touchscreen driver
|
* Interfaces exported from the ADC to the touchscreen driver
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_adc_getreg
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Read any 32-bit register using an absolute address.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
||||||
|
uint32_t sam_adc_getreg(FAR struct adc_dev_s *, uintptr_t address)
|
||||||
|
#else
|
||||||
|
# define sam_adc_getreg(handle,addr) getreg32(addr)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_adc_putreg
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Write to any 32-bit register using an absolute address.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
|
||||||
|
void sam_adc_putreg(FAR struct adc_dev_s *dev, uintptr_t address,
|
||||||
|
uint32_t regval)
|
||||||
|
#else
|
||||||
|
# define sam_adc_putreg(handle,addr,val) putreg32(val,addr)
|
||||||
|
#endif
|
||||||
|
|
||||||
#undef EXTERN
|
#undef EXTERN
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ enum sam_contact_3
|
|||||||
CONTACT_UP, /* Contact lost */
|
CONTACT_UP, /* Contact lost */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This structure describes the results of one ADS7843E sample */
|
/* This structure describes the results of one touchscreen sample */
|
||||||
|
|
||||||
struct sam_sample_s
|
struct sam_sample_s
|
||||||
{
|
{
|
||||||
@@ -105,11 +105,11 @@ struct sam_sample_s
|
|||||||
uint16_t y; /* Measured Y position */
|
uint16_t y; /* Measured Y position */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This structure describes the state of one ADS7843E driver instance */
|
/* This structure describes the state of one touchscreen driver instance */
|
||||||
|
|
||||||
struct sam_dev_s
|
struct sam_dev_s
|
||||||
{
|
{
|
||||||
uint8_t nwaiters; /* Number of threads waiting for ADS7843E data */
|
uint8_t nwaiters; /* Number of threads waiting for touchscreen data */
|
||||||
uint8_t id; /* Current touch point ID */
|
uint8_t id; /* Current touch point ID */
|
||||||
volatile bool penchange; /* An unreported event is buffered */
|
volatile bool penchange; /* An unreported event is buffered */
|
||||||
uint16_t threshx; /* Thresholding X value */
|
uint16_t threshx; /* Thresholding X value */
|
||||||
@@ -117,6 +117,7 @@ struct sam_dev_s
|
|||||||
sem_t devsem; /* Manages exclusive access to this structure */
|
sem_t devsem; /* Manages exclusive access to this structure */
|
||||||
sem_t waitsem; /* Used to wait for the availability of data */
|
sem_t waitsem; /* Used to wait for the availability of data */
|
||||||
|
|
||||||
|
struct adc_dev_s *dev; /* ADC device handle */
|
||||||
struct work_s work; /* Supports the interrupt handling "bottom half" */
|
struct work_s work; /* Supports the interrupt handling "bottom half" */
|
||||||
struct sam_sample_s sample; /* Last sampled touch point data */
|
struct sam_sample_s sample; /* Last sampled touch point data */
|
||||||
WDOG_ID wdog; /* Poll the position while the pen is down */
|
WDOG_ID wdog; /* Poll the position while the pen is down */
|
||||||
@@ -127,7 +128,7 @@ struct sam_dev_s
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CONFIG_DISABLE_POLL
|
#ifndef CONFIG_DISABLE_POLL
|
||||||
struct pollfd *fds[CONFIG_ADS7843E_NPOLLWAITERS];
|
struct pollfd *fds[CONFIG_SAMA5_TSD_NPOLLWAITERS];
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -198,14 +199,14 @@ static void sam_notify(FAR struct sam_dev_s *priv)
|
|||||||
|
|
||||||
if (priv->nwaiters > 0)
|
if (priv->nwaiters > 0)
|
||||||
{
|
{
|
||||||
/* After posting this semaphore, we need to exit because the ADS7843E
|
/* After posting this semaphore, we need to exit because the touchscreen
|
||||||
* is no longer available.
|
* is no longer available.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sem_post(&priv->waitsem);
|
sem_post(&priv->waitsem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If there are threads waiting on poll() for ADS7843E data to become available,
|
/* If there are threads waiting on poll() for touchscreen data to become available,
|
||||||
* then wake them up now. NOTE: we wake up all waiting threads because we
|
* 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,
|
* 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.
|
* then some make end up blocking after all.
|
||||||
@@ -242,7 +243,7 @@ static int sam_sample(FAR struct sam_dev_s *priv,
|
|||||||
|
|
||||||
flags = irqsave();
|
flags = irqsave();
|
||||||
|
|
||||||
/* Is there new ADS7843E sample data available? */
|
/* Is there new touchscreen sample data available? */
|
||||||
|
|
||||||
if (priv->penchange)
|
if (priv->penchange)
|
||||||
{
|
{
|
||||||
@@ -368,10 +369,11 @@ static int sam_schedule(FAR struct sam_dev_s *priv)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Disable further interrupts. touchscreen ADC interrupts will be re-enabled
|
/* Disable further touchscreen interrupts. Touchscreen interrupts will be
|
||||||
* after the worker thread executes.
|
* re-enabled after the worker thread executes.
|
||||||
*/
|
*/
|
||||||
#warning "Missing logic"
|
|
||||||
|
sam_adc_putreg32(priv->dev, SAM_ADC_IDR, ADC_TSD_INTS);
|
||||||
|
|
||||||
/* Disable the watchdog timer. It will be re-enabled in the worker thread
|
/* Disable the watchdog timer. It will be re-enabled in the worker thread
|
||||||
* while the pen remains down.
|
* while the pen remains down.
|
||||||
@@ -545,19 +547,16 @@ static void sam_bottomhalf(FAR void *arg)
|
|||||||
priv->sample.id = priv->id;
|
priv->sample.id = priv->id;
|
||||||
priv->penchange = true;
|
priv->penchange = true;
|
||||||
|
|
||||||
/* Notify any waiters that new ADS7843E data is available */
|
/* Notify any waiters that new touchscreen data is available */
|
||||||
|
|
||||||
sam_notify(priv);
|
sam_notify(priv);
|
||||||
|
|
||||||
/* Exit, re-enabling ADS7843E interrupts */
|
/* Exit, re-enabling touchscreen interrupts */
|
||||||
|
|
||||||
ignored:
|
ignored:
|
||||||
|
/* Re-enable touchscreen interrupts. */
|
||||||
|
|
||||||
/* Re-enable the PENIRQ interrupts */
|
sam_adc_putreg32(priv->dev, SAM_ADC_IER, ADC_TSD_INTS);
|
||||||
#warning Missing logic
|
|
||||||
|
|
||||||
/* Re-enable the PENIRQ interrupt at the MCU's interrupt controller */
|
|
||||||
#warning Missing logic
|
|
||||||
|
|
||||||
/* Release our lock on the state structure */
|
/* Release our lock on the state structure */
|
||||||
|
|
||||||
@@ -658,7 +657,7 @@ static ssize_t sam_read(FAR struct file *filep, FAR char *buffer, size_t len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In any event, we now have sampled ADS7843E data that we can report
|
/* In any event, we now have sampled touchscreen data that we can report
|
||||||
* to the caller.
|
* to the caller.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -870,9 +869,9 @@ errout:
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
int sam_tsd_register(int minor)
|
int sam_tsd_register(struct adc_dev_s *dev, int minor)
|
||||||
{
|
{
|
||||||
FAR struct sam_dev_s *priv = &g_tsd;
|
struct sam_dev_s *priv = &g_tsd;
|
||||||
char devname[DEV_NAMELEN];
|
char devname[DEV_NAMELEN];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -880,11 +879,12 @@ int sam_tsd_register(int minor)
|
|||||||
|
|
||||||
/* Debug-only sanity checks */
|
/* Debug-only sanity checks */
|
||||||
|
|
||||||
DEBUGASSERT(minor >= 0 && minor < 100);
|
DEBUGASSERT(dev && minor >= 0 && minor < 100);
|
||||||
|
|
||||||
/* Initialize the touchscreen device driver instance */
|
/* Initialize the touchscreen device driver instance */
|
||||||
|
|
||||||
memset(priv, 0, sizeof(struct sam_dev_s));
|
memset(priv, 0, sizeof(struct sam_dev_s));
|
||||||
|
priv->dev = dev; /* Save the ADC device handle */
|
||||||
priv->wdog = wd_create(); /* Create a watchdog timer */
|
priv->wdog = wd_create(); /* Create a watchdog timer */
|
||||||
priv->threshx = INVALID_THRESHOLD; /* Initialize thresholding logic */
|
priv->threshx = INVALID_THRESHOLD; /* Initialize thresholding logic */
|
||||||
priv->threshy = INVALID_THRESHOLD; /* Initialize thresholding logic */
|
priv->threshy = INVALID_THRESHOLD; /* Initialize thresholding logic */
|
||||||
@@ -945,20 +945,25 @@ errout_with_priv:
|
|||||||
*
|
*
|
||||||
* Returned Value:
|
* Returned Value:
|
||||||
* None
|
* None
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
void sam_tsd_interrupt(void)
|
void sam_tsd_interrupt(void)
|
||||||
{
|
{
|
||||||
FAR struct sam_dev_s *priv = &g_tsd;
|
struct sam_dev_s *priv = &g_tsd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* Disable further touchscreen interrupts */
|
||||||
|
|
||||||
|
sam_adc_putreg32(priv->dev, SAM_ADC_IDR, ADC_TSD_INTS);
|
||||||
|
|
||||||
/* Schedule sampling to occur on the worker thread */
|
/* Schedule sampling to occur on the worker thread */
|
||||||
|
|
||||||
ret = sam_schedule(priv);
|
ret = sam_schedule(priv);
|
||||||
|
if (ret < 0)
|
||||||
/* Clear any pending interrupts and return success */
|
{
|
||||||
#warning Missing logic
|
idbg("ERROR: sam_schedule failed: %d\n", ret);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ extern "C"
|
|||||||
* /dev/inputN where N is the minor device number
|
* /dev/inputN where N is the minor device number
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* minor - The input device minor number
|
* dev - The ADC device handle received from sam_adc_initialize()
|
||||||
|
* minor - The input device minor number
|
||||||
*
|
*
|
||||||
* Returned Value:
|
* Returned Value:
|
||||||
* Zero is returned on success. Otherwise, a negated errno value is
|
* Zero is returned on success. Otherwise, a negated errno value is
|
||||||
@@ -87,7 +88,7 @@ extern "C"
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
int sam_tsd_register(int minor);
|
int sam_tsd_register(FAR struct adc_dev_s *dev, int minor);
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Interfaces exported from the touchscreen to the ADC driver
|
* Interfaces exported from the touchscreen to the ADC driver
|
||||||
@@ -99,14 +100,14 @@ int sam_tsd_register(int minor);
|
|||||||
* Handles ADC interrupts associated with touchscreen channels
|
* Handles ADC interrupts associated with touchscreen channels
|
||||||
*
|
*
|
||||||
* Input parmeters:
|
* Input parmeters:
|
||||||
* None
|
* pending - Current set of pending interrupts being handled
|
||||||
*
|
*
|
||||||
* Returned Value:
|
* Returned Value:
|
||||||
* None
|
* None
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
void sam_tsd_interrupt(void);
|
void sam_tsd_interrupt(uint32_t pending);
|
||||||
|
|
||||||
#undef EXTERN
|
#undef EXTERN
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ struct adc_ops_s
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* This is the device structure used by the driver. The caller of
|
/* This is the device structure used by the driver. The caller of
|
||||||
* can_register() must allocate and initialize this structure. The
|
* adc_register() must allocate and initialize this structure. The
|
||||||
* calling logic need only set all fields to zero except:
|
* calling logic need only set all fields to zero except:
|
||||||
*
|
*
|
||||||
* The elements of 'ad_ops', and 'ad_priv'
|
* The elements of 'ad_ops', and 'ad_priv'
|
||||||
|
|||||||
Reference in New Issue
Block a user