Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2018-2019 NXP
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <counter.h>
#include <device.h>
#include "clock_freq.h"
#include "epit.h"

#define COUNTER_MAX_RELOAD	0xFFFFFFFF

struct imx_epit_config {
	struct counter_config_info info;
	EPIT_Type *base;
	u16_t prescaler;
};

struct imx_epit_data {
	volatile counter_top_callback_t callback;
	volatile void *user_data;
};

static inline const struct imx_epit_config *get_epit_config(struct device *dev)
{
	return CONTAINER_OF(dev->config->config_info, struct imx_epit_config,
			    info);
}

static void imx_epit_isr(void *arg)
{
	struct device *dev = (struct device *)arg;
	EPIT_Type *base = get_epit_config(dev)->base;
	struct imx_epit_data *driver_data = dev->driver_data;

	EPIT_ClearStatusFlag(base);

	if (driver_data->callback != NULL) {
		driver_data->callback(dev, (void *)driver_data->user_data);
	}
}

static void imx_epit_init(struct device *dev)
{
	struct imx_epit_config *config = (struct imx_epit_config *)
							   get_epit_config(dev);
	EPIT_Type *base = config->base;
	epit_init_config_t epit_config = {
		.freeRun     = true,
		.waitEnable  = true,
		.stopEnable  = true,
		.dbgEnable   = true,
		.enableMode  = true
	};

	/* Adjust frequency in the counter configuration info */
	config->info.freq = get_epit_clock_freq(base)/(config->prescaler + 1U);

	EPIT_Init(base, &epit_config);
}

static int imx_epit_start(struct device *dev)
{
	EPIT_Type *base = get_epit_config(dev)->base;

	/* Set EPIT clock source */
	EPIT_SetClockSource(base, epitClockSourcePeriph);

	/* Set prescaler */
	EPIT_SetPrescaler(base, get_epit_config(dev)->prescaler);

	/* Start the counter */
	EPIT_Enable(base);

	return 0;
}

static int imx_epit_stop(struct device *dev)
{
	EPIT_Type *base = get_epit_config(dev)->base;

	/* Disable EPIT */
	EPIT_Disable(base);

	return 0;
}

static u32_t imx_epit_read(struct device *dev)
{
	EPIT_Type *base = get_epit_config(dev)->base;
	u32_t value;

	value = EPIT_GetCounterLoadValue(base) - EPIT_ReadCounter(base);

	return value;
}

static int imx_epit_set_top_value(struct device *dev, u32_t ticks,
				  counter_top_callback_t callback,
				  void *user_data)
{
	EPIT_Type *base = get_epit_config(dev)->base;
	struct imx_epit_data *driver_data = dev->driver_data;

	/* Disable EPIT Output Compare interrupt for consistency */
	EPIT_SetIntCmd(base, false);

	driver_data->callback = callback;
	driver_data->user_data = user_data;

	/* Set both reload and counter values to "ticks" */
	EPIT_SetOverwriteCounter(base, true);
	EPIT_SetCounterLoadValue(base, ticks);

	if (callback != NULL) {
		/* (Re)enable EPIT Output Compare interrupt */
		EPIT_SetIntCmd(base, true);
	}

	return 0;
}

static u32_t imx_epit_get_pending_int(struct device *dev)
{
	EPIT_Type *base = get_epit_config(dev)->base;

	return EPIT_GetStatusFlag(base) ? 1U : 0U;
}

static u32_t imx_epit_get_top_value(struct device *dev)
{
	EPIT_Type *base = get_epit_config(dev)->base;

	return EPIT_GetCounterLoadValue(base);
}

static u32_t imx_epit_get_max_relative_alarm(struct device *dev)
{
	return COUNTER_MAX_RELOAD;
}

static const struct counter_driver_api imx_epit_driver_api = {
	.start = imx_epit_start,
	.stop = imx_epit_stop,
	.read = imx_epit_read,
	.set_top_value = imx_epit_set_top_value,
	.get_pending_int = imx_epit_get_pending_int,
	.get_top_value = imx_epit_get_top_value,
	.get_max_relative_alarm = imx_epit_get_max_relative_alarm,
};

#define COUNTER_IMX_EPIT_DEVICE(idx)					       \
static int imx_epit_config_func_##idx(struct device *dev);		       \
static const struct imx_epit_config imx_epit_##idx##z_config = {		       \
	.info = {							       \
			.max_top_value = COUNTER_MAX_RELOAD,		       \
			.freq = 1U,					       \
			.count_up = false,				       \
			.channels = 0U,					       \
		},							       \
	.base = (EPIT_Type *)DT_COUNTER_IMX_EPIT_##idx##_BASE_ADDRESS,	       \
	.prescaler = DT_COUNTER_IMX_EPIT_##idx##_PRESCALER,		       \
};									       \
static struct imx_epit_data imx_epit_##idx##_data;			       \
DEVICE_AND_API_INIT(epit_##idx, DT_COUNTER_IMX_EPIT_##idx##_LABEL,	       \
		    &imx_epit_config_func_##idx,			       \
		    &imx_epit_##idx##_data, &imx_epit_##idx##z_config.info,     \
		    PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,	       \
		    &imx_epit_driver_api);				       \
static int imx_epit_config_func_##idx(struct device *dev)		       \
{									       \
	imx_epit_init(dev);						       \
	IRQ_CONNECT(DT_COUNTER_IMX_EPIT_##idx##_IRQ,			       \
		    DT_COUNTER_IMX_EPIT_##idx##_IRQ_PRI,		       \
		    imx_epit_isr, DEVICE_GET(epit_##idx), 0);		       \
	irq_enable(DT_COUNTER_IMX_EPIT_##idx##_IRQ);			       \
	return 0;							       \
}

#ifdef CONFIG_COUNTER_IMX_EPIT_1
COUNTER_IMX_EPIT_DEVICE(1);
#endif /* CONFIG_COUNTER_IMX_EPIT_1 */

#ifdef CONFIG_COUNTER_IMX_EPIT_2
COUNTER_IMX_EPIT_DEVICE(2);
#endif /* CONFIG_COUNTER_IMX_EPIT_2 */