Home Forum Software discussion Controlling PWM-fan

Tagged: 

Viewing 1 post (of 1 total)
  • Author
    Posts
  • #7883
    nanasi
    Participant

    Here is the patch to control PWM-fan connected to J10.
    Tested with “Noctua NF-A4x20 PWM”.
    ——————
    arch/arm64/boot/dts/marvell/armada-8040-mcbin.dtsi | 51 ++++++
    arch/arm64/boot/dts/marvell/armada-cp110.dtsi | 8
    arch/arm64/configs/defconfig | 1
    drivers/gpio/gpio-mvebu.c | 172 ++++++++++++++++++—
    4 files changed, 212 insertions(+), 20 deletions(-)
    ——————

    --- linux-5.1.20/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dtsi.org	2019-07-26 16:13:08.000000000 +0900
    +++ linux-5.1.20/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dtsi	2019-07-28 09:25:13.361651661 +0900
    @@ -101,6 +101,15 @@
     		pinctrl-names = "default";
     		pinctrl-0 = <&cp0_sfp_1g_pins &cp1_sfp_1g_pins>;
     	};
    +
    +	pwm_fan: pwm-fan {
    +		/* J10 Fan header */
    +		compatible = "pwm-fan";
    +		#cooling-cells = <2>;
    +		pwms = <&cp0_gpio2 16 40000>;	/* 40000 ns is 25 kHz */
    +		pinctrl-names = "default";
    +		pinctrl-0 = <&cp0_fan_pwm_pins>;
    +	};
     };
     
     &uart0 {
    @@ -208,6 +217,10 @@
     		marvell,pins = "mpp47";
     		marvell,function = "gpio";
     	};
    +	cp0_fan_pwm_pins: fan-pwm-pins {
    +		marvell,pins = "mpp48";
    +		marvell,function = "gpio";
    +	};
     	cp0_sfp_1g_pins: sfp-1g-pins {
     		marvell,pins = "mpp51", "mpp53", "mpp54";
     		marvell,function = "gpio";
    @@ -344,3 +357,41 @@
     	usb-phy = <&usb3h0_phy>;
     	status = "okay";
     };
    +
    +&pwm_fan {
    +	cooling-levels = <0
    +	8 9 10 11 12 13 14 15
    +	16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
    +	32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
    +	48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
    +	64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
    +	80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    +	96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    +	112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    +	128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    +	144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    +	160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    +	176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
    +	192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
    +	208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
    +	224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
    +	240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255>;
    +	status = "okay";
    +};
    +
    +&ap_thermal_cpu1 {
    +	trips {
    +		cpu_active: cpu-active {
    +			temperature = <60000>;
    +			hysteresis = <2000>;
    +			type = "active";
    +		};
    +	};
    +	cooling-maps {
    +		fan-map {
    +			trip = <&cpu_active>;
    +			cooling-device = <&pwm_fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
    +		};
    +	};
    +};
    +
    --- linux-5.1.20/arch/arm64/boot/dts/marvell/armada-cp110.dtsi.org	2019-07-26 16:13:08.000000000 +0900
    +++ linux-5.1.20/arch/arm64/boot/dts/marvell/armada-cp110.dtsi	2019-07-28 08:56:25.122842572 +0900
    @@ -233,6 +233,10 @@
     				gpio-controller;
     				#gpio-cells = <2>;
     				gpio-ranges = <&CP110_LABEL(pinctrl) 0 0 32>;
    +				pwm-reg = <0x1f0 0x08>;
    +				#pwm-cells = <2>;
    +				clocks = <&CP110_LABEL(clk) 0 3>;
    +				clock-names = "core";
     				interrupt-controller;
     				interrupts = <86 IRQ_TYPE_LEVEL_HIGH>,
     					<85 IRQ_TYPE_LEVEL_HIGH>,
    @@ -248,6 +252,10 @@
     				gpio-controller;
     				#gpio-cells = <2>;
     				gpio-ranges = <&CP110_LABEL(pinctrl) 0 32 31>;
    +				pwm-reg = <0x1f8 0x08>;
    +				#pwm-cells = <2>;
    +				clocks = <&CP110_LABEL(clk) 0 3>;
    +				clock-names = "core";
     				interrupt-controller;
     				interrupts = <82 IRQ_TYPE_LEVEL_HIGH>,
     					<81 IRQ_TYPE_LEVEL_HIGH>,
    --- linux-5.1.20/arch/arm64/configs/defconfig.org	2019-07-26 16:13:08.000000000 +0900
    +++ linux-5.1.20/arch/arm64/configs/defconfig	2019-07-28 08:56:25.126842754 +0900
    @@ -779,3 +779,4 @@ CONFIG_DEBUG_KERNEL=y
     # CONFIG_DEBUG_PREEMPT is not set
     # CONFIG_FTRACE is not set
     CONFIG_MEMTEST=y
    +CONFIG_SENSORS_PWM_FAN=m
    --- linux-5.1.20/drivers/gpio/gpio-mvebu.c.org	2019-07-26 16:13:08.000000000 +0900
    +++ linux-5.1.20/drivers/gpio/gpio-mvebu.c	2019-07-28 08:56:25.126842754 +0900
    @@ -35,6 +35,7 @@
     
     #include <linux/bitops.h>
     #include <linux/clk.h>
    +#include <linux/clk-provider.h>
     #include <linux/err.h>
     #include <linux/gpio/driver.h>
     #include <linux/gpio/consumer.h>
    @@ -44,6 +45,7 @@
     #include <linux/irqchip/chained_irq.h>
     #include <linux/irqdomain.h>
     #include <linux/mfd/syscon.h>
    +#include <linux/of_address.h>
     #include <linux/of_device.h>
     #include <linux/of_irq.h>
     #include <linux/pinctrl/consumer.h>
    @@ -68,6 +70,7 @@
     /*
      * PWM register offsets.
      */
    +#define PWM_BLINK_DURATION_BASE		0x1f0
     #define PWM_BLINK_ON_DURATION_OFF	0x0
     #define PWM_BLINK_OFF_DURATION_OFF	0x4
     
    @@ -94,6 +97,7 @@
     
     struct mvebu_pwm {
     	void __iomem		*membase;
    +	u32			 offset;
     	unsigned long		 clk_rate;
     	struct gpio_desc	*gpiod;
     	struct pwm_chip		 chip;
    @@ -292,6 +296,74 @@ static void __iomem *mvebu_pwmreg_blink_
     	return mvpwm->membase + PWM_BLINK_OFF_DURATION_OFF;
     }
     
    +static void mvebu_pwmreg_blink_duration(struct mvebu_pwm *mvpwm,
    +                         struct regmap **map, unsigned int *offset)
    +{
    +	*map = mvpwm->mvchip->regs;
    +	*offset = mvpwm->offset;
    +}
    +
    +static u32
    +mvebu_pwmreg_read_blink_on_duration(struct mvebu_pwm *mvpwm)
    +{
    +	struct regmap *map;
    +	unsigned int offset;
    +	u32 val;
    +
    +	if (mvpwm->membase) {
    +		val = readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
    +	} else {
    +		mvebu_pwmreg_blink_duration(mvpwm, &map, &offset);
    +		regmap_read(map, offset + PWM_BLINK_ON_DURATION_OFF, &val);
    +	}
    +	return val;
    +}
    +
    +static void
    +mvebu_pwmreg_write_blink_on_duration(struct mvebu_pwm *mvpwm, u32 val)
    +{
    +	struct regmap *map;
    +	unsigned int offset;
    +
    +	if (mvpwm->membase) {
    +		writel_relaxed(val, mvebu_pwmreg_blink_on_duration(mvpwm));
    +	} else {
    +		mvebu_pwmreg_blink_duration(mvpwm, &map, &offset);
    +		regmap_write(map, offset + PWM_BLINK_ON_DURATION_OFF, val);
    +	}
    +}
    +
    +static u32
    +mvebu_pwmreg_read_blink_off_duration(struct mvebu_pwm *mvpwm)
    +{
    +	struct regmap *map;
    +	unsigned int offset;
    +	u32 val;
    +
    +	if (mvpwm->membase) {
    +		val = readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm));
    +	} else {
    +		mvebu_pwmreg_blink_duration(mvpwm, &map, &offset);
    +		regmap_read(map, offset + PWM_BLINK_OFF_DURATION_OFF, &val);
    +	}
    +
    +	return val;
    +}
    +
    +static void
    +mvebu_pwmreg_write_blink_off_duration(struct mvebu_pwm *mvpwm, u32 val)
    +{
    +	struct regmap *map;
    +	unsigned int offset;
    +
    +	if (mvpwm->membase) {
    +		writel_relaxed(val, mvebu_pwmreg_blink_off_duration(mvpwm));
    +	} else {
    +		mvebu_pwmreg_blink_duration(mvpwm, &map, &offset);
    +		regmap_write(map, offset + PWM_BLINK_OFF_DURATION_OFF, val);
    +	}
    +}
    +
     /*
      * Functions implementing the gpio_chip methods
      */
    @@ -661,7 +733,7 @@ static void mvebu_pwm_get_state(struct p
     	spin_lock_irqsave(&mvpwm->lock, flags);
     
     	val = (unsigned long long)
    -		readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
    +		mvebu_pwmreg_read_blink_on_duration(mvpwm);
     	val *= NSEC_PER_SEC;
     	do_div(val, mvpwm->clk_rate);
     	if (val > UINT_MAX)
    @@ -672,7 +744,7 @@ static void mvebu_pwm_get_state(struct p
     		state->duty_cycle = 1;
     
     	val = (unsigned long long)
    -		readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm));
    +		mvebu_pwmreg_read_blink_off_duration(mvpwm);
     	val *= NSEC_PER_SEC;
     	do_div(val, mvpwm->clk_rate);
     	if (val < state->duty_cycle) {
    @@ -726,8 +798,8 @@ static int mvebu_pwm_apply(struct pwm_ch
     
     	spin_lock_irqsave(&mvpwm->lock, flags);
     
    -	writel_relaxed(on, mvebu_pwmreg_blink_on_duration(mvpwm));
    -	writel_relaxed(off, mvebu_pwmreg_blink_off_duration(mvpwm));
    +	mvebu_pwmreg_write_blink_on_duration(mvpwm, on);
    +	mvebu_pwmreg_write_blink_off_duration(mvpwm, off);
     	if (state->enabled)
     		mvebu_gpio_blink(&mvchip->chip, pwm->hwpwm, 1);
     	else
    @@ -753,9 +825,9 @@ static void __maybe_unused mvebu_pwm_sus
     	regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
     		    &mvpwm->blink_select);
     	mvpwm->blink_on_duration =
    -		readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
    +		mvebu_pwmreg_read_blink_on_duration(mvpwm);
     	mvpwm->blink_off_duration =
    -		readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm));
    +		mvebu_pwmreg_read_blink_off_duration(mvpwm);
     }
     
     static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
    @@ -764,19 +836,18 @@ static void __maybe_unused mvebu_pwm_res
     
     	regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
     		     mvpwm->blink_select);
    -	writel_relaxed(mvpwm->blink_on_duration,
    -		       mvebu_pwmreg_blink_on_duration(mvpwm));
    -	writel_relaxed(mvpwm->blink_off_duration,
    -		       mvebu_pwmreg_blink_off_duration(mvpwm));
    +	mvebu_pwmreg_write_blink_on_duration(mvpwm, mvpwm->blink_on_duration);
    +	mvebu_pwmreg_write_blink_off_duration(mvpwm, mvpwm->blink_off_duration);
     }
     
    -static int mvebu_pwm_probe(struct platform_device *pdev,
    +static int mvebu_pwm_probe_raw(struct platform_device *pdev,
     			   struct mvebu_gpio_chip *mvchip,
     			   int id)
     {
     	struct device *dev = &pdev->dev;
     	struct mvebu_pwm *mvpwm;
     	struct resource *res;
    +	const __be32 *base;
     	u32 set;
     
     	if (!of_device_is_compatible(mvchip->chip.of_node,
    @@ -789,7 +860,7 @@ static int mvebu_pwm_probe(struct platfo
     	 * for the first two GPIO chips. So if the resource is missing
     	 * we can't treat it as an error.
     	 */
    -	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
    +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
     	if (!res)
     		return 0;
     
    @@ -800,12 +871,10 @@ static int mvebu_pwm_probe(struct platfo
     	 * Use set A for lines of GPIO chip with id 0, B for GPIO chip
     	 * with id 1. Don't allow further GPIO chips to be used for PWM.
     	 */
    -	if (id == 0)
    -		set = 0;
    -	else if (id == 1)
    -		set = U32_MAX;
    -	else
    +	if (id >= 2)
     		return -EINVAL;
    +	base = of_get_address(mvchip->chip.of_node, 1, NULL, NULL);
    +	set = (be32_to_cpup(base) & 8) ? U32_MAX : 0;
     	regmap_write(mvchip->regs,
     		     GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set);
     
    @@ -841,6 +910,53 @@ static int mvebu_pwm_probe(struct platfo
     	return pwmchip_add(&mvpwm->chip);
     }
     
    +static int mvebu_pwm_probe_syscon(struct platform_device *pdev,
    +			   struct mvebu_gpio_chip *mvchip,
    +			   int id)
    +{
    +	struct device *dev = &pdev->dev;
    +	struct mvebu_pwm *mvpwm;
    +	u32 base = 0;
    +	u32 set;
    +
    +	if (of_property_read_u32(pdev->dev.of_node, "pwm-reg", &base)) {
    +//		return -EINVAL;
    +		base = 0x1f0 + 8 * (id % 2) ;
    +	}
    +
    +	if (IS_ERR(mvchip->clk)) {
    +		return PTR_ERR(mvchip->clk);
    +	}
    +
    +	if (id == 0 || id >= 3) {
    +		return -EINVAL;
    +	}
    +	set = (base & 8) ? U32_MAX : 0;
    +	regmap_write(mvchip->regs,
    +		     GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set);
    +
    +	mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL);
    +	if (!mvpwm)
    +		return -ENOMEM;
    +	mvchip->mvpwm = mvpwm;
    +	mvpwm->mvchip = mvchip;
    +	mvpwm->membase = NULL;
    +	mvpwm->offset = base;
    +	mvpwm->clk_rate = clk_get_rate(mvchip->clk);
    +	if (!mvpwm->clk_rate) {
    +		dev_err(dev, "failed to get clock rate\n");
    +		return -EINVAL;
    +	}
    +
    +	mvpwm->chip.dev = dev;
    +	mvpwm->chip.ops = &mvebu_pwm_ops;
    +	mvpwm->chip.npwm = mvchip->chip.ngpio;
    +	mvpwm->chip.base = -1;
    +
    +	spin_lock_init(&mvpwm->lock);
    +	return pwmchip_add(&mvpwm->chip);
    +}
    +
     #ifdef CONFIG_DEBUG_FS
     #include <linux/seq_file.h>
     
    @@ -1132,8 +1248,12 @@ static int mvebu_gpio_probe(struct platf
     
     	mvchip->clk = devm_clk_get(&pdev->dev, NULL);
     	/* Not all SoCs require a clock.*/
    -	if (!IS_ERR(mvchip->clk))
    +	if (IS_ERR(mvchip->clk) && PTR_ERR(mvchip->clk) == -EPROBE_DEFER) {
    +		return -EPROBE_DEFER;
    +	}
    +	if (!IS_ERR(mvchip->clk)) {
     		clk_prepare_enable(mvchip->clk);
    +	}
     
     	mvchip->soc_variant = soc_variant;
     	mvchip->chip.label = dev_name(&pdev->dev);
    @@ -1260,8 +1380,20 @@ static int mvebu_gpio_probe(struct platf
     	}
     
     	/* Some MVEBU SoCs have simple PWM support for GPIO lines */
    -	if (IS_ENABLED(CONFIG_PWM))
    -		return mvebu_pwm_probe(pdev, mvchip, id);
    +	if (IS_ENABLED(CONFIG_PWM)) {
    +		switch (soc_variant) {
    +		case MVEBU_GPIO_SOC_VARIANT_A8K:
    +			err = mvebu_pwm_probe_syscon(pdev, mvchip, id);
    +			break;
    +		default:
    +			err = mvebu_pwm_probe_raw(pdev, mvchip, id);
    +			break;
    +		}
    +		if (err) {
    +			dev_err(&pdev->dev, "no PWM available\n");
    +			/* fallthrou */
    +		}
    +	}
     
     	return 0;
     
Viewing 1 post (of 1 total)
  • You must be logged in to reply to this topic.

Technical specification tables can not be displayed on mobile. Please view on desktop