Я пытаюсь написать драйвер 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);