summaryrefslogtreecommitdiff
path: root/io-module/gpio.c
blob: 10f076b0fe999912d15d888e1a05166089d336f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <linux/delay.h>

struct gpio_pin *gpio_pin_by_name(const char *name) {
	struct gpio_pin *pin;

	for (pin = gpio_pins; *pin->name; pin++) {
		if (!strcmp(pin->name, name)) {
			return pin;
		}
	}

	log_error("pin named %s not found", name);

	return NULL;
}

// A GPIO pin number must only occur once.
struct gpio_pin *gpio_pin_by_num(unsigned num) {
	int ipin = 0;
	while(*(gpio_pins[ipin].name)) {
		if (gpio_pins[ipin].pin.gpio == num) {
			return &(gpio_pins[ipin]);
		}
		ipin++;
	}

	log_error("pin numbered %u not found", num);

	return NULL;
}


struct gpio_pin *gpio_pin_by_attr_name(const char *name) {
	struct gpio_pin *pin;

	for (pin = gpio_pins; *pin->name; pin++) {
		if (!strcmp(pin->pin.label, name)) {
			return pin;
		}
	}

	log_error("pin with attr name %s not found", name);

	return NULL;
}

/* Any gpio that could potentially get routed over an i2c bus
 * as opposed to a memory write to a register must call
 * "cansleep" versions of gpio functions.  The purpose of the
 * function is to remind kernel driver writers that any GPIO
 * routed over i2c (or spi) cannot be accessed in an interrupt
 * handler. Interrupt handlers should use the GPIO pins
 * that are memory mapped. gpio_get_value and gpio_set_value
 * cannot be used with the PCA 9557 or a dump will result. */
ssize_t mts_attr_show_gpio_pin(struct device *dev,
			struct device_attribute *attr,
			char *buf)
{
	int value;
	struct gpio_pin *pin = gpio_pin_by_attr_name(attr->attr.name);

	if (!pin) {
		return -ENODEV;
	}

	mutex_lock(&mts_io_mutex);

	if (pin->do_gpio_desc == 1) {
		value = gpiod_get_value_cansleep(pin->desc);
	} else {
		value = gpio_get_value_cansleep(pin->pin.gpio);
	}

	mutex_unlock(&mts_io_mutex);

	if (value < 0) {
		return value;
	}

	if (pin->active_low) {
		value = !value;
	}

	return sprintf(buf, "%d\n", value);
}

static ssize_t mts_attr_store_gpio_pin(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	int value;
	struct gpio_pin *pin = gpio_pin_by_attr_name(attr->attr.name);

	if (!pin) {
		return -ENODEV;
	}

	if (sscanf(buf, "%i", &value) != 1) {
		return -EINVAL;
	}

	if (pin->active_low) {
		value = !value;
	}

	mutex_lock(&mts_io_mutex);
	if (pin->do_gpio_desc == 1) {
		gpiod_set_value_cansleep(pin->desc, value);
	} else {
		gpio_set_value_cansleep(pin->pin.gpio, value);
	}
	mutex_unlock(&mts_io_mutex);

	return count;
}

static int reset_gpio_pin(struct gpio_pin *pin, unsigned int delay_ms, unsigned int value)
{
	if (!pin) {
		return -ENODEV;
	}
	if (pin->do_gpio_desc == 1) {
		gpiod_set_value_cansleep(pin->desc, value);
	} else {
		gpio_set_value_cansleep(pin->pin.gpio, value);
	}
	mdelay(delay_ms);
	if (pin->do_gpio_desc == 1) {
		gpiod_set_value_cansleep(pin->desc, !value);
	} else {
		gpio_set_value_cansleep(pin->pin.gpio, !value);
	}

	return 0;
}