summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2017-12-19 04:00:13 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-12-19 04:56:53 -0500
commitfd639726bf15fca8ee1a00dce8e0096d0ad9bd18 (patch)
treefba7cf32dc096c9f1022991ba05777b45926a812
parent297a344d5238c049fdc234c34dd7bb0d83f01271 (diff)
siox: add gpio bus driver
This bus driver uses GPIOs to control the four SIOX bus lines. Acked-by: Gavin Schenk <g.schenk@eckelmann.de> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/devicetree/bindings/siox/eckelmann,siox-gpio.txt19
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--drivers/siox/Kconfig9
-rw-r--r--drivers/siox/Makefile1
-rw-r--r--drivers/siox/siox-bus-gpio.c172
5 files changed, 202 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/siox/eckelmann,siox-gpio.txt b/Documentation/devicetree/bindings/siox/eckelmann,siox-gpio.txt
new file mode 100644
index 000000000000..55259cf39c25
--- /dev/null
+++ b/Documentation/devicetree/bindings/siox/eckelmann,siox-gpio.txt
@@ -0,0 +1,19 @@
1Eckelmann SIOX GPIO bus
2
3Required properties:
4- compatible : "eckelmann,siox-gpio"
5- din-gpios, dout-gpios, dclk-gpios, dld-gpios: references gpios for the
6 corresponding bus signals.
7
8Examples:
9
10 siox {
11 compatible = "eckelmann,siox-gpio";
12 pinctrl-names = "default";
13 pinctrl-0 = <&pinctrl_siox>;
14
15 din-gpios = <&gpio6 11 0>;
16 dout-gpios = <&gpio6 8 0>;
17 dclk-gpios = <&gpio6 9 0>;
18 dld-gpios = <&gpio6 10 0>;
19 };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 0994bdd82cd3..889d1c0f5050 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -97,6 +97,7 @@ dptechnics DPTechnics
97dragino Dragino Technology Co., Limited 97dragino Dragino Technology Co., Limited
98ea Embedded Artists AB 98ea Embedded Artists AB
99ebv EBV Elektronik 99ebv EBV Elektronik
100eckelmann Eckelmann AG
100edt Emerging Display Technologies 101edt Emerging Display Technologies
101eeti eGalax_eMPIA Technology Inc 102eeti eGalax_eMPIA Technology Inc
102elan Elan Microelectronic Corp. 103elan Elan Microelectronic Corp.
diff --git a/drivers/siox/Kconfig b/drivers/siox/Kconfig
index bd24d9b50dc6..083d2e62189a 100644
--- a/drivers/siox/Kconfig
+++ b/drivers/siox/Kconfig
@@ -7,3 +7,12 @@ menuconfig SIOX
7 to drive additional I/O units. 7 to drive additional I/O units.
8 8
9 Unless you know better, it is probably safe to say "no" here. 9 Unless you know better, it is probably safe to say "no" here.
10
11if SIOX
12
13config SIOX_BUS_GPIO
14 tristate "SIOX GPIO bus driver"
15 help
16 SIOX bus driver that controls the four bus lines using GPIOs.
17
18endif
diff --git a/drivers/siox/Makefile b/drivers/siox/Makefile
index d55cb5e08868..a956f65206d5 100644
--- a/drivers/siox/Makefile
+++ b/drivers/siox/Makefile
@@ -1 +1,2 @@
1obj-$(CONFIG_SIOX) += siox-core.o 1obj-$(CONFIG_SIOX) += siox-core.o
2obj-$(CONFIG_SIOX_BUS_GPIO) += siox-bus-gpio.o
diff --git a/drivers/siox/siox-bus-gpio.c b/drivers/siox/siox-bus-gpio.c
new file mode 100644
index 000000000000..ea7ef982968b
--- /dev/null
+++ b/drivers/siox/siox-bus-gpio.c
@@ -0,0 +1,172 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2015-2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
4 */
5
6#include <linux/gpio/consumer.h>
7#include <linux/module.h>
8#include <linux/platform_device.h>
9
10#include <linux/delay.h>
11
12#include "siox.h"
13
14#define DRIVER_NAME "siox-gpio"
15
16struct siox_gpio_ddata {
17 struct gpio_desc *din;
18 struct gpio_desc *dout;
19 struct gpio_desc *dclk;
20 struct gpio_desc *dld;
21};
22
23static unsigned int siox_clkhigh_ns = 1000;
24static unsigned int siox_loadhigh_ns;
25static unsigned int siox_bytegap_ns;
26
27static int siox_gpio_pushpull(struct siox_master *smaster,
28 size_t setbuf_len, const u8 setbuf[],
29 size_t getbuf_len, u8 getbuf[])
30{
31 struct siox_gpio_ddata *ddata = siox_master_get_devdata(smaster);
32 size_t i;
33 size_t cycles = max(setbuf_len, getbuf_len);
34
35 /* reset data and clock */
36 gpiod_set_value_cansleep(ddata->dout, 0);
37 gpiod_set_value_cansleep(ddata->dclk, 0);
38
39 gpiod_set_value_cansleep(ddata->dld, 1);
40 ndelay(siox_loadhigh_ns);
41 gpiod_set_value_cansleep(ddata->dld, 0);
42
43 for (i = 0; i < cycles; ++i) {
44 u8 set = 0, get = 0;
45 size_t j;
46
47 if (i >= cycles - setbuf_len)
48 set = setbuf[i - (cycles - setbuf_len)];
49
50 for (j = 0; j < 8; ++j) {
51 get <<= 1;
52 if (gpiod_get_value_cansleep(ddata->din))
53 get |= 1;
54
55 /* DOUT is logically inverted */
56 gpiod_set_value_cansleep(ddata->dout, !(set & 0x80));
57 set <<= 1;
58
59 gpiod_set_value_cansleep(ddata->dclk, 1);
60 ndelay(siox_clkhigh_ns);
61 gpiod_set_value_cansleep(ddata->dclk, 0);
62 }
63
64 if (i < getbuf_len)
65 getbuf[i] = get;
66
67 ndelay(siox_bytegap_ns);
68 }
69
70 gpiod_set_value_cansleep(ddata->dld, 1);
71 ndelay(siox_loadhigh_ns);
72 gpiod_set_value_cansleep(ddata->dld, 0);
73
74 /*
75 * Resetting dout isn't necessary protocol wise, but it makes the
76 * signals more pretty because the dout level is deterministic between
77 * cycles. Note that this only affects dout between the master and the
78 * first siox device. dout for the later devices depend on the output of
79 * the previous siox device.
80 */
81 gpiod_set_value_cansleep(ddata->dout, 0);
82
83 return 0;
84}
85
86static int siox_gpio_probe(struct platform_device *pdev)
87{
88 struct device *dev = &pdev->dev;
89 struct siox_gpio_ddata *ddata;
90 int ret;
91 struct siox_master *smaster;
92
93 smaster = siox_master_alloc(&pdev->dev, sizeof(*ddata));
94 if (!smaster) {
95 dev_err(dev, "failed to allocate siox master\n");
96 return -ENOMEM;
97 }
98
99 platform_set_drvdata(pdev, smaster);
100 ddata = siox_master_get_devdata(smaster);
101
102 ddata->din = devm_gpiod_get(dev, "din", GPIOD_IN);
103 if (IS_ERR(ddata->din)) {
104 ret = PTR_ERR(ddata->din);
105 dev_err(dev, "Failed to get %s GPIO: %d\n", "din", ret);
106 goto err;
107 }
108
109 ddata->dout = devm_gpiod_get(dev, "dout", GPIOD_OUT_LOW);
110 if (IS_ERR(ddata->dout)) {
111 ret = PTR_ERR(ddata->dout);
112 dev_err(dev, "Failed to get %s GPIO: %d\n", "dout", ret);
113 goto err;
114 }
115
116 ddata->dclk = devm_gpiod_get(dev, "dclk", GPIOD_OUT_LOW);
117 if (IS_ERR(ddata->dclk)) {
118 ret = PTR_ERR(ddata->dclk);
119 dev_err(dev, "Failed to get %s GPIO: %d\n", "dclk", ret);
120 goto err;
121 }
122
123 ddata->dld = devm_gpiod_get(dev, "dld", GPIOD_OUT_LOW);
124 if (IS_ERR(ddata->dld)) {
125 ret = PTR_ERR(ddata->dld);
126 dev_err(dev, "Failed to get %s GPIO: %d\n", "dld", ret);
127 goto err;
128 }
129
130 smaster->pushpull = siox_gpio_pushpull;
131 /* XXX: determine automatically like spi does */
132 smaster->busno = 0;
133
134 ret = siox_master_register(smaster);
135 if (ret) {
136 dev_err(dev, "Failed to register siox master: %d\n", ret);
137err:
138 siox_master_put(smaster);
139 }
140
141 return ret;
142}
143
144static int siox_gpio_remove(struct platform_device *pdev)
145{
146 struct siox_master *master = platform_get_drvdata(pdev);
147
148 siox_master_unregister(master);
149
150 return 0;
151}
152
153static const struct of_device_id siox_gpio_dt_ids[] = {
154 { .compatible = "eckelmann,siox-gpio", },
155 { /* sentinel */ }
156};
157MODULE_DEVICE_TABLE(of, siox_gpio_dt_ids);
158
159static struct platform_driver siox_gpio_driver = {
160 .probe = siox_gpio_probe,
161 .remove = siox_gpio_remove,
162
163 .driver = {
164 .name = DRIVER_NAME,
165 .of_match_table = siox_gpio_dt_ids,
166 },
167};
168module_platform_driver(siox_gpio_driver);
169
170MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
171MODULE_LICENSE("GPL v2");
172MODULE_ALIAS("platform:" DRIVER_NAME);