Platform device model
Contract
| Field | Bound contract |
|---|---|
| Trigger | Implementing or debugging a platform_driver: the probe/remove lifecycle, resource acquisition, DT or ACPI property reads, sysfs attributes, deferred probe, or uevent and /dev node creation. |
| Authority | Read-only. Writes nothing. Chat output only. No remote mutation. |
| Side effect | Returns guidance, driver skeletons, and debug commands. No source files are modified. |
| Done | The object model, a probe/remove skeleton, the property and sysfs patterns, the deferred-probe rule, and a debug checklist are delivered. |
Inputs
- Driver code or question (required).
- Bus (optional):
platformby default; the same model coversambaand others. - Device origin (optional): the
compatiblevalue, ACPI id, or board file entry that instantiates the device. - Symptom (optional):
-EBUSY, a probe that defers forever, a missing/devnode, or aremovecrash.
Procedure
Name the device origin before writing code. Platform devices come from device tree via
of_platform_populate(), from ACPI tables, or from legacy board files. The origin decides which match table binds the driver. Done when: the origin is named and the match table that fits it is chosen.State the driver-model hierarchy for the bus: a
bus_type(platform, amba, pci, i2c, spi) carriesstruct deviceinstances, each bound to at most onestruct device_driver, and the core calls the driver'sprobewhen a match appears andremoveat unbind. Done when: the caller can restate the bus-device-driver-probe relationship.Write the probe/remove skeleton. Use
devm_*managed resources so the core unwinds them afterremovereturns;removereturnsvoidbecause the int-returning form was removed fromplatform_driverin kernel 6.11, and this tree's floor (LTS 6.18, mainline 7.2) is past it.static int my_probe(struct platform_device *pdev) { struct my_priv *priv; struct resource *res; void __iomem *base; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); platform_set_drvdata(pdev, priv); return 0; } static void my_remove(struct platform_device *pdev) { /* devm_* resources are freed by the core after this returns */ } static struct platform_driver my_pdrv = { .probe = my_probe, .remove = my_remove, .driver = { .name = "my-dev", .of_match_table = my_of_match, }, }; module_platform_driver(my_pdrv);Done when: the skeleton has the resource lookup, a managed remap, private-data storage, and the registration macro, and every manually freed resource corresponds to no
devm_*allocation.Read device properties through the unified property API, which resolves DT and ACPI behind one call; reach for
of_*accessors only for DT-specific semantics.u32 speed; device_property_read_u32(&pdev->dev, "clock-speed", &speed); bool has_feature = device_property_present(&pdev->dev, "feature-x");Done when: each property read names its type helper and handles the error return.
Expose driver state through a
sysfsattribute. Define it withDEVICE_ATTR_ROorDEVICE_ATTR_RW, format withsysfs_emit, and create it inprobe; list it in the driver's.dev_groupsto have the core create and remove it at bind and unbind.static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) { return sysfs_emit(buf, "ok\n"); } static DEVICE_ATTR_RO(status); /* in probe */ device_create_file(&pdev->dev, &dev_attr_status);Done when: the attribute is visible under
/sys/bus/platform/devices/<device>/and cannot leak after unbind.Apply the deferred-probe rule: return
-EPROBE_DEFERfromprobewhen a required clock, regulator, or other supplier is not ready, and let the core retry when suppliers appear. Check/sys/kernel/debug/devices_deferredto see who is waiting on whom. Done when: every optional-ordering dependency is either satisfied by DT ordering or handled with the return code.Debug the binding from the running system.
ls /sys/bus/platform/devices/ ls /sys/bus/platform/drivers/ cat /sys/kernel/debug/devices_deferred # debugfs mounted udevadm monitor # watch uevents for /dev nodesRoute deeper work:
device-treefor the hardware description,bus-drivers-i2c-spifor I2C/SPI children,writing-char-driversfor a userspace char interface. Done when: the reported symptom maps to one cause and one fix.
Failure and recovery
| Symptom | Cause | Recovery |
|---|---|---|
-EBUSY on probe |
Resource claimed twice | Check for duplicate nodes or an already-bound driver on the same region. |
No /dev node |
No char device registered | Register one; see writing-char-drivers. |
| Deferred forever | Supplier driver missing or never probes | Fix the DT dependency chain or enable the supplier driver. |
Crash in remove |
Manual free racing a devm_* allocation |
Remove the manual free or the devm_* call; keep one owner per resource. |
| Driver never binds | .name versus compatible confusion |
For DT devices the core matches of_match_table; .name matters only for board-file devices. |
Output
The device origin and match table; the driver-model relationship statement; the probe/remove skeleton; the property reads; the sysfs attribute with its creation path; the deferred-probe rule with the deferred list; the debug transcript; the routing to related skills.