154 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 17struct 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 24static unsigned int siox_clkhigh_ns = 1000;25static unsigned int siox_loadhigh_ns;26static unsigned int siox_bytegap_ns;27 28static 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 the77 * signals more pretty because the dout level is deterministic between78 * cycles. Note that this only affects dout between the master and the79 * first siox device. dout for the later devices depend on the output of80 * the previous siox device.81 */82 gpiod_set_value_cansleep(ddata->dout, 0);83 84 return 0;85}86 87static 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 = devm_siox_master_alloc(dev, sizeof(*ddata));95 if (!smaster)96 return dev_err_probe(dev, -ENOMEM,97 "failed to allocate siox master\n");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 return dev_err_probe(dev, PTR_ERR(ddata->din),105 "Failed to get din GPIO\n");106 107 ddata->dout = devm_gpiod_get(dev, "dout", GPIOD_OUT_LOW);108 if (IS_ERR(ddata->dout))109 return dev_err_probe(dev, PTR_ERR(ddata->dout),110 "Failed to get dout GPIO\n");111 112 ddata->dclk = devm_gpiod_get(dev, "dclk", GPIOD_OUT_LOW);113 if (IS_ERR(ddata->dclk))114 return dev_err_probe(dev, PTR_ERR(ddata->dclk),115 "Failed to get dclk GPIO\n");116 117 ddata->dld = devm_gpiod_get(dev, "dld", GPIOD_OUT_LOW);118 if (IS_ERR(ddata->dld))119 return dev_err_probe(dev, PTR_ERR(ddata->dld),120 "Failed to get dld GPIO\n");121 122 smaster->pushpull = siox_gpio_pushpull;123 /* XXX: determine automatically like spi does */124 smaster->busno = 0;125 126 ret = devm_siox_master_register(dev, smaster);127 if (ret)128 return dev_err_probe(dev, ret,129 "Failed to register siox master\n");130 131 return 0;132}133 134static const struct of_device_id siox_gpio_dt_ids[] = {135 { .compatible = "eckelmann,siox-gpio", },136 { /* sentinel */ }137};138MODULE_DEVICE_TABLE(of, siox_gpio_dt_ids);139 140static struct platform_driver siox_gpio_driver = {141 .probe = siox_gpio_probe,142 143 .driver = {144 .name = DRIVER_NAME,145 .of_match_table = siox_gpio_dt_ids,146 },147};148module_platform_driver(siox_gpio_driver);149 150MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");151MODULE_DESCRIPTION("SIOX GPIO bus driver");152MODULE_LICENSE("GPL v2");153MODULE_ALIAS("platform:" DRIVER_NAME);154