Add SC16IS752 I2C-UART Bridge Driver via Device Tree

Hello,

I’m trying to get an SC16IS752 IC running off the I2C bus on the 26-pin Raspberry Pi connector on a MangOH Red. I’m having problems getting the driver to load properly and register the UARTs and I’m hoping that someone has come across something similar and can give me a hint.

A few details about my setup:

  • I’m using a WP7603 module in a MangOH Red
  • I have pins 3 and 5 of the 26-pin Raspberry Pi connector connected to the I2C interface of an SC16IS752 IC
  • Pin 7 of the Raspberry Pi connector is connected to the interrupt pin on the SC16IS752
  • Pin 11 of the Raspberry Pi connector is connected to the reset pin on the SC16IS752
  • The SC16IS752 is configured in I2C mode to use address 0x4D
  • I’m not using any of the MangOH specific extensions, just the bare Sierra WP7603 image

What I’ve done so far:

  1. I have compiled the sc16is7xx driver into the kernel and flashed this to the device.
  2. I’ve updated the device tree to include the references to the SC16IS752, which of course hangs off the pca9546 I2C switch, and pin configurations for the interrupt and reset. I appended the below to …/yocto/kernel/arch/arm/boot/dts/qcom/mdm9607-wp76xx-mangoh-green.dts
/* pin configuration for sc16is752 */
&soc{
	tlmm_pinmux: pinctrl@1000000 {
		sc16is752_int_default: sc16is752_int_default {
			mux {
				pins = "gpio11"; /* pin 7 on MangOH header -> GPIO3 on MangOH -> GPIO24 on WP76xx -> GPIO11 on MDM9607 */
				function = "gpio";
			};
			config {
				pins = "gpio11";
				drive-strength = <16>; /* 16 mA */
				bias-pull-up; /* pull up */
			};
		};
		
		sc16is752_reset_default: sc16is752_reset_default {
			mux {
				pins = "gpio9"; /* pin 11 on MangOH header -> GPIO1 on MangOH -> GPIO22 on WP76xx -> GPIO9 on MDM9607 */
				function = "gpio";
			};
			config {
				pins = "gpio9";
				drive-strength = <16>; /* 16 mA */
				output-high; /* pull up */
			};
		};
	};
};

/* bus configuration for sc16is752 */
&i2c_4 {
	i2c_switch: pca9546@71 {
		compatible = "nxp,pca9546";
		#address-cells = <1>;
		#size-cells = <0>;
		reg = <0x71>;
		
		i2c_5: i2c_5@0{
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <0>;
		};
		
		i2c_6: i2c_6@1{
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <1>;
		};
		
		i2c_7: i2c_7@2{
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <2>;
		};
		
		i2c_8: i2c_8@3{
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <3>; /*on CH3 of I2C switch*/
			sc16is752: sc16is752@4d {
				compatible = "nxp,sc16is752";
				reg = <0x4d>;
				clocks = <&sc16is752_clk>;
				pinctrl-names = "default";
				pinctrl-0 = <&sc16is752_int_default &sc16is752_reset_default>;
				interrupt-parent = <&tlmm_pinmux>;
				interrupts = <11 2>; /* pin 7 on MangOH header -> GPIO3 on MangOH -> GPIO24 on WP76xx -> GPIO11 on MDM9607 */
				i2c-max-frequency = <400000>;
				status = "ok";
				
				sc16is752_clk: sc16is752_clk  {
					compatible = "fixed-clock";
					#clock-cells = <0>;
					clock-frequency = <1843200>;
				};
			};
		};
	};
};

What I’m seeing:

  • The driver for the pca954x I2C switch loads and binds correctly. Three additional i2c busses are enumerated as /dev/i2c-0, /dev/i2c-1, /dev/i2c-2 and /dev/i2c-3
  • The driver for the sc16is7xx is present at /sys/bus/i2c/drivers
  • The sc16is7xx device is present at /sys/devices/78b8000.i2c/i2c-4/i2c-3/3-004d
  • The driver is NOT bound to the device and the additional ttySx are not enumerated in /dev/
  • The reset and int pins are correctly configured (pin 11 is high, bringing the device out of reset)
  • Running i2c detect on i2c-3 shows the SC16IS752 present at address 0x4d, but no kernel driver loaded, and the i2c switch with a the kernel driver loaded at 0x71.
    root@swi-mdm9x28-wp:~# i2cdetect -y -r 3
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- 04 05 06 07 -- -- -- -- -- -- -- --
    10: -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- --
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    30: -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- --
    40: -- -- -- -- -- -- -- -- -- -- -- -- -- 4d -- --
    50: -- 51 -- -- -- -- -- -- -- -- -- -- -- -- -- --
    60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
    70: -- UU -- -- -- -- 76 --

I’ve turned on some verbose kernel debug messages, and here is what I’m seeing:

     [ 3.090818] PM: Moving i2c:3-004d to end of list
     [ 3.090830] i2c 3-004d: Retrying from deferred list
     [ 3.090854] bus: 'i2c': driver_probe_device: matched device 3-004d with driver sc16is7xx
     [ 3.091002] bus: 'i2c': really_probe: probing driver sc16is7xx with device 3-004d
     [ 3.091307] generic pinconfig core: found bias-pull-up with value 1
     [ 3.091321] generic pinconfig core: found drive-strength with value 16
     [ 3.091339] pinctrl core: add 2 pinmux maps
     [ 3.091538] generic pinconfig core: found drive-strength with value 16
     [ 3.091552] generic pinconfig core: found output-high with value 1
     [ 3.091568] pinctrl core: add 2 pinmux maps
     [ 3.091629] generic pinconfig core: found bias-pull-up with value 1
     [ 3.091642] generic pinconfig core: found drive-strength with value 16
     [ 3.091658] pinctrl core: add 2 pinmux maps
     [ 3.091818] generic pinconfig core: found drive-strength with value 16
     [ 3.091831] generic pinconfig core: found output-high with value 1
     [ 3.091844] pinctrl core: add 2 pinmux maps
     [ 3.091896] generic pinconfig core: found bias-pull-up with value 1
     [ 3.091908] generic pinconfig core: found drive-strength with value 16
     [ 3.091923] pinctrl core: add 2 pinmux maps
     [ 3.092075] generic pinconfig core: found drive-strength with value 16
     [ 3.092088] generic pinconfig core: found output-high with value 1
     [ 3.092103] pinctrl core: add 2 pinmux maps
     [ 3.092273] mdm9607-pinctrl 1000000.pinctrl: found group selector 11 for gpio11
     [ 3.092288] mdm9607-pinctrl 1000000.pinctrl: found group selector 11 for gpio11
     [ 3.092303] mdm9607-pinctrl 1000000.pinctrl: found group selector 9 for gpio9
     [ 3.092316] mdm9607-pinctrl 1000000.pinctrl: found group selector 9 for gpio9
     [ 3.092330] mdm9607-pinctrl 1000000.pinctrl: found group selector 11 for gpio11
     [ 3.092345] mdm9607-pinctrl 1000000.pinctrl: found group selector 11 for gpio11
     [ 3.092359] mdm9607-pinctrl 1000000.pinctrl: found group selector 9 for gpio9
     [ 3.092506] ALSA device list:
     [ 3.092516] No soundcards found.
     [ 3.092994] mdm9607-pinctrl 1000000.pinctrl: found group selector 9 for gpio9
     [ 3.093016] mdm9607-pinctrl 1000000.pinctrl: found group selector 11 for gpio11
     [ 3.093029] mdm9607-pinctrl 1000000.pinctrl: found group selector 11 for gpio11
     [ 3.093043] mdm9607-pinctrl 1000000.pinctrl: found group selector 9 for gpio9
     [ 3.093057] mdm9607-pinctrl 1000000.pinctrl: found group selector 9 for gpio9
     [ 3.093075] mdm9607-pinctrl 1000000.pinctrl: request pin 11 (GPIO_11) for 3-004d
     [ 3.093093] mdm9607-pinctrl 1000000.pinctrl: request pin 9 (GPIO_9) for 3-004d
     [ 3.093137] sc16is7xx 3-004d: probe
     [ 3.093407] i2c 3-004d: Driver sc16is7xx requests probe deferral
     [ 3.093421] i2c 3-004d: Added to deferred list

It looks like the correct driver is found and the probe is triggered, however then we see that “Driver sc16is7xx requests probe deferral”. Any idea why?

I can think of a few possibilities:

  • The IRQ fails to be assigned (I’m not sure how to debug this)
  • The sc16is7xx driver in the 3.18.122 kernel has issues
  • There is some error in what I’ve added to the device tree

I’m pretty stumped at this point!
Has anyone come across something similar?
Does my device tree look okay?

Thanks,
Jack

We dont support device tree configuration. I would suggest you watch the following youtube video.

The read through the following -->
https://docs.legato.io/latest/defFilesMdef.html

Then review how we built the various on board drivers for mangoh red and yellow on our github site:


Let us know if you have any questions.

Thanks for the reply asyal.

Can you please clarify what you mean by “we don’t support device tree configuration”?
Do you mean that adding hardware through the device tree simply won’t work on a WP module? Or do you mean that there is a better way to achieve what I’m trying to do?

I’ve had a look through the links you provided and I think I understand how that all works. A custom kernel module is developed with the purpose of creating the device instance including pin and interrupt configuration. This custom module along with the actual device driver, provided as another kernel module, is packaged into a Legato update with .mdef files. These two modules are then loaded by Legato into the target’s kernel. Once the device is created, the kernel will bind to the right driver. Have I got that more or less right?

I like this method because I don’t need to build the kernel and this provides the ability to update the modules via Legato if needed.

Jack

Hi Jack,

Some of our community prefer the yocto way to add drivers and some prefer the legato way as @asyal mentioned above.

For you first question, what @asyal meant was that the Legato way does not support device trees.
For your second question, most of our community does feel that the Legato way is the
better way. For your last question, yes you do seem to have understood the Legato route.

It seems that you have chosen to do bitbake linux-quic builds through yocto,
so device tree configuration is possible, via Legato it is not possible as
of yet (hopefully, in the future). Though, it is possible to hard-code the
platform data in Legato kernel modules and have an init module hold that data
and start the real driver afterwards with the Legato route if you want updates.
For the the rest of the post, I’ll now stick to the Yocto issues you have been encountering.
Note that you could get this working and then convert things to the Legato route.

Firstly, I am at a lost why your device was moved to i2c busses 0 - 3, as far
I know these buses are enumerated by qcom specific code for its use. You are in
the power management tree it seems - did you intend to be? “PM: Moving %s:%s to end of list\n”.

Secondly, probe deferral implies that your device needs resources from other
drivers that have not been loaded yet - I suggest you try not having this built-in
and making a loadable module for initial testing and insmod or modprobe it. Though,
the NXP device should have been added later by a workqueue function, though I don’t
see those messages here. Please check the driver code for any other driver dependencies.

Thirdly, as far as I can see you have not tied into the pinctrl subsystem, by just
editing mdm9607-wp76xx-mangoh-green.dts. You do need to do this in mdm9607-pinctrl.dtsi.

My hunch is that you have device tree coding errors. Try adding your device to pinctrl
and make the appropriate references to the allocated name in the green dts file.
Also check all driver dependencies your device has. Lastly, hopefully
no other device tree files were accidentally modified or any kernel code
that caused your device to be enumerated in qcom i2c-bus space.
Check that NXP does not say that they have any dependency on
kernel versions (e.g. 4.X or greater).

Good luck and let us know if you have success and what your fix was or if you
have any further issues.

Zahid

Hi Zahid,

Thanks for your reply.

It seems that you are are doing bitbake linux-quic builds through yocto

Yes, that’s correct.

You are in the power management tree it seems - did you intend to be?

No, I did not intend to be, I don’t know how I would have gotten there using &i2c_4 as the parent bus?

the NXP device should have been added later by a workqueue function

Yes I can see in the logs that the driver binding is repeatedly retried throughout the boot process with the same result each time. This happens probably 6 - 8 times with the last retry being just before the boot process completes and the login prompt is shown.

as far as I can see you have not tied into the pinctrl subsystem, by just
editing mdm9607-wp76xx-mangoh-green.dts.

I had understood that device tree source files are additive, meaning that if an existing node is referenced in a higher level file, the compiler will combine all additions or overwrites to that node before flattening as if it were all written in the same file.
This being the case, adding the reference to &soc { ttlm_pinmux: pinctrl@1000000 as I have done should tie me in to the pinctrl system without needing to modify the lower level mdm9607-pinctrl.dsti file directly. In fact this is how other devices are added when looking at the other dtsi files (eg mdm9607-wp76xx.dtsi for the UART pin configs). In any case the pin configs I have specified in my modifications DO come into effect correctly - the reset pin is configured as an output and set high, and the int pin as an input with a pull-up. I’m not sure that this is the problem?

Try adding your device to pinctrl

Do you mean to add the device itself to pinctrl (currently on the i2c_8 node, since it is an i2c device physically on this bus) or the pin configurations?

Check that NXP does not say they have any dependency on
kernel versions (e.g. 4.X or greater).

The driver I used is in-tree for the 3.18 kernel and in fact already present (but not built by default) in the Linux distro source provided by Sierra.

My hunch is that, for whatever reason, the IRQ is failing to be allocated to the device, hence the probe deferred message.

At this stage I’m going to see if I can get the driver to load correctly the Legato way that asyal suggested, then if that works, come back and troubleshoot the device tree method.

Jack