Pulse-width modulation (PWM) is a technique that modifies the duty-cycle of a pulsing signal to encode information or to control the amount of energy provided to a charge.

On the ConnectCore 8M Nano system-on-module:

  • Four PWM signals (from PWM1 to PWM4) are available from the i.MX8M Nano system-on-chip (multiplexed with other signals).

On the ConnectCore 8M Nano Development Kit:

  • PWM1 is available at EXP_I2C_SDA on the J48 expansion connector.

  • PWM2, 3 and 4 are connected to the J46 expansion connector on pins 3, 4 and 5 respectively.

This chapter describes the PWM channels from the CPU. See MCA Pulse Width Modulation (PWM) for information about the MCA PWM controllers.

Kernel configuration

You can manage the i.MX8M Nano PWM driver support through the following kernel configuration option:

  • i.MX PWM support (CONFIG_PWM_IMX)

This option is enabled as built-in on the default ConnectCore 8M Nano kernel configuration file.

Kernel driver

The driver for the i.MX8M Nano PWM is located at:

File Description

drivers/pwm/pwm-imx.c

PWM driver

Device tree bindings and customization

The i.MX8M Nano PWM interface is documented at Documentation/devicetree/bindings/pwm/imx-pwm.txt.

i.MX8M Nano PWM interfaces

The common i.MX8M Nano CPU device tree file contains entries for all the PWM channels:

i.MX8M Nano device tree
	pwm1: pwm@30660000 {
		compatible = "fsl,imx8mm-pwm", "fsl,imx27-pwm";
		reg = <0x0 0x30660000 0x0 0x10000>;
		interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&clk IMX8MN_CLK_PWM1_ROOT>,
			<&clk IMX8MN_CLK_PWM1_ROOT>;
		clock-names = "ipg", "per";
		#pwm-cells = <2>;
		status = "disabled";
	};

	pwm2: pwm@30670000 {
		compatible = "fsl,imx8mm-pwm", "fsl,imx27-pwm";
		reg = <0x0 0x30670000 0x0 0x10000>;
		interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&clk IMX8MN_CLK_PWM2_ROOT>,
			<&clk IMX8MN_CLK_PWM2_ROOT>;
		clock-names = "ipg", "per";
		#pwm-cells = <2>;
		status = "disabled";
	};

	pwm3: pwm@30680000 {
		compatible = "fsl,imx8mm-pwm", "fsl,imx27-pwm";
		reg = <0x0 0x30680000 0x0 0x10000>;
		interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&clk IMX8MN_CLK_PWM3_ROOT>,
			<&clk IMX8MN_CLK_PWM3_ROOT>;
		clock-names = "ipg", "per";
		#pwm-cells = <2>;
		status = "disabled";
	};

	pwm4: pwm@30690000 {
		compatible = "fsl,imx8mm-pwm", "fsl,imx27-pwm";
		reg = <0x0 0x30690000 0x0 0x10000>;
		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&clk IMX8MN_CLK_PWM4_ROOT>,
			<&clk IMX8MN_CLK_PWM4_ROOT>;
		clock-names = "ipg", "per";
		#pwm-cells = <2>;
		status = "disabled";
	};

IOMUX configuration

You must configure the pads that are to be used as i.MX8M Nano PWMs. See Pin multiplexing (IOMUX).

i.MX8M Nano pads should only have one IOMUX configuration. Remove other configurations for those pads, like GPIO, when configuring them as PWMs.

The default device tree doesn’t have any i.MX8M Nano PWMs enabled.

Example: enable all PWMs on ConnectCore 8M Nano Development Kit

For example, PWM1 is available on the ConnectCore 8M Nano Development Kit EXP_I2C_SDA pin of the J48 expansion connector, which corresponds to pad I2C4_SDA of the CPU. The other three PWMs are available on the ConnectCore 8M Nano Development Kit J46 expansion connector:

  • PWM2 corresponds to pad GPIO1_IO13

  • PWM3 corresponds to pad GPIO1_IO14

  • PWM4 corresponds to pad GPIO1_IO15

In order to enable them, you must:

  • Configure the IOMUX of each of the pads mentioned above to work as PWMs.

  • Remove any other configuration for said pads from any active driver pinctrl group; in this case, we disable the I2C4 bus entirely and remove the configuration of the GPIOs from the hog pinctrl group.

  • Enable the PWM nodes with their respective pinctrl.

Patch to enable PWMs on the ConnectCore 8M Nano Development Kit
diff --git a/arch/arm64/boot/dts/digi/ccimx8mn-dvk-wb.dts b/arch/arm64/boot/dts/digi/ccimx8mn-dvk-wb.dts
index 5e4a351..b096672 100644
--- a/arch/arm64/boot/dts/digi/ccimx8mn-dvk-wb.dts
+++ b/arch/arm64/boot/dts/digi/ccimx8mn-dvk-wb.dts
@@ -426,7 +426,7 @@
        pinctrl-1 = <&pinctrl_i2c4_gpio>;
        scl-gpios = <&gpio5 20 GPIO_ACTIVE_HIGH>;
        sda-gpios = <&gpio5 21 GPIO_ACTIVE_HIGH>;
-       status = "okay";
+       status = "disabled";
 };

 &isi_0 {
@@ -517,6 +517,30 @@
        };
 };

+&pwm1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_pwm1>;
+       status = "okay";
+};
+
+&pwm2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_pwm2>;
+       status = "okay";
+};
+
+&pwm3 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_pwm3>;
+       status = "okay";
+};
+
+&pwm4 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_pwm4>;
+       status = "okay";
+};
+
 &sai2 {
        pinctrl-names = "default", "sleep";
        pinctrl-0 = <&pinctrl_sai2>;
@@ -608,12 +632,6 @@
                        MX8MN_IOMUXC_GPIO1_IO10_GPIO1_IO10      0x16
                        /* GPIO1_IO11 on expansion connector */
                        MX8MN_IOMUXC_GPIO1_IO11_GPIO1_IO11      0x16
-                       /* GPIO1_IO13 on expansion connector */
-                       MX8MN_IOMUXC_GPIO1_IO13_GPIO1_IO13      0x16
-                       /* GPIO1_IO14 on expansion connector */
-                       MX8MN_IOMUXC_GPIO1_IO14_GPIO1_IO14      0x16
-                       /* GPIO1_IO15 on expansion connector */
-                       MX8MN_IOMUXC_GPIO1_IO15_GPIO1_IO15      0x16
                        /* User LED 3 */
                        MX8MN_IOMUXC_SD2_RESET_B_GPIO2_IO19     0x16
                        /* User button 2 */
@@ -748,6 +766,30 @@
                >;
        };

+       pinctrl_pwm1: pwm1grp {
+               fsl,pins = <
+                       MX8MN_IOMUXC_I2C4_SDA_PWM1_OUT          0x16
+               >;
+       };
+
+       pinctrl_pwm2: pwm2grp {
+               fsl,pins = <
+                       MX8MN_IOMUXC_GPIO1_IO13_PWM2_OUT        0x16
+               >;
+       };
+
+       pinctrl_pwm3: pwm3grp {
+               fsl,pins = <
+                       MX8MN_IOMUXC_GPIO1_IO14_PWM3_OUT        0x16
+               >;
+       };
+
+       pinctrl_pwm4: pwm4grp {
+               fsl,pins = <
+                       MX8MN_IOMUXC_GPIO1_IO15_PWM4_OUT        0x16
+               >;
+       };
+
        /* Ethernet PHYs regulator */
        pinctrl_reg_3v3_eth0: pinctrl_reg_3v3_eth0grp {
                fsl,pins = <

Depending on the frequency of the PWM signal and the hardware around it, you must carefully select the pad settings (the numerical values following the IOMUX definition on the device tree). See Documentation/devicetree/bindings/pinctrl/fsl,imx8mn-pinctrl.txt for information about the different values. Also see the NXP application note AN5078 Influence of pin setting on system function and performance for additional information.

Using the PWM channels

Control PWM signal from sysfs

Each of the four PWM interfaces is registered in the system as a standalone PWM controller.

On the ConnectCore 8M Nano Development Kit:

  • All PWM channels are disabled by default, but can be enabled with the device tree changes explained above

The PWM interfaces appear under /sys/class/pwm:

~# ls /sys/class/pwm/
pwmchip0  pwmchip1  pwmchip2  pwmchip3  pwmchip4

The PWM interfaces begin numbering with an index of 0. The indexes are calculated using the number of channels of the previous interface. PWM chip 4 corresponds to the MCA PWM controller.

Each CPU PWM interface only manages one PWM signal. Check the number of channels of an interface by printing the value of npwm:

~# cat /sys/class/pwm/pwmchip0/npwm
1

To access one channel of a PWM interface, export the channel index (0 since there is only one):

~# echo 0 > /sys/class/pwm/pwmchip0/export
You won’t be able to request PWM channels that are in use by other drivers, like those used by the backlight.

Now you can access the PWM channel and configure its settings:

~# ls /sys/class/pwm/pwmchip0/pwm0/
duty_cycle  enable      period      polarity    power       uevent

Period and duty cycle must be given in nanoseconds. For example, to configure a 100kHz signal with 20% duty cycle:

~# echo 10000 > /sys/class/pwm/pwmchip0/pwm0/period
~# echo 2000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle

To enable the PWM signal:

~# echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

The default polarity is normal (active high for the duty cycle). To invert the polarity:

~# echo inversed > /sys/class/pwm/pwmchip0/pwm0/polarity

Using Digi APIx library from a C application

An example application called apix-pwm-example is included in the dey-examples-digiapix recipe (part of dey-examples package) of meta-digi layer. This application shows how to generate a PWM signal using Digi APIx library on the ConnectCore 8M Nano platform.

Go to GitHub to see the application instructions and source code.

See PWM API for more information about the PWM APIx.