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