517 lines
14 KiB
C
517 lines
14 KiB
C
/****************************************************************************
|
|
* arch/arm/src/lc823450/lc823450_gpio.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <arch/board/board.h>
|
|
#include <nuttx/config.h>
|
|
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/spinlock.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include "arm_internal.h"
|
|
#include "lc823450_gpio.h"
|
|
#include "lc823450_syscontrol.h"
|
|
|
|
#ifdef CONFIG_IOEX
|
|
# include <nuttx/ioex.h>
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define PORTX_OFFSET 0x00001000
|
|
#define PORTX_DRC_OFFSET 0x0
|
|
#define PORTX_DAT_OFFSET 0x4
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static spinlock_t g_gpio_lock = SP_UNLOCKED;
|
|
|
|
#ifdef CONFIG_IOEX
|
|
static struct ioex_dev_s *g_ioex_dev;
|
|
#endif
|
|
|
|
#ifdef CONFIG_LC823450_VGPIO
|
|
#define GPIO_VIRTUAL_NUM 32
|
|
static struct vgpio_ops_s *vgpio_ops[GPIO_VIRTUAL_NUM];
|
|
#endif /* CONFIG_LC823450_VGPIO */
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_get_gpio_dir
|
|
****************************************************************************/
|
|
|
|
static uintptr_t lc823450_get_gpio_dir(unsigned int port)
|
|
{
|
|
uintptr_t regaddr =
|
|
PORT0_BASE +
|
|
(port * PORTX_OFFSET) +
|
|
PORTX_DRC_OFFSET;
|
|
|
|
return regaddr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_get_gpio_data
|
|
****************************************************************************/
|
|
|
|
static uintptr_t lc823450_get_gpio_data(unsigned int port)
|
|
{
|
|
uintptr_t regaddr =
|
|
PORT0_BASE +
|
|
(port * PORTX_OFFSET) +
|
|
PORTX_DAT_OFFSET;
|
|
|
|
return regaddr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_get_gpio_pull
|
|
****************************************************************************/
|
|
|
|
static uintptr_t lc823450_get_gpio_pull(unsigned int port)
|
|
{
|
|
uintptr_t regaddr =
|
|
PUDCNT0 + (4 * port);
|
|
|
|
return regaddr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_configinput
|
|
*
|
|
* Description:
|
|
* Configure pull up/down for the GPIO pin
|
|
*
|
|
* Assumptions:
|
|
* Interrupts are disabled so that read-modify-write operations are safe.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void lc823450_configpull(uint16_t gpiocfg,
|
|
unsigned int port, unsigned int pin)
|
|
{
|
|
uintptr_t regaddr;
|
|
uint32_t regval;
|
|
uint32_t pull;
|
|
|
|
regaddr = lc823450_get_gpio_pull(port);
|
|
pull = gpiocfg & GPIO_PUPD_MASK;
|
|
regval = getreg32(regaddr);
|
|
regval &= ~(3 << (2 * pin)); /* clear the current setting */
|
|
|
|
switch (pull)
|
|
{
|
|
case GPIO_FLOAT:
|
|
|
|
/* do nothing */
|
|
|
|
break;
|
|
case GPIO_PULLUP:
|
|
regval |= (1 << (2 * pin));
|
|
break;
|
|
case GPIO_PULLDOWN:
|
|
regval |= (2 << (2 * pin));
|
|
break;
|
|
}
|
|
|
|
putreg32(regval, regaddr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_configinput
|
|
*
|
|
* Description:
|
|
* Configure a GPIO pin as an input
|
|
*
|
|
* Assumptions:
|
|
* Interrupts are disabled so that read-modify-write operations are safe.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void lc823450_configinput(uint32_t port, uint32_t pin)
|
|
{
|
|
uintptr_t regaddr;
|
|
uint32_t regval;
|
|
|
|
regaddr = lc823450_get_gpio_dir(port);
|
|
|
|
/* Then configure the pin as a normal input by clearing the corresponding
|
|
* bit in the rPxDRC register for the port.
|
|
*/
|
|
|
|
regval = getreg32(regaddr);
|
|
regval &= ~(1 << pin);
|
|
putreg32(regval, regaddr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_configoutput
|
|
*
|
|
* Description:
|
|
* Configure a GPIO pin as an output.
|
|
*
|
|
* Assumptions:
|
|
* Interrupts are disabled so that read-modify-write operations are safe.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void lc823450_configoutput(uint16_t gpiocfg, uint32_t port,
|
|
uint32_t pin)
|
|
{
|
|
uintptr_t regaddr;
|
|
uint32_t regval;
|
|
|
|
regaddr = lc823450_get_gpio_dir(port);
|
|
|
|
/* Then configure the pin as an output by setting the corresponding
|
|
* bit in the rPxDRC register for the port.
|
|
*/
|
|
|
|
regval = getreg32(regaddr);
|
|
regval |= (1 << pin);
|
|
putreg32(regval, regaddr);
|
|
|
|
/* Set the initial value of the output */
|
|
|
|
lc823450_gpio_write(gpiocfg, GPIO_IS_ONE(gpiocfg));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_gpio_mux
|
|
*
|
|
* Description:
|
|
* Change a GPIO pin mux. (only PORT0-5 are supported)
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lc823450_gpio_mux(uint16_t gpiocfg)
|
|
{
|
|
uint32_t port = ((gpiocfg & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT);
|
|
uint32_t pin = ((gpiocfg & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT);
|
|
uint32_t mux = ((gpiocfg & GPIO_MUX_MASK) >> GPIO_MUX_SHIFT);
|
|
uint32_t val;
|
|
int ret = 0;
|
|
|
|
if (port <= (GPIO_PORT5 >> GPIO_PORT_SHIFT))
|
|
{
|
|
irqstate_t flags = spin_lock_irqsave(&g_gpio_lock);
|
|
val = getreg32(PMDCNT0 + (port * 4));
|
|
val &= ~(3 << (2 * pin));
|
|
val |= (mux << (2 *pin));
|
|
putreg32(val, PMDCNT0 + (port * 4));
|
|
spin_unlock_irqrestore(&g_gpio_lock, flags);
|
|
}
|
|
else
|
|
{
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_gpio_config
|
|
*
|
|
* Description:
|
|
* Configure a GPIO based on bit-encoded description of the pin. NOTE:
|
|
* The pin *must* have first been configured for GPIO usage with a
|
|
* corresponding call to lc823450_pin_config().
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lc823450_gpio_config(uint16_t gpiocfg)
|
|
{
|
|
uint32_t port = ((gpiocfg & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT);
|
|
uint32_t pin = ((gpiocfg & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT);
|
|
irqstate_t flags;
|
|
int ret = OK;
|
|
|
|
#ifdef CONFIG_LC823450_VGPIO
|
|
if (port == (GPIO_PORTV >> GPIO_PORT_SHIFT))
|
|
{
|
|
return OK;
|
|
}
|
|
#endif /* CONFIG_LC823450_VGPIO */
|
|
|
|
if (port <= (GPIO_PORT5 >> GPIO_PORT_SHIFT))
|
|
{
|
|
DEBUGASSERT(pin < NUM_GPIO_PINS);
|
|
|
|
modifyreg32(MCLKCNTAPB, 0, MCLKCNTAPB_PORT0_CLKEN << port);
|
|
modifyreg32(MRSTCNTAPB, 0, MRSTCNTAPB_PORT0_RSTB << port);
|
|
|
|
/* Handle the GPIO configuration by the basic mode of the pin */
|
|
|
|
flags = spin_lock_irqsave(&g_gpio_lock);
|
|
|
|
/* pull up/down specified */
|
|
|
|
if (gpiocfg & GPIO_PUPD_MASK)
|
|
{
|
|
lc823450_configpull(gpiocfg, port, pin);
|
|
}
|
|
|
|
switch (gpiocfg & GPIO_MODE_MASK)
|
|
{
|
|
case GPIO_MODE_INPUT: /* GPIO input pin */
|
|
lc823450_configinput(port, pin);
|
|
break;
|
|
|
|
case GPIO_MODE_OUTPUT: /* GPIO output pin */
|
|
lc823450_configoutput(gpiocfg, port, pin);
|
|
break;
|
|
|
|
default:
|
|
gpioerr("ERROR: Unrecognized pin mode: %04x\n", gpiocfg);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&g_gpio_lock, flags);
|
|
}
|
|
#ifdef CONFIG_IOEX
|
|
else if (port <= (GPIO_PORTEX >> GPIO_PORT_SHIFT))
|
|
{
|
|
uint32_t dir;
|
|
uint32_t pupd;
|
|
DEBUGASSERT(pin < NUM_GPIOEX_PINS);
|
|
|
|
if (GPIO_IS_INPUT(gpiocfg))
|
|
{
|
|
dir = IOEX_DIR_INPUT;
|
|
|
|
switch (gpiocfg & GPIO_PUPD_MASK)
|
|
{
|
|
case GPIO_FLOAT:
|
|
pupd = IOEX_PUPD_FLOAT;
|
|
break;
|
|
case GPIO_PULLUP:
|
|
pupd = IOEX_PUPD_PULLUP;
|
|
break;
|
|
case GPIO_PULLDOWN:
|
|
pupd = IOEX_PUPD_PULLDOWN;
|
|
break;
|
|
default:
|
|
DEBUGPANIC();
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dir = IOEX_DIR_OUTPUT;
|
|
pupd = 0;
|
|
}
|
|
|
|
ret = g_ioex_dev->ops->config(g_ioex_dev, pin, dir, pupd);
|
|
if (ret != 0)
|
|
{
|
|
gpioerr("ERROR: Failed to configure I/O expanded port\n");
|
|
}
|
|
|
|
g_ioex_dev->ops->write(g_ioex_dev, pin, GPIO_IS_ONE(gpiocfg));
|
|
}
|
|
#endif /* CONFIG_IOEX */
|
|
else
|
|
{
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_gpio_write
|
|
*
|
|
* Description:
|
|
* Write one or zero to the selected GPIO pin
|
|
*
|
|
****************************************************************************/
|
|
|
|
void lc823450_gpio_write(uint16_t gpiocfg, bool value)
|
|
{
|
|
uint32_t port = ((gpiocfg & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT);
|
|
uint32_t pin = ((gpiocfg & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT);
|
|
|
|
uintptr_t regaddr;
|
|
uint32_t regval;
|
|
irqstate_t flags;
|
|
|
|
#ifdef CONFIG_LC823450_VGPIO
|
|
if (port == (GPIO_PORTV >> GPIO_PORT_SHIFT))
|
|
{
|
|
ASSERT(pin < GPIO_VIRTUAL_NUM);
|
|
if (vgpio_ops[pin] && vgpio_ops[pin]->write)
|
|
{
|
|
vgpio_ops[pin]->write(pin, value);
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif /* CONFIG_LC823450_VGPIO */
|
|
|
|
if (port <= (GPIO_PORT5 >> GPIO_PORT_SHIFT))
|
|
{
|
|
DEBUGASSERT(pin < NUM_GPIO_PINS);
|
|
|
|
regaddr = lc823450_get_gpio_data(port);
|
|
|
|
flags = spin_lock_irqsave(&g_gpio_lock);
|
|
|
|
/* Write the value (0 or 1). To the data register */
|
|
|
|
regval = getreg32(regaddr);
|
|
|
|
if (value)
|
|
{
|
|
regval |= (1 << pin);
|
|
}
|
|
else
|
|
{
|
|
regval &= ~(1 << pin);
|
|
}
|
|
|
|
putreg32(regval, regaddr);
|
|
|
|
spin_unlock_irqrestore(&g_gpio_lock, flags);
|
|
}
|
|
#ifdef CONFIG_IOEX
|
|
else if (port <= (GPIO_PORTEX >> GPIO_PORT_SHIFT))
|
|
{
|
|
DEBUGASSERT(pin < NUM_GPIOEX_PINS);
|
|
|
|
g_ioex_dev->ops->write(g_ioex_dev, pin, value);
|
|
}
|
|
#endif /* CONFIG_IOEX */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_gpio_read
|
|
*
|
|
* Description:
|
|
* Read one or zero from the selected GPIO pin
|
|
*
|
|
****************************************************************************/
|
|
|
|
bool lc823450_gpio_read(uint16_t gpiocfg)
|
|
{
|
|
uint32_t port = ((gpiocfg & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT);
|
|
uint32_t pin = ((gpiocfg & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT);
|
|
|
|
uintptr_t regaddr;
|
|
uint32_t regval;
|
|
bool value = false;
|
|
|
|
#ifdef CONFIG_LC823450_VGPIO
|
|
if (port == (GPIO_PORTV >> GPIO_PORT_SHIFT))
|
|
{
|
|
ASSERT(pin < GPIO_VIRTUAL_NUM);
|
|
if (vgpio_ops[pin] && vgpio_ops[pin]->read)
|
|
{
|
|
return vgpio_ops[pin]->read(pin);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_LC823450_VGPIO */
|
|
|
|
if (port <= (GPIO_PORT5 >> GPIO_PORT_SHIFT))
|
|
{
|
|
DEBUGASSERT(pin < NUM_GPIO_PINS);
|
|
|
|
/* Get the value of the pin from the pin data register */
|
|
|
|
regaddr = lc823450_get_gpio_data(port);
|
|
regval = getreg32(regaddr);
|
|
value = ((regval >> pin) & 0x01);
|
|
}
|
|
#ifdef CONFIG_IOEX
|
|
else if (port <= (GPIO_PORTEX >> GPIO_PORT_SHIFT))
|
|
{
|
|
DEBUGASSERT(pin < NUM_GPIOEX_PINS);
|
|
|
|
value = g_ioex_dev->ops->read(g_ioex_dev, pin);
|
|
}
|
|
#endif /* CONFIG_IOEX */
|
|
else
|
|
{
|
|
DEBUGPANIC();
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_gpio_initialize
|
|
*
|
|
* Description:
|
|
* Initialize GPIO expander driver
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_IOEX
|
|
int lc823450_gpio_initialize(void)
|
|
{
|
|
g_ioex_dev = up_ioexinitialize(1);
|
|
if (!g_ioex_dev)
|
|
{
|
|
gpioerr("ERROR: Failed to initialize ioex driver\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif /* CONFIG_IOEX */
|
|
|
|
/****************************************************************************
|
|
* Name: lc823450_vgpio_register
|
|
*
|
|
* Description:
|
|
* Register Virtual GPIO driver
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_LC823450_VGPIO
|
|
int lc823450_vgpio_register(unsigned int pin, struct vgpio_ops_s *ops)
|
|
{
|
|
ASSERT(pin < GPIO_VIRTUAL_NUM);
|
|
vgpio_ops[pin] = ops;
|
|
return OK;
|
|
}
|
|
#endif /* CONFIG_LC823450_VGPIO */
|