J'ai déjà pensé au pilote de plate-forme ainsi qu'au pilote de périphérique normal comme:
Avant de rencontrer un pilote i2c ... Mais ici, je lis le pilote i2c multifonction défini comme pilote de plate-forme. J'étais passé par https://www.kernel.org/doc/Documentation/driver-model/platform.txt . Mais je n'arrivais toujours pas à avoir une idée claire de parvenir à une conclusion sur la façon de définir les pilotes, comme pour les appareils à puce et les appareils bien interfacés.
Veuillez expliquer à quelqu'un.
Vos références sont bonnes mais manquent de définition de ce qu'est un appareil de plateforme . Il y en a un sur LWN . Ce que nous pouvons apprendre de cette page:
Les périphériques de la plate-forme sont intrinsèquement non détectables , c'est-à-dire que le matériel ne peut pas dire "Hé! Je suis présent!" au Logiciel. Des exemples typiques sont les périphériques i2c, kernel/Documentation/i2c/instantiating-devices
États:
Contrairement aux périphériques PCI ou USB, les périphériques I2C ne sont pas énumérés au niveau matériel (au moment de l'exécution). Au lieu de cela, le logiciel doit savoir (au moment de la compilation) quels périphériques sont connectés sur chaque segment de bus I2C. Ainsi, USB et PCI sont des appareils de plate-forme pas.
Les périphériques de la plate-forme sont liés aux pilotes en faisant correspondre les noms ,
Donc, fondamentalement, la question " est-ce un périphérique de plate-forme ou un périphérique standard?" est plus une question de quel bus il utilise . Pour travailler avec un périphérique de plateforme particulier, vous devez:
Le pilote de plate-forme est destiné aux périphériques sur puce.
Pas vrai (en théorie, mais vrai en pratique). Les appareils i2c ne sont pas onChip, mais sont des appareils de plate-forme car ils ne sont pas détectables. Nous pouvons également penser aux appareils onChip qui sont des appareils normal. Exemple: une puce GPU PCI intégrée sur un processeur x86 moderne. Il est découvrable, donc pas un appareil de plate-forme.
Le pilote de périphérique normal est destiné à ceux qui sont interfacés avec la puce du processeur. avant de rencontrer un pilote i2c.
Pas vrai. De nombreux périphériques normal sont interfacés avec le processeur, mais pas via un bus i2c. Exemple: une souris USB.
[EDIT] Dans votre cas, jetez un œil à drivers/usb/Host/ohci-pnx4008.c
, qui est un périphérique de plate-forme de contrôleur hôte USB (ici, le contrôleur hôte USB n'est pas détectable, contrairement aux périphériques USB qui s'y connecteront). Il s'agit d'un périphérique de plate-forme enregistré par le fichier board (Arch/arm/mach-pnx4008/core.c:pnx4008_init
). Et dans sa fonction de sonde, il enregistre son périphérique i2c sur le bus avec i2c_register_driver
. Nous pouvons en déduire que le chipset du contrôleur hôte USB parle à le CPU via un bus i2c.
Pourquoi cette architecture? Parce que d'une part, cet appareil peut être considéré comme un appareil i2c nu fournissant certaines fonctionnalités au système. D'un autre côté, c'est un appareil compatible USB Host. Il doit s'enregistrer sur la pile USB (usb_create_hcd
). Donc, sonder uniquement i2c sera insuffisant. Jetez un œil à Documentation/i2c/instantiating-devices
.
Exemples de code de module minimal
Peut-être que la différence deviendra également plus claire avec quelques exemples concrets.
Exemple de périphérique de plate-forme
Code:
Autres notes d'intégration sur: https://stackoverflow.com/a/44612957/895245
Regarde comment:
-M versatilepb
description de la machine, qui représente le SoCcompatible
qui correspond à platform_driver.name
dans le piloteplatform_driver_register
est l'interface principale du registre#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)
Exemple de périphérique non plateforme PCI
Regarde comment:
vendor:device
ID (QEMU_VENDOR_ID, EDU_DEVICE_ID
sur l'exemple). Cela est intégré à chaque appareil et les fournisseurs doivent garantir l'unicité.device_add edu
et device_del edu
comme nous pouvons dans la vraie vie. Le sondage n'est pas automatique, mais peut être effectué après le démarrage avec echo 1 > /sys/bus/pci/rescan
. Voir aussi: Pourquoi la méthode de sonde est-elle nécessaire dans les pilotes de périphériques Linux en plus d'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);