drivers/pps/clients/pps-gpio.c

Source file repositories/reference/linux-study-clean/drivers/pps/clients/pps-gpio.c

File Facts

System
Linux kernel
Corpus path
drivers/pps/clients/pps-gpio.c
Extension
.c
Size
7214 bytes
Lines
260
Domain
Driver Families
Bucket
drivers/pps
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct pps_gpio_device_data {
	int irq;			/* IRQ used as PPS source */
	struct pps_device *pps;		/* PPS source device */
	struct pps_source_info info;	/* PPS source information */
	struct gpio_desc *gpio_pin;	/* GPIO port descriptors */
	struct gpio_desc *echo_pin;
	struct timer_list echo_timer;	/* timer to reset echo active state */
	bool assert_falling_edge;
	bool capture_clear;
	unsigned int echo_active_ms;	/* PPS echo active duration */
	unsigned long echo_timeout;	/* timer timeout value in jiffies */
};

/*
 * Report the PPS event
 */

static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
{
	const struct pps_gpio_device_data *info;
	struct pps_event_time ts;
	int rising_edge;

	/* Get the time stamp first */
	pps_get_ts(&ts);

	info = data;

	/* Small trick to bypass the check on edge's direction when capture_clear is unset */
	rising_edge = info->capture_clear ?
		      gpiod_get_value(info->gpio_pin) : !info->assert_falling_edge;
	if ((rising_edge && !info->assert_falling_edge) ||
			(!rising_edge && info->assert_falling_edge))
		pps_event(info->pps, &ts, PPS_CAPTUREASSERT, data);
	else if (info->capture_clear &&
			((rising_edge && info->assert_falling_edge) ||
			(!rising_edge && !info->assert_falling_edge)))
		pps_event(info->pps, &ts, PPS_CAPTURECLEAR, data);
	else
		dev_warn_ratelimited(&info->pps->dev, "IRQ did not trigger any PPS event\n");

	return IRQ_HANDLED;
}

/* This function will only be called when an ECHO GPIO is defined */
static void pps_gpio_echo(struct pps_device *pps, int event, void *data)
{
	/* add_timer() needs to write into info->echo_timer */
	struct pps_gpio_device_data *info = data;

	switch (event) {
	case PPS_CAPTUREASSERT:
		if (pps->params.mode & PPS_ECHOASSERT)
			gpiod_set_value(info->echo_pin, 1);
		break;

	case PPS_CAPTURECLEAR:
		if (pps->params.mode & PPS_ECHOCLEAR)
			gpiod_set_value(info->echo_pin, 1);
		break;
	}

	/* fire the timer */
	if (info->pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) {
		info->echo_timer.expires = jiffies + info->echo_timeout;
		add_timer(&info->echo_timer);
	}
}

/* Timer callback to reset the echo pin to the inactive state */
static void pps_gpio_echo_timer_callback(struct timer_list *t)
{
	const struct pps_gpio_device_data *info;

	info = timer_container_of(info, t, echo_timer);

	gpiod_set_value(info->echo_pin, 0);
}

static int pps_gpio_setup(struct device *dev)
{
	struct pps_gpio_device_data *data = dev_get_drvdata(dev);
	int ret;
	u32 value;

	data->gpio_pin = devm_gpiod_get(dev, NULL, GPIOD_IN);
	if (IS_ERR(data->gpio_pin))
		return dev_err_probe(dev, PTR_ERR(data->gpio_pin),
				     "failed to request PPS GPIO\n");

Annotation

Implementation Notes