linux/drivers/siox/siox-bus-gpio.c
<<
>>
Prefs
   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
  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 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
  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 = 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");
 139err:
 140                siox_master_put(smaster);
 141        }
 142
 143        return ret;
 144}
 145
 146static 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
 155static const struct of_device_id siox_gpio_dt_ids[] = {
 156        { .compatible = "eckelmann,siox-gpio", },
 157        { /* sentinel */ }
 158};
 159MODULE_DEVICE_TABLE(of, siox_gpio_dt_ids);
 160
 161static 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};
 170module_platform_driver(siox_gpio_driver);
 171
 172MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
 173MODULE_LICENSE("GPL v2");
 174MODULE_ALIAS("platform:" DRIVER_NAME);
 175