Referensi Anda bagus tetapi tidak memiliki definisi tentang apa yang dimaksud dengan perangkat platform . Ada satu di LWN. Apa yang dapat kita pelajari dari halaman ini:
-
Perangkat platform pada dasarnya tidak dapat ditemukan , yaitu perangkat keras tidak dapat mengatakan "Hei! Saya hadir!" ke perangkat lunak. Contoh umumnya adalah perangkat i2c,
kernel/Documentation/i2c/instantiating-devices
menyatakan:Tidak seperti perangkat PCI atau USB, perangkat I2C tidak disebutkan pada tingkat perangkat keras (pada saat dijalankan). Sebagai gantinya, perangkat lunak harus mengetahui (pada waktu kompilasi) perangkat mana yang terhubung pada setiap segmen bus I2C. Jadi USB dan PCI adalah tidak perangkat platform.
-
Perangkat platform terikat ke driver dengan nama yang cocok ,
- Perangkat platform harus didaftarkan paling awal selama booting sistem. Karena sering kali penting bagi sistem (platform) lainnya dan drivernya.
Jadi pada dasarnya, pertanyaan "apakah itu perangkat platform atau perangkat standar? " lebih merupakan pertanyaan tentang bus mana yang digunakannya . Untuk bekerja dengan perangkat platform tertentu, Anda harus:
- daftarkan driver platform yang akan mengelola perangkat ini. Itu harus mendefinisikan unik nama,
- daftarkan perangkat platform Anda , menentukan nama yang sama dengan driver.
Driver platform adalah untuk perangkat yang menggunakan chip.
Tidak benar (dalam teori, tetapi benar dalam praktek). Perangkat i2c bukan onChip, tetapi merupakan perangkat platform karena tidak dapat ditemukan. Kami juga dapat memikirkan perangkat onChip yang normal perangkat. Contoh:chip GPU PCI terintegrasi pada prosesor x86 modern. Itu dapat ditemukan, jadi bukan perangkat platform.
Driver perangkat normal adalah untuk yang dihubungkan ke chip prosesor. sebelum menemukan satu driver i2c.
Tidak benar. Banyak normal perangkat dihubungkan ke prosesor, tetapi tidak melalui bus i2c. Contoh:mouse USB.
[EDIT] Dalam kasus Anda, lihat drivers/usb/host/ohci-pnx4008.c
, yang merupakan perangkat platform pengontrol host USB (Di sini pengontrol host USB tidak dapat ditemukan, sedangkan perangkat USB, yang akan terhubung dengannya, adalah). Ini adalah perangkat platform yang didaftarkan oleh file papan (arch/arm/mach-pnx4008/core.c:pnx4008_init
). Dan di dalam fungsi penyelidikannya, ia mendaftarkan perangkat i2c-nya ke bus dengan i2c_register_driver
. Kami dapat menyimpulkan bahwa chipset pengontrol Host USB berbicara dengan CPU melalui bus i2c.
Mengapa arsitektur itu? Karena di satu sisi, perangkat ini dapat dianggap sebagai perangkat i2c telanjang yang menyediakan beberapa fungsionalitas ke sistem. Di sisi lain, ini adalah perangkat yang mendukung USB Host. Perlu mendaftar ke tumpukan USB (usb_create_hcd
). Jadi menyelidiki hanya i2c tidak akan cukup. Lihat Documentation/i2c/instantiating-devices
.
Contoh kode modul minimal
Mungkin perbedaannya juga akan menjadi lebih jelas dengan beberapa contoh nyata.
Contoh perangkat platform
Kode:
- pengemudi di hulu
- berbasis perangkat virtual QEMU minimal.
- Modifikasi entri DTS pada kernel Linux
Catatan integrasi lebih lanjut di:https://stackoverflow.com/a/44612957/895245
Lihat caranya:
- register dan alamat interupsi di-hardcode di pohon perangkat dan cocok dengan QEMU
-M versatilepb
deskripsi mesin, yang mewakili SoC - tidak ada cara untuk melepas perangkat keras perangkat (karena merupakan bagian dari SoC)
- driver yang benar dipilih oleh
compatible
properti pohon perangkat yang cocok denganplatform_driver.name
di driver platform_driver_register
adalah antarmuka register utama
#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)
Contoh perangkat non-platform PCI
- pengemudi di hulu
- minimal digerakkan oleh perangkat virtual QEMU
Lihat caranya:
- alamat register dan interupsi dialokasikan secara dinamis oleh sistem PCI, tidak ada hierarki perangkat yang digunakan
- driver yang benar dipilih oleh
vendor:device
PCI ID (QEMU_VENDOR_ID, EDU_DEVICE_ID
pada contoh). Ini dimasukkan ke dalam setiap perangkat, dan vendor harus memastikan keunikannya. - kita dapat memasukkan dan melepas perangkat PCI dengan
device_add edu
dandevice_del edu
seperti yang kita bisa dalam kehidupan nyata. Probing tidak otomatis, tetapi dapat dilakukan setelah booting denganecho 1 > /sys/bus/pci/rescan
. Lihat juga:Mengapa metode probe diperlukan di driver perangkat Linux selain 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);