/* * SPI master driver using generic bitbanged GPIO * * Sebastian Hesselbarth * * Based on Linux driver * Copyright (C) 2006,2008 David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include struct gpio_spi { struct spi_master master; struct gpio_spi_pdata *data; }; #define priv_from_spi_device(s) container_of(s->master, struct gpio_spi, master) static inline void setsck(const struct spi_device *spi, int is_on) { struct gpio_spi *priv = priv_from_spi_device(spi); gpio_set_value(priv->data->sck, is_on); } static inline void setmosi(const struct spi_device *spi, int is_on) { struct gpio_spi *priv = priv_from_spi_device(spi); if (!gpio_is_valid(priv->data->mosi)) return; gpio_set_value(priv->data->mosi, is_on); } static inline int getmiso(const struct spi_device *spi) { struct gpio_spi *priv = priv_from_spi_device(spi); if (!gpio_is_valid(priv->data->miso)) return 1; return !!gpio_get_value(priv->data->miso); } #define spidelay(nsecs) do { } while (0) #include "spi-bitbang-txrx.h" static int gpio_spi_set_cs(struct spi_device *spi, bool en) { struct gpio_spi *priv = priv_from_spi_device(spi); if (!gpio_is_valid(priv->data->cs[spi->chip_select])) return 0; gpio_set_value(priv->data->cs[spi->chip_select], (spi->mode & SPI_CS_HIGH) ? en : !en); return 0; } static inline u32 gpio_spi_txrx_word(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) { int cpol = !!(spi->mode & SPI_CPOL); if (spi->mode & SPI_CPHA) return bitbang_txrx_be_cpha1(spi, nsecs, cpol, word, bits); else return bitbang_txrx_be_cpha0(spi, nsecs, cpol, word, bits); } static int gpio_spi_transfer_one(struct spi_device *spi, struct spi_transfer *t) { bool read = (t->rx_buf) ? true : false; u32 word = 0; int n; for (n = 0; n < t->len; n++) { if (!read) word = ((const u8 *)t->tx_buf)[n]; word = gpio_spi_txrx_word(spi, 0, word, 8); if (read) ((u8 *)t->rx_buf)[n] = word & 0xff; } return 0; } static int gpio_spi_transfer(struct spi_device *spi, struct spi_message *msg) { struct spi_transfer *t; int ret; ret = gpio_spi_set_cs(spi, true); if (ret) return ret; list_for_each_entry(t, &msg->transfers, transfer_list) { ret = gpio_spi_transfer_one(spi, t); if (ret) return ret; msg->actual_length += t->len; } ret = gpio_spi_set_cs(spi, false); if (ret) return ret; return 0; } static int gpio_spi_setup(struct spi_device *spi) { if (spi->bits_per_word != 8) { dev_err(spi->master->dev, "master does not support %d bits per word\n", spi->bits_per_word); return -EINVAL; } return 0; } static int gpio_spi_of_probe(struct device_d *dev) { struct device_node *np = dev->device_node; struct gpio_spi_pdata *pdata; int n, sck; if (!IS_ENABLED(CONFIG_OFDEVICE) || dev->platform_data) return 0; sck = of_get_named_gpio(np, "gpio-sck", 0); if (sck == -EPROBE_DEFER) return sck; if (!gpio_is_valid(sck)) { dev_err(dev, "missing mandatory SCK gpio\n"); return sck; } pdata = xzalloc(sizeof(*pdata)); pdata->sck = sck; pdata->num_cs = MAX_CHIPSELECT; pdata->miso = of_get_named_gpio(np, "gpio-miso", 0); if (!gpio_is_valid(pdata->miso)) pdata->miso = SPI_GPIO_NO_MISO; pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0); if (!gpio_is_valid(pdata->mosi)) pdata->mosi = SPI_GPIO_NO_MOSI; for (n = 0; n < MAX_CHIPSELECT; n++) { pdata->cs[n] = of_get_named_gpio(np, "cs-gpios", n); if (!gpio_is_valid(pdata->cs[n])) pdata->cs[n] = SPI_GPIO_NO_CS; } dev->platform_data = pdata; return 0; } static int gpio_spi_probe(struct device_d *dev) { struct gpio_spi *priv; struct gpio_spi_pdata *pdata; struct spi_master *master; int n, ret; ret = gpio_spi_of_probe(dev); if (ret) return ret; pdata = dev->platform_data; ret = gpio_request_one(pdata->sck, GPIOF_DIR_OUT, "spi-sck"); if (ret) return ret; if (pdata->miso != SPI_GPIO_NO_MISO) { ret = gpio_request_one(pdata->miso, GPIOF_DIR_IN, "spi-miso"); if (ret) return ret; } if (pdata->mosi != SPI_GPIO_NO_MOSI) { ret = gpio_request_one(pdata->mosi, GPIOF_DIR_OUT, "spi-mosi"); if (ret) return ret; } for (n = 0; n < pdata->num_cs; n++) { char *cs_name; if (!gpio_is_valid(pdata->cs[n])) continue; cs_name = basprintf("spi-cs%d", n); ret = gpio_request_one(pdata->cs[n], GPIOF_DIR_OUT, cs_name); if (ret) return ret; } priv = xzalloc(sizeof(*priv)); priv->data = pdata; master = &priv->master; master->dev = dev; master->bus_num = dev->id; master->setup = gpio_spi_setup; master->transfer = gpio_spi_transfer; master->num_chipselect = priv->data->num_cs; return spi_register_master(&priv->master); } static struct of_device_id __maybe_unused gpio_spi_dt_ids[] = { { .compatible = "spi-gpio", }, { } }; static struct driver_d gpio_spi_driver = { .name = "gpio-spi", .probe = gpio_spi_probe, .of_compatible = DRV_OF_COMPAT(gpio_spi_dt_ids), }; device_platform_driver(gpio_spi_driver);