blob: 57362f368ab2a48a2177ba5d526f8144daf05ad8 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/gd32f4/gd32f4xx_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 <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/irq.h>
#include "chip.h"
#include "gd32f4xx_syscfg.h"
#include "gd32f4xx_gpio.h"
#include "gd32f4xx.h"
/****************************************************************************
* Public Data
****************************************************************************/
/* Base addresses for each GPIO block */
const uint32_t g_gpio_base[GD32_NGPIO_PORTS] =
{
#if GD32_NGPIO_PORTS > 0
GD32_GPIOA,
#endif
#if GD32_NGPIO_PORTS > 1
GD32_GPIOB,
#endif
#if GD32_NGPIO_PORTS > 2
GD32_GPIOC,
#endif
#if GD32_NGPIO_PORTS > 3
GD32_GPIOD,
#endif
#if GD32_NGPIO_PORTS > 4
GD32_GPIOE,
#endif
#if GD32_NGPIO_PORTS > 5
GD32_GPIOF,
#endif
#if GD32_NGPIO_PORTS > 6
GD32_GPIOG,
#endif
#if GD32_NGPIO_PORTS > 7
GD32_GPIOH,
#endif
#if GD32_NGPIO_PORTS > 8
GD32_GPIOI,
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Function: gd32_gpio_clock_enable
*
* Description:
*
* Enable GPIO clock
*
****************************************************************************/
static void gd32_gpio_clock_enable(uint32_t port_base)
{
uint32_t rcu_en;
uint32_t regaddr;
/* Determine which GPIO port to configure */
switch (port_base)
{
default:
return;
#if GD32_NGPIO_PORTS > 0
case GD32_GPIOA:
rcu_en = RCU_AHB1EN_PAEN;
break;
#endif
#if GD32_NGPIO_PORTS > 1
case GD32_GPIOB:
rcu_en = RCU_AHB1EN_PBEN;
break;
#endif
#if GD32_NGPIO_PORTS > 2
case GD32_GPIOC:
rcu_en = RCU_AHB1EN_PCEN;
break;
#endif
#if GD32_NGPIO_PORTS > 3
case GD32_GPIOD:
rcu_en = RCU_AHB1EN_PDEN;
break;
#endif
#if GD32_NGPIO_PORTS > 4
case GD32_GPIOE:
rcu_en = RCU_AHB1EN_PEEN;
break;
#endif
#if GD32_NGPIO_PORTS > 5
case GD32_GPIOF:
rcu_en = RCU_AHB1EN_PFEN;
break;
#endif
#if GD32_NGPIO_PORTS > 6
case GD32_GPIOG:
rcu_en = RCU_AHB1EN_PGEN;
break;
#endif
#if GD32_NGPIO_PORTS > 7
case GD32_GPIOH:
rcu_en = RCU_AHB1EN_PHEN;
break;
#endif
#if GD32_NGPIO_PORTS > 8
case GD32_GPIOI:
rcu_en = RCU_AHB1EN_PIEN;
break;
#endif
}
regaddr = GD32_RCU_AHB1EN;
/* Check clock if alreay enable. */
if (rcu_en != (rcu_en & getreg32(regaddr)))
{
/* Enable/disable AHB clock for GPIO */
modifyreg32(regaddr, 0, rcu_en);
}
}
/****************************************************************************
* Function: gd32_gpio_output_set
*
* Description:
*
* Set GPIO output
*
****************************************************************************/
static void gd32_gpio_output_set(uint32_t port_base, uint32_t pin,
bool value)
{
uintptr_t regaddr;
uint32_t regval;
if (value)
{
regval = GPIO_BOP_SET(pin);
regaddr = GD32_GPIO_BOP(port_base);
}
else
{
regval = GPIO_BC_SET(pin);
regaddr = GD32_GPIO_BC(port_base);
}
putreg32(regval, regaddr);
}
/****************************************************************************
* Function: gd32_gpio_input_get
*
* Description:
*
* Get GPIO input status
*
****************************************************************************/
static inline bool gd32_gpio_input_get(uint32_t port_base, uint32_t pin)
{
uintptr_t regaddr;
uint32_t regval;
regaddr = GD32_GPIO_ISTAT(port_base);
regval = getreg32(regaddr);
return ((regval & GPIO_PIN(pin)) != 0);
}
/****************************************************************************
* Name: gd32_gpio_af_config
*
* Description:
* Configure a GPIO pin as the Alternative function.
****************************************************************************/
static void gd32_gpio_af_config(uint32_t cfgset, uint32_t port_base,
uint32_t pin)
{
uint32_t af_sel;
uint32_t regaddr;
uint32_t regval;
uint32_t pos;
af_sel = (cfgset & GPIO_CFG_AF_MASK) >> GPIO_CFG_AF_SHIFT;
if (pin < 8)
{
regaddr = GD32_GPIO_AFSEL0(port_base);
pos = pin;
}
else
{
regaddr = GD32_GPIO_AFSEL1(port_base);
pos = pin - 8;
}
regval = getreg32(regaddr);
regval &= ~GPIO_AF_MASK(pos);
regval |= (af_sel << GPIO_AF_SHIFT(pos));
putreg32(regval, regaddr);
}
/****************************************************************************
* Name: gd32_gpio_af_unconfig
*
* Description:
* Configure a GPIO pin's Alternative function as reset value.
****************************************************************************/
static void gd32_gpio_af_unconfig(uint32_t cfgset, uint32_t port_base,
uint32_t pin)
{
uint32_t regaddr;
uint32_t regval;
uint32_t pos;
if (pin < 8)
{
regaddr = GD32_GPIO_AFSEL0(port_base);
pos = pin;
}
else
{
regaddr = GD32_GPIO_AFSEL1(port_base);
pos = pin - 8;
}
regval = getreg32(regaddr);
regval &= ~GPIO_AF_MASK(pos);
putreg32(regval, regaddr);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: gd32_gpio_config
*
* Description:
* Configure a GPIO pin based on bit-encoded description of the pin.
*
* Return value:
* OK on success
* A negated errno value on invalid port or mode.
*
****************************************************************************/
int gd32_gpio_config(uint32_t cfgset)
{
uint32_t port_base;
uint32_t regval;
uint32_t setting;
uint32_t port;
uint32_t pin;
uint32_t pinmode;
irqstate_t flags;
/* Interrupts must be disabled from here on out so that we have mutually
* exclusive access to all of the GPIO configuration registers.
*/
flags = enter_critical_section();
/* Verify that this hardware supports the select GPIO port */
port = (cfgset & GPIO_CFG_PORT_MASK) >> GPIO_CFG_PORT_SHIFT;
if (port >= GD32_NGPIO_PORTS)
{
leave_critical_section(flags);
return -EINVAL;
}
/* Get the port base address */
port_base = g_gpio_base[port];
/* Eable the GPIO port clock */
gd32_gpio_clock_enable(port_base);
/* Get the pin number and select the port configuration register for that
* pin
*/
pin = (cfgset & GPIO_CFG_PIN_MASK) >> GPIO_CFG_PIN_SHIFT;
/* Set up the mode register (and remember whether the pin mode) */
switch (cfgset & GPIO_CFG_MODE_MASK)
{
/* Input mode */
case GPIO_CFG_MODE_INPUT:
pinmode = GPIO_MODE_INPUT;
break;
/* General purpose output mode */
case GPIO_CFG_MODE_OUTPUT:
/* Set the initial output value */
gd32_gpio_write(cfgset, (cfgset & GPIO_CFG_OUTPUT_SET) != 0);
pinmode = GPIO_MODE_OUTPUT;
break;
/* Alternate function mode */
case GPIO_CFG_MODE_AF:
gd32_gpio_af_config(cfgset, port_base, pin);
pinmode = GPIO_MODE_AF;
break;
/* Analog mode */
case GPIO_CFG_MODE_ANALOG:
pinmode = GPIO_MODE_ANALOG;
break;
default:
leave_critical_section(flags);
return -EINVAL;
}
regval = getreg32(GD32_GPIO_CTL(port_base));
regval &= ~GPIO_MODE_MASK(pin);
regval |= ((uint32_t)pinmode << GPIO_MODE_SHIFT(pin));
putreg32(regval, GD32_GPIO_CTL(port_base));
/* Unconfigure alternate function */
if (pinmode != GPIO_MODE_AF)
{
gd32_gpio_af_unconfig(cfgset, port_base, pin);
}
/* Set up the mode register (and remember whether the pin mode) */
switch (cfgset & GPIO_CFG_PUPD_MASK)
{
default:
/* No pull-up, pull-down */
case GPIO_CFG_PUPD_NONE:
setting = GPIO_PUPD_NONE;
break;
/* With pull-up resistor */
case GPIO_CFG_PUPD_PULLUP:
setting = GPIO_PUPD_PULLUP;
break;
/* With pull-down resistor */
case GPIO_CFG_PUPD_PULLDOWN:
setting = GPIO_PUPD_PULLDOWN;
break;
}
regval = getreg32(GD32_GPIO_PUD(port_base));
regval &= ~GPIO_PUPD_MASK(pin);
regval |= ((uint32_t)setting << GPIO_MODE_SHIFT(pin));
putreg32(regval, GD32_GPIO_PUD(port_base));
/* Set push-pull/open-drain (Only outputs and alternate function pins) */
setting = GPIO_OTYPE_OD(pin);
regval = getreg32(GD32_GPIO_OMODE(port_base));
if ((pinmode == GPIO_MODE_OUTPUT || pinmode == GPIO_MODE_AF) &&
(cfgset & GPIO_CFG_ODPP_MASK) == GPIO_CFG_OD)
{
regval |= setting;
}
else
{
regval &= ~setting;
}
putreg32(regval, GD32_GPIO_OMODE(port_base));
/* Set speed (Only outputs and alternate function pins) */
setting = 0;
if (pinmode == GPIO_MODE_OUTPUT || pinmode == GPIO_MODE_AF)
{
switch (cfgset & GPIO_CFG_SPEED_MASK)
{
default:
/* 2 MHz Low speed output */
case GPIO_CFG_SPEED_2MHZ:
setting = GPIO_OSPEED_2MHZ;
break;
/* 25 MHz Medium speed output */
case GPIO_CFG_SPEED_25MHZ:
setting = GPIO_OSPEED_25MHZ;
break;
/* 50 MHz Fast speed output */
case GPIO_CFG_SPEED_50MHZ:
setting = GPIO_OSPEED_50MHZ;
break;
/* 200 MHz High speed output */
case GPIO_CFG_SPEED_200MHZ:
setting = GPIO_OSPEED_200MHZ;
break;
}
}
regval = getreg32(GD32_GPIO_OSPD(port_base));
regval &= ~GPIO_OSPEED_MASK(pin);
regval |= (setting << GPIO_OSPEED_SHIFT(pin));
putreg32(regval, GD32_GPIO_OSPD(port_base));
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: gd32_gpio_unconfig
*
* Description:
* Unconfigure a GPIO pin based on bit-encoded description of the pin, set
* it into default HiZ state.
*
****************************************************************************/
int gd32_gpio_unconfig(uint32_t cfgset)
{
/* Reuse port and pin number */
cfgset &= GPIO_CFG_PORT_MASK | GPIO_CFG_PIN_MASK;
/* Set GPIO in floating input mode */
cfgset |= GPIO_CFG_MODE_INPUT | GPIO_CFG_PUPD_NONE;
/* To-Do: Mark its unuse for automatic power saving options */
return gd32_gpio_config(cfgset);
}
/****************************************************************************
* Name: gd32_gpio_write
*
* Description:
* Write one or zero to the selected GPIO pin
*
****************************************************************************/
void gd32_gpio_write(uint32_t pinset, bool value)
{
irqstate_t flags;
uint32_t port_base;
uint32_t port;
uint32_t pin;
flags = enter_critical_section();
port = (pinset & GPIO_CFG_PORT_MASK) >> GPIO_CFG_PORT_SHIFT;
/* Get the port base address */
port_base = g_gpio_base[port];
/* Get the pin number */
pin = (pinset & GPIO_CFG_PIN_MASK) >> GPIO_CFG_PIN_SHIFT;
/* Set or clear the output on the pin */
gd32_gpio_output_set(port_base, pin, value);
leave_critical_section(flags);
}
/****************************************************************************
* Name: gd32_gpio_read
*
* Description:
* Read one or zero from the selected GPIO pin
*
****************************************************************************/
bool gd32_gpio_read(uint32_t pinset)
{
irqstate_t flags;
uint32_t port_base;
uint32_t port;
uint32_t pin;
bool value;
flags = enter_critical_section();
port = (pinset & GPIO_CFG_PORT_MASK) >> GPIO_CFG_PORT_SHIFT;
/* Get the port base address */
port_base = g_gpio_base[port];
/* Get the pin number and return the input state of that pin */
pin = (pinset & GPIO_CFG_PIN_MASK) >> GPIO_CFG_PIN_SHIFT;
value = gd32_gpio_input_get(port_base, pin);
leave_critical_section(flags);
return value;
}