-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvni.c
139 lines (113 loc) · 2.83 KB
/
vni.c
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
135
136
137
138
139
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
struct vni_device {
struct spi_device *spi;
struct gpio_desc *out_en;
struct gpio_chip gpio_chip;
u8 val;
};
static int vni_send(struct spi_device *spi, u8 x)
{
u8 buf[2];
struct spi_transfer xfer = {
.tx_buf = buf,
.len = 2,
};
int ret;
buf[0] = x;
buf[1] = ((((x >> 0) & 0x1) +
((x >> 1) & 0x1) +
((x >> 2) & 0x1) +
((x >> 3) & 0x1) +
((x >> 4) & 0x1) +
((x >> 5) & 0x1) +
((x >> 6) & 0x1) +
((x >> 7) & 0x1)) & 0x1) << 1;
buf[1] |= ((buf[1] >> 1) & 0x1) ? 0 : 1;
buf[1] |= ((((x >> 1) & 0x1) +
((x >> 3) & 0x1) +
((x >> 5) & 0x1) +
((x >> 7) & 0x1)) & 0x1) << 2;
buf[1] |= ((((x >> 0) & 0x1) +
((x >> 2) & 0x1) +
((x >> 4) & 0x1) +
((x >> 6) & 0x1)) & 0x1) << 3;
ret = spi_sync_transfer(spi, &xfer, 1);
if (ret < 0)
return ret;
return 0;
}
static int vni_get_direction(struct gpio_chip *chip, unsigned int offset)
{
return GPIOF_DIR_OUT;
}
static void vni_set(struct gpio_chip *chip, unsigned int offset, int value)
{
struct vni_device *data = container_of(chip, struct vni_device, gpio_chip);
if (value)
data->val |= BIT(offset);
else
data->val &= ~BIT(offset);
vni_send(data->spi, data->val);
}
static int vni_probe(struct spi_device *spi)
{
struct vni_device *data;
int ret;
data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->spi = spi;
data->out_en = devm_gpiod_get_optional(&spi->dev, "output-enable",
GPIOD_OUT_HIGH);
if (!data->out_en)
dev_warn(&spi->dev, "no OUT_EN GPIO available, ignoring\n");
if (data->out_en && !IS_ERR(data->out_en))
gpiod_set_value(data->out_en, 1);
spi->bits_per_word = 8;
ret = spi_setup(spi);
if (ret)
return ret;
data->gpio_chip.parent = &spi->dev;
data->gpio_chip.of_node = spi->dev.of_node;
data->gpio_chip.of_gpio_n_cells = 2;
data->gpio_chip.of_xlate = of_gpio_simple_xlate;
data->gpio_chip.base = -1;
data->gpio_chip.ngpio = 8;
data->gpio_chip.get_direction = vni_get_direction;
data->gpio_chip.set = vni_set;
data->val = 0x0;
ret = devm_gpiochip_add_data(&spi->dev, &data->gpio_chip, NULL);
if (ret) {
dev_err(&spi->dev, "Adding GPIO chip failed\n");
return ret;
}
ret = vni_send(spi, data->val);
if (ret < 0)
return ret;
dev_info(&spi->dev, "VNI8200XP\n");
return 0;
}
static int vni_remove(struct spi_device *spi)
{
return 0;
}
static const struct of_device_id vni_dt_ids[] = {
{ .compatible = "st,vni8200xp" },
{}
};
MODULE_DEVICE_TABLE(of, vni_dt_ids);
static struct spi_driver vni_spi_driver = {
.driver = {
.name = "vni8200xp",
.of_match_table = vni_dt_ids,
},
.probe = vni_probe,
.remove = vni_remove,
};
module_spi_driver(vni_spi_driver);
MODULE_LICENSE("GPL");