diff options
author | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2017-12-19 04:00:13 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-12-19 04:56:53 -0500 |
commit | fd639726bf15fca8ee1a00dce8e0096d0ad9bd18 (patch) | |
tree | fba7cf32dc096c9f1022991ba05777b45926a812 | |
parent | 297a344d5238c049fdc234c34dd7bb0d83f01271 (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.txt | 19 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/vendor-prefixes.txt | 1 | ||||
-rw-r--r-- | drivers/siox/Kconfig | 9 | ||||
-rw-r--r-- | drivers/siox/Makefile | 1 | ||||
-rw-r--r-- | drivers/siox/siox-bus-gpio.c | 172 |
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 @@ | |||
1 | Eckelmann SIOX GPIO bus | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "eckelmann,siox-gpio" | ||
5 | - din-gpios, dout-gpios, dclk-gpios, dld-gpios: references gpios for the | ||
6 | corresponding bus signals. | ||
7 | |||
8 | Examples: | ||
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 | |||
97 | dragino Dragino Technology Co., Limited | 97 | dragino Dragino Technology Co., Limited |
98 | ea Embedded Artists AB | 98 | ea Embedded Artists AB |
99 | ebv EBV Elektronik | 99 | ebv EBV Elektronik |
100 | eckelmann Eckelmann AG | ||
100 | edt Emerging Display Technologies | 101 | edt Emerging Display Technologies |
101 | eeti eGalax_eMPIA Technology Inc | 102 | eeti eGalax_eMPIA Technology Inc |
102 | elan Elan Microelectronic Corp. | 103 | elan 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 | |||
11 | if SIOX | ||
12 | |||
13 | config SIOX_BUS_GPIO | ||
14 | tristate "SIOX GPIO bus driver" | ||
15 | help | ||
16 | SIOX bus driver that controls the four bus lines using GPIOs. | ||
17 | |||
18 | endif | ||
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 @@ | |||
1 | obj-$(CONFIG_SIOX) += siox-core.o | 1 | obj-$(CONFIG_SIOX) += siox-core.o |
2 | obj-$(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 | |||
16 | struct 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 | |||
23 | static unsigned int siox_clkhigh_ns = 1000; | ||
24 | static unsigned int siox_loadhigh_ns; | ||
25 | static unsigned int siox_bytegap_ns; | ||
26 | |||
27 | static 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 | |||
86 | static 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); | ||
137 | err: | ||
138 | siox_master_put(smaster); | ||
139 | } | ||
140 | |||
141 | return ret; | ||
142 | } | ||
143 | |||
144 | static 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 | |||
153 | static const struct of_device_id siox_gpio_dt_ids[] = { | ||
154 | { .compatible = "eckelmann,siox-gpio", }, | ||
155 | { /* sentinel */ } | ||
156 | }; | ||
157 | MODULE_DEVICE_TABLE(of, siox_gpio_dt_ids); | ||
158 | |||
159 | static 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 | }; | ||
168 | module_platform_driver(siox_gpio_driver); | ||
169 | |||
170 | MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); | ||
171 | MODULE_LICENSE("GPL v2"); | ||
172 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||