Шина SPI состоит из четырех линий связи.,полный дуплекс、Последовательная синхронная связь «главный-подчиненный»,Передавать 8 бит за раз,Сначала высокая позиция,Низко сзади.
Подсистема шины режима «главный-подчиненный» в Linux использует ту же идею разделения.,Конкретные стратегии разделения аналогичны.,Также разделен науровень драйвера устройства、основной слой、уровень водителя автобуса。Подробный анализ конкретных стратегий разделения см.Анализ подсистемы I2C драйвера LinuxПромежуточная проверкаI2CОбъяснение структуры подсистемы。Автор здесь иI2Cаналогия с подсистемой,Перечислите имена структур данных.
| I2C | SPI |
---|---|---|
Хост-адаптер (контроллер) | struct i2c_adapter | struct spi_master |
Как управлять контроллером машины | struct i2c_algorithm | struct spi_bitbang |
Ведомое устройство | struct i2c_client | struct spi_device |
Ведомое устройство Информация о плате | struct i2c_board_info | struct spi_board_info |
Ведомое устройствоводить машину | struct i2c_driver | struct spi_driver |
Полный пакет | struct i2c_msg | struct spi_transfer |
Инкапсуляция нескольких полных пакетов данных | никто | struct spi_message |
Поскольку архитектура подсистемы аналогична шинам типа I2C,Поэтому он не будет расширяться на некоторые повторяющиеся части.,Подробный анализ см.Анализ подсистемы I2C драйвера LinuxАналитические методы в。
Базовый уровень SPIКод находится по адресуdrivers/spi/spi.c
середина, Начните анализ с функции init
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type); /* Регистрация шины SPI */
if (status < 0)
goto err1;
status = class_register(&spi_master_class); /* Зарегистрировать класс SPI */
if (status < 0)
goto err2;
return 0;
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}
spi_init
Функция похожа наI2Cосновной Функция init в слое делает те же две вещи,Регистрация шины SPI и создание классов SPI,Это основная процедура модели драйвера ядра.,Я не скажу больше этого. Далее давайте посмотрим на функцию сопоставления
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
const struct spi_device *sdev)
{
while (id->name[0]) {
if (!strcmp(sdev->modalias, id->name))
return id;
id++;
}
return NULL;
}
/* Метод сопоставления шины SPI */
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0;
}
Видно, что сопоставление устройства SPI и драйвера заключается в том, чтобы сначала сопоставить имя в таблице id_table и модали устройства, а затем сопоставить имя драйвера и модали устройства.
Драйвер контроллера SPI, а именно уровень водителя автобусародыdrivers/spi/spi_s3c24xx. c
середина,Начните анализ с функции init。
static int __init s3c24xx_spi_init(void)
{
return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
}
найдетSPIконтроллерводить машину Он не используетсяplatform_driver_register
Интерфейс для регистрации,Вместо этого используется другой интерфейсplatform_driver_probe
, По сути, это выделенная платформенная шина, предоставляемая ядром и не поддерживающая горячую замену.водить машинуинтерфейс регистрации,Этот интерфейс принимает два параметра,Первый хорошо известенstruct platform_driver
,Вторая функция — зонд.,когдаводить машину Сопоставьте устройствоначальство Это будет вызвано позжеprobeфункция。войти в s3c24xx_spi_probe
функцияпровести анализ,Функция зонда имеет много кода,Анализируйте по разделам.
struct s3c2410_spi_info *pdata;
struct s3c24xx_spi *hw;
struct spi_master *master;
struct resource *res;
int err = 0;
/* Создать экземпляр spi-контроллера */
master = spi_alloc_master(&pdev->dev, squdongqudongizeof(struct s3c24xx_spi));
if (master == NULL) {
dev_err(&pdev->dev, "No memory for spi_master\n");
err = -ENOMEM;
goto err_nomem;
}
/* Получите частную структуру данных spi и инициализируйте ее как пустую. */
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct s3c24xx_spi));
/* Установить личные данные spi*/
hw->master = spi_master_get(master);
hw->pdata = pdata = pdev->dev.platform_data;
hw->dev = &pdev->dev;
if (pdata == NULL) {
dev_err(&pdev->dev, "No platform data supplied\n");
err = -ENOENT;
goto err_no_pdata;
}
platform_set_drvdata(pdev, hw);
init_completion(&hw->done); /* завершение инициализации, Используется для синхронизации ввода-вывода*/
После создания экземпляра контроллера SPI установите частные данные SPI, а затем инициализируйте завершение.
/* initialise fiq handler */
s3c24xx_spi_initfiq(hw); /* Инициализируйте обработчик в структуре s3c24xx_spi и привяжите к нему функцию обработки прерываний. */
/* setup the master state. */
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->num_chipselect = hw->pdata->num_cs;
master->bus_num = pdata->bus_num;
Этот раздел инициализируетs3c24xx_spi
Структуравhandler,Привяжите к нему функцию обработчика прерываний,Затем установите режим SPI, поддерживаемый хост-контроллером.,Установите номер линии выбора чипа ведущего устройства и номер шины.
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
hw->bitbang.chipselect = s3c24xx_spi_chipsel;
hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
hw->master->setup = s3c24xx_spi_setup;
hw->master->cleanup = s3c24xx_spi_cleanup;
bitbang представляет метод работы SPI. Ключом к этому разделу является заполнение параметра setup_transfer, который является методом передачи.
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
err = -ENOENT;
goto err_no_iores;
}
hw->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
if (hw->ioarea == NULL) {
dev_err(&pdev->dev, "Cannot reserve region\n");
err = -ENXIO;
goto err_no_iores;
}
hw->regs = ioremap(res->start, resource_size(res));
if (hw->regs == NULL) {
dev_err(&pdev->dev, "Cannot map IO\n");
err = -ENXIO;
goto err_no_iomap;
}
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq < 0) {
dev_err(&pdev->dev, "No IRQ specified\n");
err = -ENOENT;
goto err_no_irq;
}
err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);
if (err) {
dev_err(&pdev->dev, "Cannot claim IRQ\n");
goto err_no_irq;
}
hw->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(hw->clk)) {
dev_err(&pdev->dev, "No clock for device\n");
err = PTR_ERR(hw->clk);
goto err_no_clk;
}
/* setup any gpio we can */
if (!pdata->set_cs) {
if (pdata->pin_cs < 0) {
dev_err(&pdev->dev, "No chipselect pin\n");
goto err_register;
}
err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));
if (err) {
dev_err(&pdev->dev, "Failed to get gpio for cs\n");
goto err_register;
}
hw->set_cs = s3c24xx_spi_gpiocs;
gpio_direction_output(pdata->pin_cs, 1);
} else
hw->set_cs = pdata->set_cs;
s3c24xx_spi_initialsetup(hw);
s3c24xx_spi_initialsetup(hw); /* spi Инициализация контроллера */
/* register our spi controller */
/* Внутренне последний spi_register_master используется для регистрации контроллера SPI. */
err = spi_bitbang_start(&hw->bitbang);
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master\n");
goto err_register;weiyu
}
Этот раздел тесно связан с конкретным оборудованием.,Начните с получения ресурсов платформы,Затем мы сделали IO-маппинг и Привязку соответственно. приложения прерывания и функции обработки прерывания、Инициализация часов и выбор чипаGPIOприменил и потянул высоко。Последний ключ - позвонитьs3c24xx_spi_initialsetup
функция,Долженфункция Внутри был сделан последний звонокspi_register_master
Способ регистрацииSPIконтроллер。добрый СравниватьI2Cсуществоватьprobeфункциясерединаназываетсяi2c_add_numbered_adapter
функция,Он будет сканировать информацию о плате SPI внутри.,Затем используйте информацию о плате для создания устройств SPI.,и будетконтроллерspi_master
подсел наspi_device
начальство,впоследствиисуществовать Уровень драйвера устройства SPIсередина Зарегистрировать устройствоводить машинупозвони послеprobeфункция会获取到Долженspi_device
,Тогда ты сможешь пройтиspi_device
серединаустановленspi_master
позвонитьконтроллер Как работатьspi_bitbang_transfer
для передачи данных。Следует отметить, чтоSPIиI2CПредоставляется общее оборудованиеводить машинудругой,Генерация узлов устройства не завершена в зарегистрированном хост-контроллере.,Скорее это делается в устройстве общего назначения.,Этот абзац начинается с уровня драйвера Анализ устройства можно посмотреть. Эта логика аналогична I2C, поэтому мы не будем ссылаться на Анализ. исходного код. (Ладно, как всегда ленив, QAQ)
SPIОбщее оборудованиеводить машинуродыdrivers/spi/spidev.c
середина,Начните с функции init.
static struct spi_driver spidev_spi_driver = {
.driver = {
.name = "spidev",
.owner = THIS_MODULE,
},
.probe = spidev_probe,
.remove = __devexit_p(spidev_remove),
};
static int __init spidev_init(void)
{
int status;
/* Claim our 256 reserved device numbers. Then register a class
* that will key udev/mdev to add/remove /dev nodes. Last, register
* the driver which manages those device numbers.
*/
BUILD_BUG_ON(N_SPI_MINORS > 256);
/* Зарегистрируйтесь в качестве драйвера символьного устройства и предоставьте интерфейс вызова для уровня приложения. */
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
if (status < 0)
return status;
/* Создать класс spidev */
spidev_class = class_create(THIS_MODULE, "spidev");
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class);
}
/* вызовосновной Интерфейс, предоставляемый слоем для регистрации драйверов устройств */
status = spi_register_driver(&spidev_spi_driver);
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
return status;
}
Напишу еще раз, когда будет время. Давай сначала отдохнем.
Автор этой статьи: Ifan Tsai (кай-кай)
Ссылка на эту статью: https://cloud.tencent.com/developer/article/2164594
Заявление об авторских правах: Эта статья принимает Creative Commons Attribution-NonCommercial-ShareAlike 4.0 Международное лицензионное соглашение Дайте разрешение. При перепечатке просьба указывать источник!