1fd639726SUwe Kleine-König // SPDX-License-Identifier: GPL-2.0
2fd639726SUwe Kleine-König /*
3fd639726SUwe Kleine-König * Copyright (C) 2015-2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
4fd639726SUwe Kleine-König */
5fd639726SUwe Kleine-König
6fd639726SUwe Kleine-König #include <linux/gpio/consumer.h>
7fd639726SUwe Kleine-König #include <linux/module.h>
8ac316725SRandy Dunlap #include <linux/mod_devicetable.h>
9fd639726SUwe Kleine-König #include <linux/platform_device.h>
10fd639726SUwe Kleine-König
11fd639726SUwe Kleine-König #include <linux/delay.h>
12fd639726SUwe Kleine-König
13fd639726SUwe Kleine-König #include "siox.h"
14fd639726SUwe Kleine-König
15fd639726SUwe Kleine-König #define DRIVER_NAME "siox-gpio"
16fd639726SUwe Kleine-König
17fd639726SUwe Kleine-König struct siox_gpio_ddata {
18fd639726SUwe Kleine-König struct gpio_desc *din;
19fd639726SUwe Kleine-König struct gpio_desc *dout;
20fd639726SUwe Kleine-König struct gpio_desc *dclk;
21fd639726SUwe Kleine-König struct gpio_desc *dld;
22fd639726SUwe Kleine-König };
23fd639726SUwe Kleine-König
24fd639726SUwe Kleine-König static unsigned int siox_clkhigh_ns = 1000;
25fd639726SUwe Kleine-König static unsigned int siox_loadhigh_ns;
26fd639726SUwe Kleine-König static unsigned int siox_bytegap_ns;
27fd639726SUwe Kleine-König
siox_gpio_pushpull(struct siox_master * smaster,size_t setbuf_len,const u8 setbuf[],size_t getbuf_len,u8 getbuf[])28fd639726SUwe Kleine-König static int siox_gpio_pushpull(struct siox_master *smaster,
29fd639726SUwe Kleine-König size_t setbuf_len, const u8 setbuf[],
30fd639726SUwe Kleine-König size_t getbuf_len, u8 getbuf[])
31fd639726SUwe Kleine-König {
32fd639726SUwe Kleine-König struct siox_gpio_ddata *ddata = siox_master_get_devdata(smaster);
33fd639726SUwe Kleine-König size_t i;
34fd639726SUwe Kleine-König size_t cycles = max(setbuf_len, getbuf_len);
35fd639726SUwe Kleine-König
36fd639726SUwe Kleine-König /* reset data and clock */
37fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dout, 0);
38fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dclk, 0);
39fd639726SUwe Kleine-König
40fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dld, 1);
41fd639726SUwe Kleine-König ndelay(siox_loadhigh_ns);
42fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dld, 0);
43fd639726SUwe Kleine-König
44fd639726SUwe Kleine-König for (i = 0; i < cycles; ++i) {
45fd639726SUwe Kleine-König u8 set = 0, get = 0;
46fd639726SUwe Kleine-König size_t j;
47fd639726SUwe Kleine-König
48fd639726SUwe Kleine-König if (i >= cycles - setbuf_len)
49fd639726SUwe Kleine-König set = setbuf[i - (cycles - setbuf_len)];
50fd639726SUwe Kleine-König
51fd639726SUwe Kleine-König for (j = 0; j < 8; ++j) {
52fd639726SUwe Kleine-König get <<= 1;
53fd639726SUwe Kleine-König if (gpiod_get_value_cansleep(ddata->din))
54fd639726SUwe Kleine-König get |= 1;
55fd639726SUwe Kleine-König
56fd639726SUwe Kleine-König /* DOUT is logically inverted */
57fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dout, !(set & 0x80));
58fd639726SUwe Kleine-König set <<= 1;
59fd639726SUwe Kleine-König
60fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dclk, 1);
61fd639726SUwe Kleine-König ndelay(siox_clkhigh_ns);
62fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dclk, 0);
63fd639726SUwe Kleine-König }
64fd639726SUwe Kleine-König
65fd639726SUwe Kleine-König if (i < getbuf_len)
66fd639726SUwe Kleine-König getbuf[i] = get;
67fd639726SUwe Kleine-König
68fd639726SUwe Kleine-König ndelay(siox_bytegap_ns);
69fd639726SUwe Kleine-König }
70fd639726SUwe Kleine-König
71fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dld, 1);
72fd639726SUwe Kleine-König ndelay(siox_loadhigh_ns);
73fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dld, 0);
74fd639726SUwe Kleine-König
75fd639726SUwe Kleine-König /*
76fd639726SUwe Kleine-König * Resetting dout isn't necessary protocol wise, but it makes the
77fd639726SUwe Kleine-König * signals more pretty because the dout level is deterministic between
78fd639726SUwe Kleine-König * cycles. Note that this only affects dout between the master and the
79fd639726SUwe Kleine-König * first siox device. dout for the later devices depend on the output of
80fd639726SUwe Kleine-König * the previous siox device.
81fd639726SUwe Kleine-König */
82fd639726SUwe Kleine-König gpiod_set_value_cansleep(ddata->dout, 0);
83fd639726SUwe Kleine-König
84fd639726SUwe Kleine-König return 0;
85fd639726SUwe Kleine-König }
86fd639726SUwe Kleine-König
siox_gpio_probe(struct platform_device * pdev)87fd639726SUwe Kleine-König static int siox_gpio_probe(struct platform_device *pdev)
88fd639726SUwe Kleine-König {
89fd639726SUwe Kleine-König struct device *dev = &pdev->dev;
90fd639726SUwe Kleine-König struct siox_gpio_ddata *ddata;
91fd639726SUwe Kleine-König int ret;
92fd639726SUwe Kleine-König struct siox_master *smaster;
93fd639726SUwe Kleine-König
94fd639726SUwe Kleine-König smaster = siox_master_alloc(&pdev->dev, sizeof(*ddata));
95fd639726SUwe Kleine-König if (!smaster) {
96fd639726SUwe Kleine-König dev_err(dev, "failed to allocate siox master\n");
97fd639726SUwe Kleine-König return -ENOMEM;
98fd639726SUwe Kleine-König }
99fd639726SUwe Kleine-König
100fd639726SUwe Kleine-König platform_set_drvdata(pdev, smaster);
101fd639726SUwe Kleine-König ddata = siox_master_get_devdata(smaster);
102fd639726SUwe Kleine-König
103fd639726SUwe Kleine-König ddata->din = devm_gpiod_get(dev, "din", GPIOD_IN);
104fd639726SUwe Kleine-König if (IS_ERR(ddata->din)) {
105*75020f2dSThorsten Scherer ret = dev_err_probe(dev, PTR_ERR(ddata->din),
106*75020f2dSThorsten Scherer "Failed to get din GPIO\n");
107fd639726SUwe Kleine-König goto err;
108fd639726SUwe Kleine-König }
109fd639726SUwe Kleine-König
110fd639726SUwe Kleine-König ddata->dout = devm_gpiod_get(dev, "dout", GPIOD_OUT_LOW);
111fd639726SUwe Kleine-König if (IS_ERR(ddata->dout)) {
112*75020f2dSThorsten Scherer ret = dev_err_probe(dev, PTR_ERR(ddata->dout),
113*75020f2dSThorsten Scherer "Failed to get dout GPIO\n");
114fd639726SUwe Kleine-König goto err;
115fd639726SUwe Kleine-König }
116fd639726SUwe Kleine-König
117fd639726SUwe Kleine-König ddata->dclk = devm_gpiod_get(dev, "dclk", GPIOD_OUT_LOW);
118fd639726SUwe Kleine-König if (IS_ERR(ddata->dclk)) {
119*75020f2dSThorsten Scherer ret = dev_err_probe(dev, PTR_ERR(ddata->dclk),
120*75020f2dSThorsten Scherer "Failed to get dclk GPIO\n");
121fd639726SUwe Kleine-König goto err;
122fd639726SUwe Kleine-König }
123fd639726SUwe Kleine-König
124fd639726SUwe Kleine-König ddata->dld = devm_gpiod_get(dev, "dld", GPIOD_OUT_LOW);
125fd639726SUwe Kleine-König if (IS_ERR(ddata->dld)) {
126*75020f2dSThorsten Scherer ret = dev_err_probe(dev, PTR_ERR(ddata->dld),
127*75020f2dSThorsten Scherer "Failed to get dld GPIO\n");
128fd639726SUwe Kleine-König goto err;
129fd639726SUwe Kleine-König }
130fd639726SUwe Kleine-König
131fd639726SUwe Kleine-König smaster->pushpull = siox_gpio_pushpull;
132fd639726SUwe Kleine-König /* XXX: determine automatically like spi does */
133fd639726SUwe Kleine-König smaster->busno = 0;
134fd639726SUwe Kleine-König
135fd639726SUwe Kleine-König ret = siox_master_register(smaster);
136fd639726SUwe Kleine-König if (ret) {
137*75020f2dSThorsten Scherer dev_err_probe(dev, ret,
138*75020f2dSThorsten Scherer "Failed to register siox master\n");
139fd639726SUwe Kleine-König err:
140fd639726SUwe Kleine-König siox_master_put(smaster);
141fd639726SUwe Kleine-König }
142fd639726SUwe Kleine-König
143fd639726SUwe Kleine-König return ret;
144fd639726SUwe Kleine-König }
145fd639726SUwe Kleine-König
siox_gpio_remove(struct platform_device * pdev)146fd639726SUwe Kleine-König static int siox_gpio_remove(struct platform_device *pdev)
147fd639726SUwe Kleine-König {
148fd639726SUwe Kleine-König struct siox_master *master = platform_get_drvdata(pdev);
149fd639726SUwe Kleine-König
150fd639726SUwe Kleine-König siox_master_unregister(master);
151fd639726SUwe Kleine-König
152fd639726SUwe Kleine-König return 0;
153fd639726SUwe Kleine-König }
154fd639726SUwe Kleine-König
155fd639726SUwe Kleine-König static const struct of_device_id siox_gpio_dt_ids[] = {
156fd639726SUwe Kleine-König { .compatible = "eckelmann,siox-gpio", },
157fd639726SUwe Kleine-König { /* sentinel */ }
158fd639726SUwe Kleine-König };
159fd639726SUwe Kleine-König MODULE_DEVICE_TABLE(of, siox_gpio_dt_ids);
160fd639726SUwe Kleine-König
161fd639726SUwe Kleine-König static struct platform_driver siox_gpio_driver = {
162fd639726SUwe Kleine-König .probe = siox_gpio_probe,
163fd639726SUwe Kleine-König .remove = siox_gpio_remove,
164fd639726SUwe Kleine-König
165fd639726SUwe Kleine-König .driver = {
166fd639726SUwe Kleine-König .name = DRIVER_NAME,
167fd639726SUwe Kleine-König .of_match_table = siox_gpio_dt_ids,
168fd639726SUwe Kleine-König },
169fd639726SUwe Kleine-König };
170fd639726SUwe Kleine-König module_platform_driver(siox_gpio_driver);
171fd639726SUwe Kleine-König
172fd639726SUwe Kleine-König MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
173fd639726SUwe Kleine-König MODULE_LICENSE("GPL v2");
174fd639726SUwe Kleine-König MODULE_ALIAS("platform:" DRIVER_NAME);
175