Date: Wed, 13 Aug 2014 15:14:58 -0300 From: =?UTF-8?Q?Mat=C3=ADas_Perret_Cantoni?= <perretcantonim@gmail.com> To: Ian Lepore <ian@freebsd.org> Cc: freebsd-arm@freebsd.org Subject: Re: Two questions on Flattened Device Tree, newbus and device driver attaching Message-ID: <CADLKG01MNtY%2B4V5SdZTKrWttX8f3%2BRbXfk0rrRwOOSTkg8U-6A@mail.gmail.com> In-Reply-To: <1407335765.56408.320.camel@revolution.hippie.lan> References: <CADLKG03f2KAEeTGCZTh=_ABufDfiWo6pSXW407aCcByon2BinA@mail.gmail.com> <1407182403.56408.297.camel@revolution.hippie.lan> <CADLKG03ANJJ1x-TwE3O9yc9fBnJx-DoZWZpPk2ptosiNSRYSPQ@mail.gmail.com> <1407335765.56408.320.camel@revolution.hippie.lan>
next in thread | previous in thread | raw e-mail | index | archive | help
Thank you very much for your patient, Ian. Things are much clearer to me now. I can move one with my project. Regards, Matias.- 2014-08-06 11:36 GMT-03:00 Ian Lepore <ian@freebsd.org>: > On Tue, 2014-08-05 at 22:31 -0300, Matas Perret Cantoni wrote: > > 2014-08-04 17:00 GMT-03:00 Ian Lepore <ian@freebsd.org>: > > > > > 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 full= y > > > > 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=3D260789&view=3Dmarkup > > > >), > > > > has the CPU definition all commented out: > > > > > > > > ... > > > > // cpus { > > > > // #address-cells =3D <1>; > > > > // #size-cells =3D <0>; > > > > // cpu@0 { > > > > // device-type =3D "cpu"; > > > > // model =3D "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 *d= o > > > > define* it's > > > > CPUs. For example: > > > > > > > > imx53x.dtsi: (here > > > > < > > > > https://svnweb.freebsd.org/base/release/10.0.0/sys/boot/fdt/dts/imx53x.dt= si?view=3Dmarkup > > > > > > > > ) > > > > > > > > ... > > > > cpus { > > > > #address-cells =3D <1>; > > > > #size-cells =3D <0>; > > > > > > > > cpu@0 { > > > > device_type =3D "cpu"; > > > > compatible =3D "ARM,MCIMX535"; > > > > reg =3D <0x0>; > > > > d-cache-line-size =3D <32>; > > > > i-cache-line-size =3D <32>; > > > > d-cache-size =3D <0x8000>; > > > > i-cache-size =3D <0x8000>; > > > > l2-cache-line-size =3D <32>; > > > > l2-cache-line =3D <0x40000>; > > > > timebase-frequency =3D <0>; > > > > bus-frequency =3D <0>; > > > > clock-frequency =3D <0>; > > > > }; > > > > ... > > > > > > > > or: > > > > > > > > p1020rdb.dts (here > > > > < > > > > https://svnweb.freebsd.org/base/release/10.0.0/sys/boot/fdt/dts/p1020rdb.= dts?view=3Dmarkup > > > > > > > > ) > > > > > > > > ... > > > > cpus { > > > > #address-cells =3D <1>; > > > > #size-cells =3D <0>; > > > > > > > > PowerPC,P1020@0 { > > > > device_type =3D "cpu"; > > > > reg =3D <0x0>; > > > > next-level-cache =3D <&L2>; > > > > }; > > > > > > > > PowerPC,P1020@1 { > > > > device_type =3D "cpu"; > > > > reg =3D <0x1>; > > > > next-level-cache =3D <&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 a= nd > > > > * then call that driver to initialise the hardware appropriately. T= he > > > > * 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 create= d > > > > * 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?revisi= on=3D260789&view=3Dmarkup > > > > > > > > ) > > > > > > > > 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=3D260789&view=3Dmarkup > > > >) > > > > 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 th= e > > > > 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 =E2=80=9Cmanufacturer,model=E2=80=9D, whe= re manufacturer > is a > > > > string describing the name of the manufacturer (such as a stock > ticker > > > > symbol), and model > > > > specifies the model number. > > > > > > > > Example: *compatible=3D=E2=80=9Cfsl,mpc8641-uart=E2=80=9D, = =E2=80=9Cns16550";* > > > > > > > > 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 ca= n > 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 ea= ch > > > cpu. We've been slowly (very slowly) moving towards a unified kernel > > > that can boot on multiple arm chips (or at least closely related chip= s > > > within a family), and that will make the cpu info more important some > > > day. > > > > > > > > Oh, I see. Now It is clearer if I take a look in > /10.0.0/sys/arm/std.zynq7: > > > > ... > > # > > # std.zynq7 - Generic configuration for Xilinx Zynq-7000 PS= . > > # > > # $FreeBSD$ > > > > cpu CPU_CORTEXA > > machine arm armv6 > > ... > > > > > > So #1 is solved. Thank you both! > > > > For #2, if you're looking for some big master table that maps compatibl= e > > > strings to drivers, no such thing exists. > > > > > > Each driver source has one or more DRIVER_MODULE() macros that provid= es > > > 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 man= y > > > different types of buses (such as internal on-chip connections betwee= n > > > the cpu and internal devices). A hardware bus such as PCI has ways t= o > > > 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 t= he > > > drivers whose DRIVER_MODULE() said they could be children of simplebu= s. > > > 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 examp= le > > > 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 FooStar10= 00 > > > 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 > > > > > > > #2 is way more clearer now, but I still can't see a few things: > > > > I can see the function simplebus_attach() calling newbus to create a > child, > > probe it and attach it for each of the ftd nodes claiming to be simpleb= us > > compatible. Here is a snippet from simplebus.c: > > > > /* > > * Walk simple-bus and add direct subordinates as our children. > > */ > > dt_node =3D ofw_bus_get_node(dev); > > for (dt_child =3D OF_child(dt_node); dt_child !=3D 0; > > dt_child =3D OF_peer(dt_child)) { > > > > ... > > /* Add newbus device for this FDT node */ > > dev_child =3D device_add_child(dev, NULL, -1); > > } > > ... > > return (bus_generic_attach(dev)); > > } > > > > > > So there are two functions from bus.h being called: device_add_child() > > which makes a new newbus child, and bus_generic_attach(dev) which cause= s > > newbus to probe and attach each of the new childs of the simplebus node= . > > > > I can see fdtbus doing the same thing for each node of the fdt's root > node. > > Here is a snippet from fdtbus.c: > > > > /* > > * Walk the FDT root node and add top-level devices as our children. > > */ > > for (child =3D OF_child(root); child !=3D 0; child =3D OF_peer(child)) = { > > > > ... > > newbus_device_from_fdt_node(dev, child); > > ... > > > > return (bus_generic_attach(dev)); > > > > > > _________ > > > > So until here I think I understand how the newbus nodes (devices) are > being > > created and how the drivers are being attached to them. > > But when I print the information about system device configuration with > > devinfo I can see that there is a "nexus0" node and a "ofwbus0" node in > the > > newbus hierarchy: > > > > root@zedboard:~ # devinfo > > nexus0 > > ofwbus0 > > simplebus0 > > zy7_slcr0 > > gic0 > > l2cache0 > > .... > > > > > > I think that the nexus0 node is created by this function: > > > > /* > > * Determine i/o configuration for a machine. > > */ > > static void > > configure_first(void *dummy) > > { > > > > device_add_child(root_bus, "nexus", 0); > > } > > > > > > (which is in the file /10.0.0/sys/arm/arm/autoconf.c and by the way I > can't > > find any calling function to configure_first). > > > > And I guess the root node is declared in /10.0.0/sys/kern/subr_bus.c > file: > > > > device_t root_bus; > > devclass_t root_devclass; > > > > > > But I can't find out how the "ofwbus0" node is created neither who is > doing > > so! > > > > Thanks in advance. > > Best regards, Matias.- > > configure_first() and the other functions in autoconf.c are started by > the system init code in mi_startup() in kern/init_main.c. Any part of > the kernel or modules that may be loaded by loader(8) can use a > SYSINIT() macro to declare that they have init code to run at kernel > startup. Linker magic combines all the SYSINIT info into a special > section in the kernel binary, and the mi_startup() code walks through > that info and calls all the functions. > > You've already discovered that nexus gets added by configure_first() (I > think I knew that years ago, but had completely forgotten). That causes > nexus to be probed and attached. The probe always succeeds with nexus, > and nexus attach() calls bus_generic_probe(). That causes newbus to > call the identify() routine for every driver that said it might be a > child of nexus. Most drivers don't have an identify() routine, it's > rarely used. I think of it as a sort of pre-probe routine (man 9 > DEVICE_IDENTIFY has more info on it.) > > The ofwbus driver (dev/ofw/ofwbus.c) has a DRIVER_MODULE() macro that > declares it to be a potential child of nexus, and it has an identify() > routine that calls device_add_child() to add itself as a child of nexus. > I think of this as "forced adoption"... normally it is the parent bus > that does device_add_child(), but in this case the child attaches itself > to the parent. Once ofwbus is added to nexus in this way, its attach() > routine gets called when nexus does bus_generic_attach(), and the ofwbus > attach() then uses the FDT data to attach simplebus and other children > described in the data. > > -- Ian > > >
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CADLKG01MNtY%2B4V5SdZTKrWttX8f3%2BRbXfk0rrRwOOSTkg8U-6A>