blob: 3d95278d0576c7ca68e531ff05e8b3f5e257e693 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/lc823450/lc823450_gpio.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.
*
****************************************************************************/
/****************************************************************************
* 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
****************************************************************************/
#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(NULL);
val = getreg32(PMDCNT0 + (port * 4));
val &= ~(3 << (2 * pin));
val |= (mux << (2 *pin));
putreg32(val, PMDCNT0 + (port * 4));
spin_unlock_irqrestore(NULL, 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(NULL);
/* 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(NULL, 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(NULL);
/* 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(NULL, 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 */