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); 173