RFC Board management controller, MFD and device tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



fHi

now that the basic support for the sl28 board is upstream I'd want to
continue with the onboard management CPLD. Connected via I2C, the CPLD
has different building blocks at different internal addresses. Eg. there
is a watchog, GPIO, PWM, interrupt controller and a fan monitoring.
Somehow like a PMIC but without the regulator support. The basic
building blocks might be there multiple times, eg. there are two PWM
controllers (at different internal addresses) and multiple GPIO
controllers. But due to size constraints some of them are output-only
and some of them are input only. This board management CPLD might be
reused by other boards but with different components inside at
different addresses. Thus, naturally this is a MFD.

How would you implement this regarding the device tree binding? At the
moment there are individual drivers (like in gpio/, pwm/ watchdog/,..)
and one MFD driver which basically just exports one regmap, which is
fetched by the individual drivers by dev_get_regmap(pdev->dev.parent).
The current device tree binding is as follows:

&i2c {
        sl28cpld@4a {
                #address-cells = <1>;
                #size-cells = <0>;
                compatible = "kontron,sl28cpld";
                reg = <0x4a>;

                watchdog@4 {
                        compatible = "kontron,sl28cpld-wdt";
                        reg = <0x4>;
                };

                hwmon@b {
                        compatible = "kontron,sl28cpld-hwmon";
                        reg = <0xb>;
                };

                pwm0: pwm@c {
                        #pwm-cells = <2>;
                        compatible = "kontron,sl28cpld-pwm";
                        reg = <0xc>;
                };

                pwm1: pwm@e {
                        #pwm-cells = <2>;
                        compatible = "kontron,sl28cpld-pwm";
                        reg = <0xe>;
                };

                gpio0: gpio-controller@10 {
                        compatible = "kontron,sl28cpld-gpio";
                        reg = <0x10>;
                        interrupt-parent = <&gpio2>;
                        interrupts = <6 IRQ_TYPE_EDGE_FALLING>;

                        gpio-controller;
                        #gpio-cells = <2>;
                        gpio-line-names = "a", "b", "c";

                        interrupt-controller;
                        #interrupt-cells = <2>;
                };

                gpio1: gpio-controller@1a {
                        compatible = "kontron,sl28cpld-gpo";
                        reg = <0x1a>;
                        gpio-controller;
                        #gpio-cells = <2>;
                };

                intc: interrupt-controller@1c {
                        compatible = "kontron,sl28cpld-intc";
                        reg = <0x1c>;
                        interrupt-parent = <&gpio2>;
                        interrupts = <6 IRQ_TYPE_EDGE_FALLING>;

                        interrupt-controller;
                        #interrupt-cells = <2>;
                };
                [..snip..]

Note that the reg property is the internal offset of the building block.
Because there might be multiple variants of this CPLD on different
boards, the register map is not fixed. Thus individual drivers need to
know the base offset of their registers. At the moment, they read the
reg property using of_get_address(np, 0, NULL, NULL) to get the base
offset.

This is working but has some drawbacks. First of all, that might fall
into the category "this is no information about the hardware and thus
should not go into the device tree". Second, if there is an update in
the future I would like to be able to support also these CPLDs. Eg. you
can read a global version register and for example know that since this
version something has changed like the register map. Thus it might make
sense to have the base offsets inside the MFD base driver, where they
can be adjusted in _probe(). I guess that also falls into the first
argument to not have to much information in the device tree.

I've looked into how you could do the second implementation. The MFD can
pass the register offset via resources and IORESOURCE_REG like the
wm831x-core.c does it. Also the interrupt which in the device tree above
is a property of the children (which I think is kinda hacky), could be
a property of the mfd and passed to the children with IORESOURCE_IRQ.

What is still missing is the device tree binding. Eg. if I need to have
a phandle to the first pwm controller. Here, I could think of two
different implementations:

&i2c {
        bmc: sl28cpld@4a {
                #address-cells = <1>;
                #size-cells = <0>;
                compatible = "kontron,sl28cpld";
                reg = <0x4a>;
                interrupt-parent = <&gpio2>;
                interrupts = <6 IRQ_TYPE_EDGE_FALLING>;

                gpio-controller;
                #gpio-cells = <2>;

                interrupt-controller;
                #interrupt-cells = <2>;

                #pwm-cells = <2>;
};

&i2c {
        sl28cpld@4a {
                #address-cells = <1>;
                #size-cells = <0>;
                compatible = "kontron,sl28cpld";
                reg = <0x4a>;

                watchdog {
                        compatible = "kontron,sl28cpld-wdt";
                };

                hwmon {
                        compatible = "kontron,sl28cpld-hwmon";
                };

                pwm0: pwm@0 {
                        #pwm-cells = <2>;
                        compatible = "kontron,sl28cpld-pwm";
                        reg = <0>;
                };

                pwm1: pwm@1 {
                        #pwm-cells = <2>;
                        compatible = "kontron,sl28cpld-pwm";
                        reg = <1>;
                };

                gpio0: gpio-controller@0 {
                        compatible = "kontron,sl28cpld-gpio";
                        reg = <0>;

                        gpio-controller;
                        #gpio-cells = <2>;
                        gpio-line-names = "a", "b", "c";

                        interrupt-controller;
                        #interrupt-cells = <2>;
                };

                gpio1: gpio-controller@1a {
                        compatible = "kontron,sl28cpld-gpo";
                        reg = <0x1a>;
                        gpio-controller;
                        #gpio-cells = <2>;
                };

                intc: interrupt-controller {
                        compatible = "kontron,sl28cpld-intc";

                        interrupt-controller;
                        #interrupt-cells = <2>;
                };
                [..snip..]
};

The first implementation would just be some kind of super node which
exposes all features, eg. you'd do "pwms = <&bmc 0>;" or "gpios = <&bmc
0 GPIO_ACTIVE_LOW>;". I don't know it this is a good idea or if this is
even possible.

The second one is almost like the current implemention only that there
are no register offsets or irqs in the child nodes. But because there
might be two children of the same building block (eg. two pwm nodes)
the reg property is now an ID.

Phew, that was a long mail. I wanted to know your thoughts/ideas if
 (1) the current solution would be accepted, (if one would life with the
     drawback of not being able to detect multiple verions of the CPLD
     on runtime)
 (2) whould you do the super-node contains everything or the more
     elaborate device tree.

I tend to have the last one (the more elaborate device tree). The mfd
core can already match mfd_cells to device tree nodes, but only if the
compatible string is unique (see mfd_add_device()). Eg. the current code
would match the mfd cells with of_compatible = "kontron,sl28cpld-pwm"
only to the first node. I thought of also comparing the reg property
against the id property of struct mfd_cell.

-michael



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux