Sus referencias son buenas pero carecen de una definición de qué es un dispositivo de plataforma . Hay uno en LWN. Lo que podemos aprender de esta página:
-
Los dispositivos de plataforma son inherentemente no detectables , es decir, el hardware no puede decir "¡Oye! ¡Estoy presente!" al software. Ejemplos típicos son los dispositivos i2c,
kernel/Documentation/i2c/instantiating-devices
estados:A diferencia de los dispositivos PCI o USB, los dispositivos I2C no se enumeran a nivel de hardware (en tiempo de ejecución). En cambio, el software debe saber (en tiempo de compilación) qué dispositivos están conectados en cada segmento de bus I2C. Por lo tanto, USB y PCI son no dispositivos de la plataforma.
-
Los dispositivos de la plataforma están vinculados a los controladores por nombres coincidentes ,
- Los dispositivos de plataforma se deben registrar muy pronto durante el arranque del sistema. Porque a menudo son fundamentales para el resto del sistema (plataforma) y sus controladores.
Básicamente, la pregunta "¿es un dispositivo de plataforma o un dispositivo estándar? " es más una cuestión de qué bus usa . Para trabajar con un dispositivo de plataforma en particular, debe:
- registrar un controlador de plataforma que administrará este dispositivo. Debe definir un único nombre,
- registre su dispositivo de plataforma , definiendo el mismo nombre que el controlador.
El controlador de plataforma es para aquellos dispositivos que están en chip.
No es cierto (en teoría, pero es cierto en la práctica). Los dispositivos i2c no son onChip, pero son dispositivos de plataforma porque no se pueden detectar. También podemos pensar en dispositivos onChip que son normales dispositivos. Ejemplo:un chip PCI GPU integrado en un procesador x86 moderno. Es reconocible, por lo que no es un dispositivo de plataforma.
Los controladores de dispositivos normales son para aquellos que están interconectados con el chip del procesador. antes de encontrar un controlador i2c.
No es verdad. Muchos normales los dispositivos están conectados al procesador, pero no a través de un bus i2c. Ejemplo:un ratón USB.
[EDITAR] En su caso, eche un vistazo a drivers/usb/host/ohci-pnx4008.c
, que es un dispositivo de plataforma de controlador de host USB (aquí, el controlador de host USB no se puede detectar, mientras que los dispositivos USB, que se conectarán a él, sí). Es un dispositivo de plataforma registrado por el archivo de placa (arch/arm/mach-pnx4008/core.c:pnx4008_init
). Y dentro de su función de sondeo, registra su dispositivo i2c en el bus con i2c_register_driver
. Podemos inferir que el conjunto de chips del controlador USB Host habla con la CPU a través de un bus i2c.
¿Por qué esa arquitectura? Porque, por un lado, este dispositivo puede considerarse un dispositivo i2c simple que proporciona algunas funcionalidades al sistema. Por otro lado, es un dispositivo compatible con USB Host. Necesita registrarse en la pila USB (usb_create_hcd
). Por lo tanto, probar solo i2c será insuficiente. Echa un vistazo a Documentation/i2c/instantiating-devices
.
Ejemplos de código de módulo mínimo
Tal vez la diferencia también se aclare con algunos ejemplos concretos.
Ejemplo de dispositivo de plataforma
Código:
- controlador aguas arriba
- controlado por un dispositivo virtual QEMU mínimo.
- Modificaciones de entrada de DTS en el kernel de Linux
Más notas de integración en:https://stackoverflow.com/a/44612957/895245
Vea cómo:
- las direcciones de registro e interrupción están codificadas en el árbol de dispositivos y coinciden con QEMU
-M versatilepb
descripción de la máquina, que representa el SoC - no hay forma de quitar el hardware del dispositivo (ya que es parte del SoC)
- el controlador correcto es seleccionado por el
compatible
propiedad del árbol de dispositivos que coincide conplatform_driver.name
en el controlador platform_driver_register
es la interfaz de registro principal
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
MODULE_LICENSE("GPL");
static struct resource res;
static unsigned int irq;
static void __iomem *map;
static irqreturn_t lkmc_irq_handler(int irq, void *dev)
{
/* TODO this 34 and not 18 as in the DTS, likely the interrupt controller moves it around.
* Understand precisely. 34 = 18 + 16. */
pr_info("lkmc_irq_handler irq = %d dev = %llx\n", irq, *(unsigned long long *)dev);
/* ACK the IRQ. */
iowrite32(0x9ABCDEF0, map + 4);
return IRQ_HANDLED;
}
static int lkmc_platform_device_probe(struct platform_device *pdev)
{
int asdf;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
dev_info(dev, "probe\n");
/* Play with our custom poperty. */
if (of_property_read_u32(np, "lkmc-asdf", &asdf) ) {
dev_err(dev, "of_property_read_u32\n");
return -EINVAL;
}
if (asdf != 0x12345678) {
dev_err(dev, "asdf = %llx\n", (unsigned long long)asdf);
return -EINVAL;
}
/* IRQ. */
irq = irq_of_parse_and_map(dev->of_node, 0);
if (request_irq(irq, lkmc_irq_handler, 0, "lkmc_platform_device", dev) < 0) {
dev_err(dev, "request_irq");
return -EINVAL;
}
dev_info(dev, "irq = %u\n", irq);
/* MMIO. */
if (of_address_to_resource(pdev->dev.of_node, 0, &res)) {
dev_err(dev, "of_address_to_resource");
return -EINVAL;
}
if (!request_mem_region(res.start, resource_size(&res), "lkmc_platform_device")) {
dev_err(dev, "request_mem_region");
return -EINVAL;
}
map = of_iomap(pdev->dev.of_node, 0);
if (!map) {
dev_err(dev, "of_iomap");
return -EINVAL;
}
dev_info(dev, "res.start = %llx resource_size = %llx\n",
(unsigned long long)res.start, (unsigned long long)resource_size(&res));
/* Test MMIO and IRQ. */
iowrite32(0x12345678, map);
return 0;
}
static int lkmc_platform_device_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "remove\n");
free_irq(irq, &pdev->dev);
iounmap(map);
release_mem_region(res.start, resource_size(&res));
return 0;
}
static const struct of_device_id of_lkmc_platform_device_match[] = {
{ .compatible = "lkmc_platform_device", },
{},
};
MODULE_DEVICE_TABLE(of, of_lkmc_platform_device_match);
static struct platform_driver lkmc_plaform_driver = {
.probe = lkmc_platform_device_probe,
.remove = lkmc_platform_device_remove,
.driver = {
.name = "lkmc_platform_device",
.of_match_table = of_lkmc_platform_device_match,
.owner = THIS_MODULE,
},
};
static int lkmc_platform_device_init(void)
{
pr_info("lkmc_platform_device_init\n");
return platform_driver_register(&lkmc_plaform_driver);
}
static void lkmc_platform_device_exit(void)
{
pr_info("lkmc_platform_device_exit\n");
platform_driver_unregister(&lkmc_plaform_driver);
}
module_init(lkmc_platform_device_init)
module_exit(lkmc_platform_device_exit)
Ejemplo de dispositivo sin plataforma PCI
- controlador aguas arriba
- controlado por un dispositivo virtual QEMU mínimo
Vea cómo:
- Las direcciones de registro e interrupción son asignadas dinámicamente por el sistema PCI, no se utiliza ningún árbol de dispositivos
- el controlador correcto es seleccionado por PCI
vendor:device
ID (QEMU_VENDOR_ID, EDU_DEVICE_ID
en el ejemplo). Esto está integrado en todos los dispositivos y los proveedores deben garantizar la exclusividad. - podemos insertar y quitar el dispositivo PCI con
device_add edu
ydevice_del edu
como podemos en la vida real. El sondeo no es automático, pero se puede realizar después del arranque conecho 1 > /sys/bus/pci/rescan
. Consulte también:¿Por qué se necesita el método de sondeo en los controladores de dispositivos de Linux además de init?
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#define BAR 0
#define CDEV_NAME "lkmc_hw_pci_min"
#define EDU_DEVICE_ID 0x11e9
#define QEMU_VENDOR_ID 0x1234
MODULE_LICENSE("GPL");
static struct pci_device_id id_table[] = {
{ PCI_DEVICE(QEMU_VENDOR_ID, EDU_DEVICE_ID), },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, id_table);
static int major;
static struct pci_dev *pdev;
static void __iomem *mmio;
static struct file_operations fops = {
.owner = THIS_MODULE,
};
static irqreturn_t irq_handler(int irq, void *dev)
{
pr_info("irq_handler irq = %d dev = %d\n", irq, *(int *)dev);
iowrite32(0, mmio + 4);
return IRQ_HANDLED;
}
static int probe(struct pci_dev *dev, const struct pci_device_id *id)
{
pr_info("probe\n");
major = register_chrdev(0, CDEV_NAME, &fops);
pdev = dev;
if (pci_enable_device(dev) < 0) {
dev_err(&(pdev->dev), "pci_enable_device\n");
goto error;
}
if (pci_request_region(dev, BAR, "myregion0")) {
dev_err(&(pdev->dev), "pci_request_region\n");
goto error;
}
mmio = pci_iomap(pdev, BAR, pci_resource_len(pdev, BAR));
pr_info("dev->irq = %u\n", dev->irq);
if (request_irq(dev->irq, irq_handler, IRQF_SHARED, "pci_irq_handler0", &major) < 0) {
dev_err(&(dev->dev), "request_irq\n");
goto error;
}
iowrite32(0x12345678, mmio);
return 0;
error:
return 1;
}
static void remove(struct pci_dev *dev)
{
pr_info("remove\n");
free_irq(dev->irq, &major);
pci_release_region(dev, BAR);
unregister_chrdev(major, CDEV_NAME);
}
static struct pci_driver pci_driver = {
.name = CDEV_NAME,
.id_table = id_table,
.probe = probe,
.remove = remove,
};
static int myinit(void)
{
if (pci_register_driver(&pci_driver) < 0) {
return 1;
}
return 0;
}
static void myexit(void)
{
pci_unregister_driver(&pci_driver);
}
module_init(myinit);
module_exit(myexit);