linux/drivers/spi/spi-xcomm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Analog Devices AD-FMCOMMS1-EBZ board I2C-SPI bridge driver
   4 *
   5 * Copyright 2012 Analog Devices Inc.
   6 * Author: Lars-Peter Clausen <lars@metafoo.de>
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/delay.h>
  12#include <linux/i2c.h>
  13#include <linux/spi/spi.h>
  14#include <asm/unaligned.h>
  15
  16#define SPI_XCOMM_SETTINGS_LEN_OFFSET           10
  17#define SPI_XCOMM_SETTINGS_3WIRE                BIT(6)
  18#define SPI_XCOMM_SETTINGS_CS_HIGH              BIT(5)
  19#define SPI_XCOMM_SETTINGS_SAMPLE_END           BIT(4)
  20#define SPI_XCOMM_SETTINGS_CPHA                 BIT(3)
  21#define SPI_XCOMM_SETTINGS_CPOL                 BIT(2)
  22#define SPI_XCOMM_SETTINGS_CLOCK_DIV_MASK       0x3
  23#define SPI_XCOMM_SETTINGS_CLOCK_DIV_64         0x2
  24#define SPI_XCOMM_SETTINGS_CLOCK_DIV_16         0x1
  25#define SPI_XCOMM_SETTINGS_CLOCK_DIV_4          0x0
  26
  27#define SPI_XCOMM_CMD_UPDATE_CONFIG     0x03
  28#define SPI_XCOMM_CMD_WRITE             0x04
  29
  30#define SPI_XCOMM_CLOCK 48000000
  31
  32struct spi_xcomm {
  33        struct i2c_client *i2c;
  34
  35        uint16_t settings;
  36        uint16_t chipselect;
  37
  38        unsigned int current_speed;
  39
  40        uint8_t buf[63];
  41};
  42
  43static int spi_xcomm_sync_config(struct spi_xcomm *spi_xcomm, unsigned int len)
  44{
  45        uint16_t settings;
  46        uint8_t *buf = spi_xcomm->buf;
  47
  48        settings = spi_xcomm->settings;
  49        settings |= len << SPI_XCOMM_SETTINGS_LEN_OFFSET;
  50
  51        buf[0] = SPI_XCOMM_CMD_UPDATE_CONFIG;
  52        put_unaligned_be16(settings, &buf[1]);
  53        put_unaligned_be16(spi_xcomm->chipselect, &buf[3]);
  54
  55        return i2c_master_send(spi_xcomm->i2c, buf, 5);
  56}
  57
  58static void spi_xcomm_chipselect(struct spi_xcomm *spi_xcomm,
  59        struct spi_device *spi, int is_active)
  60{
  61        unsigned long cs = spi->chip_select;
  62        uint16_t chipselect = spi_xcomm->chipselect;
  63
  64        if (is_active)
  65                chipselect |= BIT(cs);
  66        else
  67                chipselect &= ~BIT(cs);
  68
  69        spi_xcomm->chipselect = chipselect;
  70}
  71
  72static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
  73        struct spi_device *spi, struct spi_transfer *t, unsigned int *settings)
  74{
  75        if (t->len > 62)
  76                return -EINVAL;
  77
  78        if (t->speed_hz != spi_xcomm->current_speed) {
  79                unsigned int divider;
  80
  81                divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, t->speed_hz);
  82                if (divider >= 64)
  83                        *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_64;
  84                else if (divider >= 16)
  85                        *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_16;
  86                else
  87                        *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_4;
  88
  89                spi_xcomm->current_speed = t->speed_hz;
  90        }
  91
  92        if (spi->mode & SPI_CPOL)
  93                *settings |= SPI_XCOMM_SETTINGS_CPOL;
  94        else
  95                *settings &= ~SPI_XCOMM_SETTINGS_CPOL;
  96
  97        if (spi->mode & SPI_CPHA)
  98                *settings &= ~SPI_XCOMM_SETTINGS_CPHA;
  99        else
 100                *settings |= SPI_XCOMM_SETTINGS_CPHA;
 101
 102        if (spi->mode & SPI_3WIRE)
 103                *settings |= SPI_XCOMM_SETTINGS_3WIRE;
 104        else
 105                *settings &= ~SPI_XCOMM_SETTINGS_3WIRE;
 106
 107        return 0;
 108}
 109
 110static int spi_xcomm_txrx_bufs(struct spi_xcomm *spi_xcomm,
 111        struct spi_device *spi, struct spi_transfer *t)
 112{
 113        int ret;
 114
 115        if (t->tx_buf) {
 116                spi_xcomm->buf[0] = SPI_XCOMM_CMD_WRITE;
 117                memcpy(spi_xcomm->buf + 1, t->tx_buf, t->len);
 118
 119                ret = i2c_master_send(spi_xcomm->i2c, spi_xcomm->buf, t->len + 1);
 120                if (ret < 0)
 121                        return ret;
 122                else if (ret != t->len + 1)
 123                        return -EIO;
 124        } else if (t->rx_buf) {
 125                ret = i2c_master_recv(spi_xcomm->i2c, t->rx_buf, t->len);
 126                if (ret < 0)
 127                        return ret;
 128                else if (ret != t->len)
 129                        return -EIO;
 130        }
 131
 132        return t->len;
 133}
 134
 135static int spi_xcomm_transfer_one(struct spi_master *master,
 136        struct spi_message *msg)
 137{
 138        struct spi_xcomm *spi_xcomm = spi_master_get_devdata(master);
 139        unsigned int settings = spi_xcomm->settings;
 140        struct spi_device *spi = msg->spi;
 141        unsigned cs_change = 0;
 142        struct spi_transfer *t;
 143        bool is_first = true;
 144        int status = 0;
 145        bool is_last;
 146
 147        spi_xcomm_chipselect(spi_xcomm, spi, true);
 148
 149        list_for_each_entry(t, &msg->transfers, transfer_list) {
 150
 151                if (!t->tx_buf && !t->rx_buf && t->len) {
 152                        status = -EINVAL;
 153                        break;
 154                }
 155
 156                status = spi_xcomm_setup_transfer(spi_xcomm, spi, t, &settings);
 157                if (status < 0)
 158                        break;
 159
 160                is_last = list_is_last(&t->transfer_list, &msg->transfers);
 161                cs_change = t->cs_change;
 162
 163                if (cs_change ^ is_last)
 164                        settings |= BIT(5);
 165                else
 166                        settings &= ~BIT(5);
 167
 168                if (t->rx_buf) {
 169                        spi_xcomm->settings = settings;
 170                        status = spi_xcomm_sync_config(spi_xcomm, t->len);
 171                        if (status < 0)
 172                                break;
 173                } else if (settings != spi_xcomm->settings || is_first) {
 174                        spi_xcomm->settings = settings;
 175                        status = spi_xcomm_sync_config(spi_xcomm, 0);
 176                        if (status < 0)
 177                                break;
 178                }
 179
 180                if (t->len) {
 181                        status = spi_xcomm_txrx_bufs(spi_xcomm, spi, t);
 182
 183                        if (status < 0)
 184                                break;
 185
 186                        if (status > 0)
 187                                msg->actual_length += status;
 188                }
 189                status = 0;
 190
 191                spi_transfer_delay_exec(t);
 192
 193                is_first = false;
 194        }
 195
 196        if (status != 0 || !cs_change)
 197                spi_xcomm_chipselect(spi_xcomm, spi, false);
 198
 199        msg->status = status;
 200        spi_finalize_current_message(master);
 201
 202        return status;
 203}
 204
 205static int spi_xcomm_probe(struct i2c_client *i2c,
 206        const struct i2c_device_id *id)
 207{
 208        struct spi_xcomm *spi_xcomm;
 209        struct spi_master *master;
 210        int ret;
 211
 212        master = spi_alloc_master(&i2c->dev, sizeof(*spi_xcomm));
 213        if (!master)
 214                return -ENOMEM;
 215
 216        spi_xcomm = spi_master_get_devdata(master);
 217        spi_xcomm->i2c = i2c;
 218
 219        master->num_chipselect = 16;
 220        master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_3WIRE;
 221        master->bits_per_word_mask = SPI_BPW_MASK(8);
 222        master->flags = SPI_MASTER_HALF_DUPLEX;
 223        master->transfer_one_message = spi_xcomm_transfer_one;
 224        master->dev.of_node = i2c->dev.of_node;
 225        i2c_set_clientdata(i2c, master);
 226
 227        ret = devm_spi_register_master(&i2c->dev, master);
 228        if (ret < 0)
 229                spi_master_put(master);
 230
 231        return ret;
 232}
 233
 234static const struct i2c_device_id spi_xcomm_ids[] = {
 235        { "spi-xcomm" },
 236        { },
 237};
 238MODULE_DEVICE_TABLE(i2c, spi_xcomm_ids);
 239
 240static struct i2c_driver spi_xcomm_driver = {
 241        .driver = {
 242                .name   = "spi-xcomm",
 243        },
 244        .id_table       = spi_xcomm_ids,
 245        .probe          = spi_xcomm_probe,
 246};
 247module_i2c_driver(spi_xcomm_driver);
 248
 249MODULE_LICENSE("GPL");
 250MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 251MODULE_DESCRIPTION("Analog Devices AD-FMCOMMS1-EBZ board I2C-SPI bridge driver");
 252