Linux драйвер SPI ядра не включает CS перед записью - PullRequest
0 голосов
/ 04 февраля 2020

Я пытаюсь написать драйвер SPI пространства ядра и заставить его связываться с платой Arduino, для начала. Я выдаю простую функцию spi_write в пространстве ядра. У меня проблема в том, что Chip Select или Slave Select (SS) включены, когда драйвер проверяется После spi_write он отключается и никогда больше не устанавливается в HIGH. Поэтому я могу писать только один раз из драйвера SPI ядра linux.

Драйвер выглядит следующим образом:

#define MIBI_READ(mibi_priv, reg)   ((mibi_priv)->bops->read((mibi_priv)->dev, reg))
#define MIBI_WRITE(mibi_priv, reg, val) ((mibi_priv)->bops->write((mibi_priv)->dev, reg, val))

struct spi_bus_ops {
    u16 bustype;
    int (*read)(struct device *, unsigned char);
    int (*read_block)(struct device *, unsigned char, int, void *);
    int (*write)(struct device *, unsigned char, unsigned char);
};

struct mibi {
    struct device *dev;
    const struct spi_bus_ops *bops;
};

static const struct spi_bus_ops spi_bops = {
    .bustype    = BUS_SPI,
    .write      = mibi_spi_write,
    .read       = mibi_spi_read,
    .read_block = mibi_spi_read_block,
};

static int mibi_spi_probe(struct spi_device *spi)
{
    struct mibi *mibi_priv;

    dev_info(&spi->dev, "mibi_spi probed\n");

    /* send the spi operations */
    mibi_priv = mibi_probe(&spi->dev, &spi_bops);

    if (IS_ERR(mibi_priv))
        return PTR_ERR(mibi_priv);

    /* Attach the SPI device to the private structure */
    spi_set_drvdata(spi, mibi_priv);

    dev_info(&spi->dev, "mibi_spi exited\n");

    return 0;
}

static int mibi_spi_write(struct device *dev,
                 unsigned char reg, unsigned char val)
{
    struct spi_device *spi = to_spi_device(dev);

    dev_info(dev, "mibi_spi_write entered\n");

    u8 buf[2];

    buf[0] = reg;
    buf[1] = val;

    dev_info(dev, "mibi_spi_write exited\n");

    return spi_write(spi, buf, sizeof(buf));
}

struct mibi *mibi_probe(struct device *dev,
                  const struct spi_bus_ops *bops)
{
    struct mibi *mibi_priv; /* declare our private structure */
    struct spi_device *spi = to_spi_device(dev);
    int err, ret;
    u8 revid;

    dev_info(dev, "mibi probed\n");

    /* Allocate private structure*/
    mibi_priv = devm_kzalloc(dev, sizeof(*mibi_priv), GFP_KERNEL);
    if (!mibi_priv) {
        dev_err(dev, "Failed to allocate memory\n");
        err = -ENOMEM;
        goto err_out;
    }

    /* Initialize our private structure */
    mibi_priv->dev = dev;

    /* Store the SPI operations in our private structure */
    mibi_priv->bops = bops;

    /* Sysfs registration */
    ret = sysfs_create_group(&spi->dev.kobj, &mibi_sysfs_group);
    if (ret < 0)
    {
        dev_err(dev, "Couldn't register sysfs group\n");
        return ret;
    }

    dev_info(dev, "mibi exited\n");

    return mibi_priv;

 err_out:
    return ERR_PTR(err);
}

Devicetree выглядит следующим образом:

&spi0 {
    mibi0: mibi@0 {
        compatible = "mibi";
        reg = <0>;
        spi-max-frequency = <5000000>;
    };
    spidev0: spidev@0 {
        compatible = "spidev";
        reg = <0>;
        spi-max-frequency = <125000000>;
    };
};

Из обратного вызова sysfs я просто вызываю MIBI_WRITE(mibi_priv, 0x01, 0x06);.

Сторона Arduino выглядит следующим образом:

//Initialize SPI slave.
void SlaveInit(void) {
  // Initialize SPI pins.
  pinMode(SCK, INPUT);
  pinMode(MOSI, INPUT);
  pinMode(MISO, INPUT);
  pinMode(SS, INPUT);
  // Enable SPI as slave.
  SPCR = (1 << SPE);
}

// SPI Transfer.
byte SPItransfer(byte value) {
  SPDR = value;
  while(!(SPSR & (1<<SPIF)));
  delay(10);
  return SPDR;
}

void setup() {
  Serial.begin(9600);
  SlaveInit();
}

void loop()
{
// Slave Enabled?
  if (!digitalRead(SS)) {
    // Yes, first time?
    if (SSlast != LOW) {
      // Yes, take MISO pin.
      pinMode(MISO, OUTPUT);
      Serial.println("***Slave Enabled.");
      // Write -1 slave response code and receive master command code
      byte rx = SPItransfer(255);
      Serial.println("Initial -1 slave response code sent");
      Serial.println("rx:" + String(rx) + ".");
      // Update SSlast.
      SSlast = LOW;
    }
  }
  else {
    // No, first time?
    if (SSlast != HIGH) {
      // Yes, release MISO pin.
      pinMode(MISO, INPUT);
      Serial.println("Slave Disabled.");
      // Update SSlast.
      SSlast = HIGH;
    }
  }
}

Я могу написать один раз из ядра Linux в Arduino, но тогда СС всегда низок, поэтому я больше не могу писать. Как ты думаешь, что мне здесь не хватает? Есть ли способ убедиться, что SS изменяется, когда я вызываю spi_write? (Может быть, проблема связана с подключением spidev и mibi к одной и той же CS?)

Буду признателен за общие советы по достижению этой цели, а также за поддержку решения моей проблемы. Спасибо.

РЕДАКТИРОВАТЬ Выбор чипа по-прежнему не изменяется, даже если я преобразовал драйвер для использования Regmap-SPI. Ситуация такая же, как и раньше. Я могу написать один раз в Arduino. Но выбор чипа не меняется после проверки драйвера. Как мне работать с Chip Select в подсистеме Linux SPI? Найдите окончательное состояние драйвера следующим образом:

#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/regmap.h>

/* Top level struct */
struct mibi_state {
    struct regmap *regmap;
    struct spi_device *spi;
    u8 buffer[4];
};

/* IIO-Userspace communication channels */
#define MIBI_NUM_IIO_CHANNELS 2
static const struct iio_chan_spec mibi_channels[MIBI_NUM_IIO_CHANNELS] = {
    {
        .type       = IIO_VOLTAGE,
        .indexed    = 1,
        .output     = 1,
        .channel    = 0,
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    },
    {
        .type       = IIO_VOLTAGE,
        .indexed    = 1,
        .output     = 1,
        .channel    = 1,
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    },
};

/* IIO-Userspace communication read callback */
static int mibi_read_raw(struct iio_dev *indio_dev,
    struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{
    int ret;
    struct mibi_state *st = iio_priv(indio_dev);

    dev_info(&st->spi->dev, "Entered read_raw\n");

    u32 read_value;

    switch (mask) {
        case IIO_CHAN_INFO_RAW:

            ret = regmap_read(st->regmap, 0x01, &read_value);
            if (ret) {
                dev_err(&st->spi->dev, "Error reading in read_raw callback\n");
                return ret;
            }
            dev_info(&st->spi->dev, "regmap_read 0x01 %d\n",read_value);

            *val  = read_value;

            dev_info(&st->spi->dev, "read_raw value %d\n", *val);

            return IIO_VAL_INT;

        default:
            return -EINVAL;
    }
}

/* IIO-Userspace communication write callback */
static int mibi_write_raw(struct iio_dev *indio_dev,
                struct iio_chan_spec const *chan,
                int val, int val2, long mask)
{
    struct mibi_state *st = iio_priv(indio_dev);

    u8 value;
    value = 0;
    dev_info(&st->spi->dev, "Entered write_raw\n");

    switch (mask) {
        case IIO_CHAN_INFO_RAW:
            value = val;
            dev_info(&st->spi->dev, "regmap_write 0x01 %d\n",value);
            return regmap_write(st->regmap, 0x01, value);
        default :
            return -EINVAL;
    }
}

/* IIO-Userspace communication */
static const struct iio_info mibi_info = {
    .read_raw = &mibi_read_raw,
    .write_raw = &mibi_write_raw,
};

/* Regmap-SPI config */
static const struct regmap_config mibi_spi_regmap_config = {
    .reg_bits = 8,
    .val_bits = 8,
     /* Setting bits 7 and 6 enables multiple-byte read */
    .read_flag_mask = BIT(7) | BIT(6),
};

/* Probe */
static int mibi_probe(struct spi_device *spi)
{
    struct iio_dev *indio_dev;
    struct mibi_state *st;
    struct regmap *regmap;
    int err;
    int ret;
    dev_info(&spi->dev, "mibi_probe() entered.\n");

    const struct spi_device_id *id = spi_get_device_id(spi);

    /* Allocate IIO */
    indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
    if (indio_dev == NULL)
        return -ENOMEM;

    st = iio_priv(indio_dev);

    /* Allocate regmap */
    regmap = devm_regmap_init_spi(spi, &mibi_spi_regmap_config);
    if (IS_ERR(regmap)) {
        dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
            PTR_ERR(regmap));
        return PTR_ERR(regmap);
    }

    /* Populate top level struct */
    st->regmap = regmap;
    st->spi = spi;

    /* Populate IIO struct */
    indio_dev->dev.parent = &spi->dev;
    indio_dev->channels = mibi_channels;
    indio_dev->info = &mibi_info;
    indio_dev->name = id->name;
    indio_dev->num_channels = MIBI_NUM_IIO_CHANNELS;
    indio_dev->modes = INDIO_DIRECT_MODE;

    /* IIO Register */
    err = devm_iio_device_register(&spi->dev, indio_dev);
    if (err < 0)
        return err;

    ret = regmap_write(st->regmap, 0x01, 0x06);
    if (ret < 0) {
        dev_err(&spi->dev, "Error writing to device: %d\n", ret);
        return ret;
    }

    return 0;
}


static const struct of_device_id mibi_dt_ids[] = {
    { .compatible = "mozcelikors,mibi", },
    { }
};

MODULE_DEVICE_TABLE(of, mibi_dt_ids);

static const struct spi_device_id mibi_id[] = {
    { .name = "mibi", },
    { }
};
MODULE_DEVICE_TABLE(spi, mibi_id);


static struct spi_driver mibi_driver = {
    .driver = {
        .name   = "mibi",
        .owner  = THIS_MODULE,
        .of_match_table = mibi_dt_ids,
    },
    .probe      = mibi_probe,
    .id_table   = mibi_id,
};

module_spi_driver(mibi_driver);
...