Date: Mon, 04 Aug 2014 14:00:03 -0600 From: Ian Lepore <ian@FreeBSD.org> To: =?ISO-8859-1?Q?Mat=EDas?= Perret Cantoni <perretcantonim@gmail.com> Cc: freebsd-arm@freebsd.org Subject: Re: Two questions on Flattened Device Tree, newbus and device driver attaching Message-ID: <1407182403.56408.297.camel@revolution.hippie.lan> In-Reply-To: <CADLKG03f2KAEeTGCZTh=_ABufDfiWo6pSXW407aCcByon2BinA@mail.gmail.com> References: <CADLKG03f2KAEeTGCZTh=_ABufDfiWo6pSXW407aCcByon2BinA@mail.gmail.com>
next in thread | previous in thread | raw e-mail | index | archive | help
On Sun, 2014-08-03 at 16:41 -0300, Matas Perret Cantoni wrote: > Hello everyone! > I'm working with FreeBSD on the Zedboard (ported by Thomas Skibo > <http://www.thomasskibo.com/zedbsd/>). Currently I'm trying to fully > understand how the Flattened Device Tree (FDT) mechanism works and how it > integrates with FreeBSD. What I've already understand (I think) is: > > (1) how to represent devices, memory mapping/ranges, interrupts, etc... > in a Device Tree Source (DTS) file, > (2) how the newbus framework works, and > (3) how the kernel manages resources, devices and drivers. > > Although I've read all the documents I could find (and some source code) > there are still two things I don't understand: > > *1) The DTS source file and CPUs definition:* > > The DTS file for the zedboard, /release/10.0.0/sys/boot/fdt/dts/zedboard.dts > (here > <https://svnweb.freebsd.org/base/release/10.0.0/sys/boot/fdt/dts/zedboard.dts?revision=260789&view=markup>), > has the CPU definition all commented out: > > ... > // cpus { > // #address-cells = <1>; > // #size-cells = <0>; > // cpu@0 { > // device-type = "cpu"; > // model = "ARM Cortex-A9"; > // }; > // }; > ... > > This sounds really strange to me! How can the system tell the CPU it's > running on? I'v found some other DTS files for other boards that *do > define* it's > CPUs. For example: > > imx53x.dtsi: (here > <https://svnweb.freebsd.org/base/release/10.0.0/sys/boot/fdt/dts/imx53x.dtsi?view=markup> > ) > > ... > cpus { > #address-cells = <1>; > #size-cells = <0>; > > cpu@0 { > device_type = "cpu"; > compatible = "ARM,MCIMX535"; > reg = <0x0>; > d-cache-line-size = <32>; > i-cache-line-size = <32>; > d-cache-size = <0x8000>; > i-cache-size = <0x8000>; > l2-cache-line-size = <32>; > l2-cache-line = <0x40000>; > timebase-frequency = <0>; > bus-frequency = <0>; > clock-frequency = <0>; > }; > ... > > or: > > p1020rdb.dts (here > <https://svnweb.freebsd.org/base/release/10.0.0/sys/boot/fdt/dts/p1020rdb.dts?view=markup> > ) > > ... > cpus { > #address-cells = <1>; > #size-cells = <0>; > > PowerPC,P1020@0 { > device_type = "cpu"; > reg = <0x0>; > next-level-cache = <&L2>; > }; > > PowerPC,P1020@1 { > device_type = "cpu"; > reg = <0x1>; > next-level-cache = <&L2>; > }; > }; > ... > > *So my first question is: How can the system tell on wich CPU it running > on? can I add the CPUs definition in my DTS file?* > > 2) The 'compatible' property of a node, finding the driver and attaching it > to the corresponding newbus node: During autoconfiguration the the .dtb > (device tree blob) file is parsed and for each node of the device three the > autoconfiguration systen will create a new newbus node (with > device_add_child()) and then it will find a suitable driver for it and will > attach it: > > */ > * This function is the core of the device autoconfiguration > * system. Its purpose is to select a suitable driver for a device and > * then call that driver to initialise the hardware appropriately. The > * driver is selected by calling the DEVICE_PROBE() method of a set of > * candidate drivers and then choosing the driver which returned the > * best value. This driver is then attached to the device using > * device_attach(). > * > * The set of suitable drivers is taken from the list of drivers in > * the parent device's devclass. If the device was originally created > * with a specific class name (see device_add_child()), only drivers > * with that name are probed, otherwise all drivers in the devclass > * are probed. If no drivers return successful probe values in the > * parent devclass, the search continues in the parent of that > * devclass (see devclass_get_parent()) if any. > * > * @param dev the device to initialise > */ > > int device_probe(device_t dev) > > (I extracted this from here > <https://svnweb.freebsd.org/base/release/10.0.0/sys/kern/subr_bus.c?revision=260789&view=markup> > ) > > I believe that the autoconfiguration system uses the > fdt_node_check_compatible() function (from fdtlib > <https://svnweb.freebsd.org/base/release/10.0.0/sys/contrib/libfdt/libfdt.h?revision=260789&view=markup>) > to get the "compatible" property out of each node of the .dtb blob and then > it calls device_probe() to find the best driver and attach it to the > corresponding newbus node. (is this correct?). > > From the ePAPR > <https://www.power.org/wp-content/uploads/2012/06/Power_ePAPR_APPROVED_v1.1.pdf> > standard > we have this: > > 2.3.1 compatible > Property: compatible > Value type: <stringlist> > > Description: The compatible property value consists of one or more strings > that define the specific > programming model for the device. This list of strings should be used by a > client program for > device driver selection. The property value consists of a concatenated list > of null terminated > strings, from most specific to most general. They allow a device to express > its compatibility > with a family of similar devices, potentially allowing a single device > driver to match against > several devices. > The recommended format is “manufacturer,model”, where manufacturer is a > string describing the name of the manufacturer (such as a stock ticker > symbol), and model > specifies the model number. > > Example: *compatible=“fsl,mpc8641-uart”, “ns16550";* > > In this example, an operating system would first try to locate a device > driver that supported > fsl,mpc8641-uart. If a driver was not found, it would then try to locate a > driver that supported > the more general ns16550 device type. > > > *What I don't understand is how the system locates a device driver based on > the compatible property. For example the cpu node's compatible property > ("ARM,MCIMX535") of the imx53x.dtsi example. More precisely: How can the > system relate the string "ARM,MCIMX535" with the actual device driver in > the file system.* > > I hope my questions are clear enough. Many thanks in advance. > For #1, virtually none of our arm code uses the cpu information from the fdt data, because we generally compile a custom kernel specific to each cpu. We've been slowly (very slowly) moving towards a unified kernel that can boot on multiple arm chips (or at least closely related chips within a family), and that will make the cpu info more important some day. For #2, if you're looking for some big master table that maps compatible strings to drivers, no such thing exists. Each driver source has one or more DRIVER_MODULE() macros that provides some information about the driver. One of the things it provides is the parent. The newbus system builds a metadata hierarchy that tracks which drivers have described themselves as potential children on each bus. Usually a device's parent is some sort of bus such as PCI. In an fdt-based system "simplebus" is an abstraction that can represent many different types of buses (such as internal on-chip connections between the cpu and internal devices). A hardware bus such as PCI has ways to query the hardware to see what's connected. In the fdt world, simplebus uses the fdt data to do this query... it looks at all the fdt device entries that are described as its children in the fdt data. For each child in the fdt data, simplebus asks newbus to probe all the drivers whose DRIVER_MODULE() said they could be children of simplebus. The probe() routine of each driver has access to the fdt data for the device simplebus is trying to probe. The driver compares the compatible strings in that data to the compatible strings that it knows how to handle, and returns a success/fail code from probe() to indicate whether or not it is the driver for the device. Sometimes multiple drivers can handle the same hardware, so newbus probes every child driver against every device on the bus. For example a usb keyboard is a pretty generic thing, but a FooStar1000 keyboard might have a special driver that understands extra keys. The generic usb keyboard driver would return BUS_PROBE_GENERIC, and the FooStar1000 driver would return BUS_PROBE_SPECIFIC. After probing all potential devices, newbus chooses the one with the highest return value from probe() as being the one most-specific to that hardware. (In reality this doesn't happen much; usually only one driver returns success and all others return an error.) -- Ian
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?1407182403.56408.297.camel>