drivers: sdhc: imx_usdhc: add support for card interrupts

Add support for card interrupt sources to USDHC driver.

Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
Daniel DeGrasse 2022-08-15 16:57:09 -05:00 committed by Anas Nashif
parent c9acfafd78
commit 69aaed1266

View file

@ -75,6 +75,7 @@ struct usdhc_config {
};
struct usdhc_data {
const struct device *dev;
struct sdhc_host_props props;
bool card_present;
struct k_sem transfer_sem;
@ -82,6 +83,9 @@ struct usdhc_data {
usdhc_handle_t transfer_handle;
struct sdhc_io host_io;
struct k_mutex access_mutex;
sdhc_interrupt_cb_t sdhc_cb;
struct gpio_callback cd_callback;
void *sdhc_cb_user_data;
uint8_t usdhc_rx_dummy[128] __aligned(32);
#ifdef CONFIG_IMX_USDHC_DMA_SUPPORT
uint32_t *usdhc_dma_descriptor; /* ADMA descriptor table (noncachable) */
@ -107,6 +111,55 @@ static void transfer_complete_cb(USDHC_Type *usdhc, usdhc_handle_t *handle,
k_sem_give(&data->transfer_sem);
}
static void sdio_interrupt_cb(USDHC_Type *usdhc, void *user_data)
{
const struct device *dev = user_data;
struct usdhc_data *data = dev->data;
if (data->sdhc_cb) {
data->sdhc_cb(dev, SDHC_INT_SDIO, data->sdhc_cb_user_data);
}
}
static void card_inserted_cb(USDHC_Type *usdhc, void *user_data)
{
const struct device *dev = user_data;
struct usdhc_data *data = dev->data;
if (data->sdhc_cb) {
data->sdhc_cb(dev, SDHC_INT_INSERTED, data->sdhc_cb_user_data);
}
}
static void card_removed_cb(USDHC_Type *usdhc, void *user_data)
{
const struct device *dev = user_data;
struct usdhc_data *data = dev->data;
if (data->sdhc_cb) {
data->sdhc_cb(dev, SDHC_INT_REMOVED, data->sdhc_cb_user_data);
}
}
static void card_detect_gpio_cb(const struct device *port,
struct gpio_callback *cb,
gpio_port_pins_t pins)
{
struct usdhc_data *data = CONTAINER_OF(cb, struct usdhc_data, cd_callback);
const struct device *dev = data->dev;
const struct usdhc_config *cfg = dev->config;
if (data->sdhc_cb) {
if (gpio_pin_get_dt(&cfg->detect_gpio)) {
data->sdhc_cb(dev, SDHC_INT_INSERTED, data->sdhc_cb_user_data);
} else {
data->sdhc_cb(dev, SDHC_INT_REMOVED, data->sdhc_cb_user_data);
}
}
}
static int imx_usdhc_dat3_pull(const struct usdhc_config *cfg, bool pullup)
{
int ret = 0U;
@ -757,8 +810,7 @@ static int imx_usdhc_get_card_present(const struct device *dev)
} else if (cfg->detect_gpio.port) {
data->card_present = gpio_pin_get_dt(&cfg->detect_gpio) > 0;
} else {
LOG_WRN("No card presence method configured, assuming card is present");
data->card_present = true;
data->card_present = USDHC_DetectCardInsert(cfg->base);
}
return ((int)data->card_present);
}
@ -775,6 +827,132 @@ static int imx_usdhc_get_host_props(const struct device *dev,
return 0;
}
/*
* Enable SDHC card interrupt
*/
static int imx_usdhc_enable_interrupt(const struct device *dev,
sdhc_interrupt_cb_t callback,
int sources, void *user_data)
{
const struct usdhc_config *cfg = dev->config;
struct usdhc_data *data = dev->data;
int ret;
/* Record SDIO callback parameters */
data->sdhc_cb = callback;
data->sdhc_cb_user_data = user_data;
/* Disable interrupts, then enable what the user requested */
USDHC_DisableInterruptStatus(cfg->base, kUSDHC_CardInterruptFlag);
USDHC_DisableInterruptSignal(cfg->base, kUSDHC_CardInterruptFlag);
if (cfg->detect_gpio.port) {
ret = gpio_pin_interrupt_configure_dt(&cfg->detect_gpio,
GPIO_INT_DISABLE);
if (ret) {
return ret;
}
} else {
USDHC_DisableInterruptSignal(cfg->base, kUSDHC_CardInsertionFlag);
USDHC_DisableInterruptStatus(cfg->base, kUSDHC_CardInsertionFlag);
USDHC_DisableInterruptSignal(cfg->base, kUSDHC_CardRemovalFlag);
USDHC_DisableInterruptStatus(cfg->base, kUSDHC_CardRemovalFlag);
}
if (sources & SDHC_INT_SDIO) {
/* Enable SDIO card interrupt */
USDHC_EnableInterruptStatus(cfg->base, kUSDHC_CardInterruptFlag);
USDHC_EnableInterruptSignal(cfg->base, kUSDHC_CardInterruptFlag);
}
if (sources & SDHC_INT_INSERTED) {
if (cfg->detect_gpio.port) {
/* Use GPIO interrupt */
ret = gpio_pin_interrupt_configure_dt(&cfg->detect_gpio,
GPIO_INT_EDGE_TO_ACTIVE);
if (ret) {
return ret;
}
} else {
/* Enable card insertion interrupt */
USDHC_EnableInterruptStatus(cfg->base,
kUSDHC_CardInsertionFlag);
USDHC_EnableInterruptSignal(cfg->base,
kUSDHC_CardInsertionFlag);
}
}
if (sources & SDHC_INT_REMOVED) {
if (cfg->detect_gpio.port) {
/* Use GPIO interrupt */
ret = gpio_pin_interrupt_configure_dt(&cfg->detect_gpio,
GPIO_INT_EDGE_TO_INACTIVE);
if (ret) {
return ret;
}
} else {
/* Enable card removal interrupt */
USDHC_EnableInterruptStatus(cfg->base,
kUSDHC_CardRemovalFlag);
USDHC_EnableInterruptSignal(cfg->base,
kUSDHC_CardRemovalFlag);
}
}
return 0;
}
static int imx_usdhc_disable_interrupt(const struct device *dev, int sources)
{
const struct usdhc_config *cfg = dev->config;
struct usdhc_data *data = dev->data;
int ret;
if (sources & SDHC_INT_SDIO) {
/* Disable SDIO card interrupt */
USDHC_DisableInterruptStatus(cfg->base, kUSDHC_CardInterruptFlag);
USDHC_DisableInterruptSignal(cfg->base, kUSDHC_CardInterruptFlag);
}
if (sources & SDHC_INT_INSERTED) {
if (cfg->detect_gpio.port) {
ret = gpio_pin_interrupt_configure_dt(&cfg->detect_gpio,
GPIO_INT_DISABLE);
if (ret) {
return ret;
}
} else {
/* Disable card insertion interrupt */
USDHC_DisableInterruptStatus(cfg->base,
kUSDHC_CardInsertionFlag);
USDHC_DisableInterruptSignal(cfg->base,
kUSDHC_CardInsertionFlag);
}
}
if (sources & SDHC_INT_REMOVED) {
if (cfg->detect_gpio.port) {
ret = gpio_pin_interrupt_configure_dt(&cfg->detect_gpio,
GPIO_INT_DISABLE);
if (ret) {
return ret;
}
} else {
/* Disable card removal interrupt */
USDHC_DisableInterruptStatus(cfg->base,
kUSDHC_CardRemovalFlag);
USDHC_DisableInterruptSignal(cfg->base,
kUSDHC_CardRemovalFlag);
}
}
/* If all interrupt flags are disabled, remove callback */
if ((USDHC_GetEnabledInterruptStatusFlags(cfg->base) &
(kUSDHC_CardInterruptFlag | kUSDHC_CardInsertionFlag |
kUSDHC_CardRemovalFlag)) == 0) {
data->sdhc_cb = NULL;
data->sdhc_cb_user_data = NULL;
}
return 0;
}
static int imx_usdhc_isr(const struct device *dev)
{
const struct usdhc_config *cfg = dev->config;
@ -795,6 +973,9 @@ static int imx_usdhc_init(const struct device *dev)
int ret;
const usdhc_transfer_callback_t callbacks = {
.TransferComplete = transfer_complete_cb,
.SdioInterrupt = sdio_interrupt_cb,
.CardInserted = card_inserted_cb,
.CardRemoved = card_removed_cb,
};
if (!device_is_ready(cfg->clock_dev)) {
@ -833,7 +1014,14 @@ static int imx_usdhc_init(const struct device *dev)
if (ret) {
return ret;
}
gpio_init_callback(&data->cd_callback, card_detect_gpio_cb,
BIT(cfg->detect_gpio.pin));
ret = gpio_add_callback_dt(&cfg->detect_gpio, &data->cd_callback);
if (ret) {
return ret;
}
}
data->dev = dev;
k_mutex_init(&data->access_mutex);
memset(&data->host_io, 0, sizeof(data->host_io));
return k_sem_init(&data->transfer_sem, 0, 1);
@ -847,6 +1035,8 @@ static const struct sdhc_driver_api usdhc_api = {
.execute_tuning = imx_usdhc_execute_tuning,
.card_busy = imx_usdhc_card_busy,
.get_host_props = imx_usdhc_get_host_props,
.enable_interrupt = imx_usdhc_enable_interrupt,
.disable_interrupt = imx_usdhc_disable_interrupt,
};
#ifdef CONFIG_NOCACHE_MEMORY