Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Uwe Kleine-König and committed by
Greg Kroah-Hartman
fd639726 297a344d

+202
+19
Documentation/devicetree/bindings/siox/eckelmann,siox-gpio.txt
··· 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 + };
+1
Documentation/devicetree/bindings/vendor-prefixes.txt
··· 97 97 dragino Dragino Technology Co., Limited 98 98 ea Embedded Artists AB 99 99 ebv EBV Elektronik 100 + eckelmann Eckelmann AG 100 101 edt Emerging Display Technologies 101 102 eeti eGalax_eMPIA Technology Inc 102 103 elan Elan Microelectronic Corp.
+9
drivers/siox/Kconfig
··· 7 7 to drive additional I/O units. 8 8 9 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
+1
drivers/siox/Makefile
··· 1 1 obj-$(CONFIG_SIOX) += siox-core.o 2 + obj-$(CONFIG_SIOX_BUS_GPIO) += siox-bus-gpio.o
+172
drivers/siox/siox-bus-gpio.c
··· 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);