2018-06-19 10:56:30

by Tomer Maimon

[permalink] [raw]
Subject: [PATCH v2 0/2] hwmon: Add NPCM7xx PWM and Fan driver support

This patch set adds Pulse Width Modulation (PWM) and Fan tacho
support for the Nuvoton NPCM7xx Baseboard Management Controller (BMC).

The Nuvoton BMC NPCM7xx has two identical PWM controller modules,
each module has four PWM controller outputs and eight identical Fan
controller modules, each module has two Fan controller inputs.

The hwmon driver provides sysfs entries through which the user can
configure and get the duty cycle between 0 (disable the specific PWM)
to 255 of particular PWM output port.

The driver support cooling device creation, that could be bound
to a thermal zone for the thermal control.

The NPCM7xx PWM and Fan driver tested on NPCM750 evaluation board.

Addressed comments from:.
- Guenter Roeck: https://www.spinics.net/lists/devicetree/msg231982.html

Changes since version 1:
- Rename driver name
- Adding Fan Controller support
- Adding cooling device support
- Modifying dt-binding documentation for Fan and cooling binding

Tomer Maimon (2):
dt-binding: hwmon: Add NPCM7xx PWM and Fan controller documentation
hwmon: npcm750: add NPCM7xx PWM and Fan driver

.../devicetree/bindings/hwmon/npcm750-pwm-fan.txt | 84 ++
drivers/hwmon/Kconfig | 7 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/npcm750-pwm-fan.c | 1099 ++++++++++++++++++++
4 files changed, 1191 insertions(+)
create mode 100644 Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt
create mode 100644 drivers/hwmon/npcm750-pwm-fan.c

--
2.14.1



2018-06-19 10:56:06

by Tomer Maimon

[permalink] [raw]
Subject: [PATCH v2 1/2] dt-binding: hwmon: Add NPCM7xx PWM and Fan controller documentation

Added device tree binding documentation for Nuvoton BMC
NPCM7xx Pulse Width Modulation (PWM) and Fan tach controller.
The PWM controller can support upto 8 PWM output ports.
The Fan tach controller can support upto 16 tachometer inputs.

Signed-off-by: Tomer Maimon <[email protected]>
---
.../devicetree/bindings/hwmon/npcm750-pwm-fan.txt | 84 ++++++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt

diff --git a/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt
new file mode 100644
index 000000000000..a9eacda34f92
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt
@@ -0,0 +1,84 @@
+Nuvoton NPCM7xx PWM and Fan Tacho controller device driver
+
+The NPCM7xx has two identical Pulse-width modulation (PWM) controller modules,
+Each PWM module has four PWM controller outputs, Totally 8 PWM controller outputs.
+
+The NPCM7xx has eight identical Fan tachometer controller modules,
+Each Fan module has two Fan controller inputs, Totally 16 Fan controller inputs.
+
+Required properties for pwm-fan node:
+- compatible : "nuvoton,npcm750-pwm-fan" for Poleg NPCM7XX.
+- reg : specifies physical base address and size of the registers.
+- reg-names : must contain:
+ * "pwm-base" for the PWM registers.
+ * "fan-base" for the Fan registers.
+- clocks : phandle of reference clocks.
+- clock-names : must contain
+ * "clk_apb3" for clock of PWM controller.
+ * "clk_apb4" for clock of Fan controller.
+- interrupts : contain the Fan interrupts with flags for falling edge.
+- pinctrl-names : a pinctrl state named "default" must be defined.
+- pinctrl-0 : phandle referencing pin configuration of the PWM and Fan
+ controller ports.
+
+pwm subnode format:
+===================
+Under pwm subnode can be upto 8 child nodes, each child node representing a PWM channel.
+Each pwm subnode must have one PWM channel and atleast one Fan tach channel.
+
+For PWM channel can be configured cooling-levels to create cooling device.
+Cooling device could be bound to a thermal zone for the thermal control.
+
+Required properties for each child node:
+- pwm-ch : specify the PWM output channel.
+ integer value in the range 0 through 7, that represent
+ the PWM channel number that used.
+
+- fan-ch : specify the Fan input channel.
+ integer value in the range 0 through 15, that represent
+ the Fan channel number that used.
+
+ At least one Fan tach input channel is required
+
+Optional property for each child node:
+- cooling-levels: PWM duty cycle values in a range from 0 to 255
+ which correspond to thermal cooling states.
+
+Examples:
+
+pwm_fan:pwm_fan_controller@103000 {
+ compatible = "nuvoton,npcm750-pwm-fan";
+ reg = <0x103000 0x2000>,
+ <0x180000 0x8000>;
+ reg-names = "pwm_base", "fan_base";
+ clocks = <&clk NPCM7XX_CLK_APB3>,
+ <&clk NPCM7XX_CLK_APB4>;
+ clock-names = "clk_apb3","clk_apb4";
+ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm0_pins &pwm1_pins &pwm2_pins
+ &fanin0_pins &fanin1_pins &fanin2_pins
+ &fanin3_pins &fanin4_pins>;
+
+ pwm@0 {
+ pwm-ch = /bits/ 8 <0x00>;
+ fan-ch = /bits/ 8 <0x00 0x01>;
+ cooling-levels = <127 255>;
+ };
+ pwm@1 {
+ pwm-ch = /bits/ 8 <0x01>;
+ fan-ch = /bits/ 8 <0x02 0x03>;
+ };
+ pwm@2 {
+ pwm-ch = /bits/ 8 <0x02>;
+ fan-ch = /bits/ 8 <0x04>;
+ };
+
+};
\ No newline at end of file
--
2.14.1


2018-06-19 10:57:44

by Tomer Maimon

[permalink] [raw]
Subject: [PATCH v2 2/2] hwmon: npcm750: add NPCM7xx PWM and Fan driver

Add Nuvoton BMC NPCM7xx Pulse Width Modulation (PWM) and Fan tacho driver.

The Nuvoton BMC NPCM7xx has two identical PWM controller modules,
each module has four PWM controller outputs and eight identical Fan
controller modules, each module has two Fan controller inputs.

The driver provides a sysfs entries through which the user can
configure the duty-cycle value (ranging from 0 to 100 percent)
and read the fan tacho rpm value.

The driver support cooling device creation, that could be bound
to a thermal zone for the thermal control.

Signed-off-by: Tomer Maimon <[email protected]>
---
drivers/hwmon/Kconfig | 7 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/npcm750-pwm-fan.c | 1099 +++++++++++++++++++++++++++++++++++++++
3 files changed, 1107 insertions(+)
create mode 100644 drivers/hwmon/npcm750-pwm-fan.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index f10840ad465c..f6c2eff9bb7d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1888,6 +1888,13 @@ config SENSORS_XGENE
If you say yes here you get support for the temperature
and power sensors for APM X-Gene SoC.

+config SENSORS_NPCM7XX
+ tristate "Nuvoton NPCM7XX PWM and Fan driver"
+ depends on THERMAL || THERMAL=n
+ help
+ This driver provides support for Nuvoton NPCM7XX PWM and Fan
+ controllers.
+
if ACPI

comment "ACPI drivers"
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index e7d52a36e6c4..004e2ec5b68f 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -174,6 +174,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
+obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o

obj-$(CONFIG_PMBUS) += pmbus/

diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c
new file mode 100644
index 000000000000..82898197686f
--- /dev/null
+++ b/drivers/hwmon/npcm750-pwm-fan.c
@@ -0,0 +1,1099 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/sysfs.h>
+#include <linux/spinlock.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/thermal.h>
+
+/* NPCM7XX PWM registers */
+#define NPCM7XX_PWM_REG_BASE(base, n) (base + ((n) * 0x1000L))
+
+#define NPCM7XX_PWM_REG_PR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x00)
+#define NPCM7XX_PWM_REG_CSR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x04)
+#define NPCM7XX_PWM_REG_CR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x08)
+#define NPCM7XX_PWM_REG_CNRx(base, n, ch) \
+ (NPCM7XX_PWM_REG_BASE(base, n) + 0x0C + (12 * ch))
+#define NPCM7XX_PWM_REG_CMRx(base, n, ch) \
+ (NPCM7XX_PWM_REG_BASE(base, n) + 0x10 + (12 * ch))
+#define NPCM7XX_PWM_REG_PDRx(base, n, ch) \
+ (NPCM7XX_PWM_REG_BASE(base, n) + 0x14 + (12 * ch))
+#define NPCM7XX_PWM_REG_PIER(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x3C)
+#define NPCM7XX_PWM_REG_PIIR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x40)
+#define NPCM7XX_FAN_REG_BASE(base, n) (base + ((n) * 0x1000L))
+
+#define NPCM7XX_PWM_CTRL_CH0_MODE_BIT BIT(3)
+#define NPCM7XX_PWM_CTRL_CH1_MODE_BIT BIT(11)
+#define NPCM7XX_PWM_CTRL_CH2_MODE_BIT BIT(15)
+#define NPCM7XX_PWM_CTRL_CH3_MODE_BIT BIT(19)
+
+#define NPCM7XX_PWM_CTRL_CH0_INV_BIT BIT(2)
+#define NPCM7XX_PWM_CTRL_CH1_INV_BIT BIT(10)
+#define NPCM7XX_PWM_CTRL_CH2_INV_BIT BIT(14)
+#define NPCM7XX_PWM_CTRL_CH3_INV_BIT BIT(18)
+
+#define NPCM7XX_PWM_CTRL_CH0_EN_BIT BIT(0)
+#define NPCM7XX_PWM_CTRL_CH1_EN_BIT BIT(8)
+#define NPCM7XX_PWM_CTRL_CH2_EN_BIT BIT(12)
+#define NPCM7XX_PWM_CTRL_CH3_EN_BIT BIT(16)
+
+/* Define the maximum PWM channel number */
+#define NPCM7XX_PWM_MAX_CHN_NUM 8
+#define NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE 4
+#define NPCM7XX_PWM_MAX_MODULES 2
+
+/* Define the Counter Register, value = 100 for match 100% */
+#define NPCM7XX_PWM_COUNTER_DEFALUT_NUM 255
+#define NPCM7XX_PWM_COMPARATOR_DEFALUT_NUM 127
+
+#define NPCM7XX_PWM_COMPARATOR_MAX 255
+
+/* default all PWM channels PRESCALE2 = 1 */
+#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH0 0x4
+#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH1 0x40
+#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH2 0x400
+#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH3 0x4000
+
+#define PWM_OUTPUT_FREQ_25KHZ 25000
+#define PWN_CNT_DEFAULT 256
+#define MIN_PRESCALE1 2
+#define NPCM7XX_PWM_PRESCALE_SHIFT_CH01 8
+
+#define NPCM7XX_PWM_PRESCALE2_DEFALUT (NPCM7XX_PWM_PRESCALE2_DEFALUT_CH0 | \
+ NPCM7XX_PWM_PRESCALE2_DEFALUT_CH1 | \
+ NPCM7XX_PWM_PRESCALE2_DEFALUT_CH2 | \
+ NPCM7XX_PWM_PRESCALE2_DEFALUT_CH3)
+
+#define NPCM7XX_PWM_CTRL_MODE_DEFALUT (NPCM7XX_PWM_CTRL_CH0_MODE_BIT | \
+ NPCM7XX_PWM_CTRL_CH1_MODE_BIT | \
+ NPCM7XX_PWM_CTRL_CH2_MODE_BIT | \
+ NPCM7XX_PWM_CTRL_CH3_MODE_BIT)
+
+#define NPCM7XX_PWM_CTRL_EN_DEFALUT (NPCM7XX_PWM_CTRL_CH0_EN_BIT | \
+ NPCM7XX_PWM_CTRL_CH1_EN_BIT | \
+ NPCM7XX_PWM_CTRL_CH2_EN_BIT | \
+ NPCM7XX_PWM_CTRL_CH3_EN_BIT)
+
+/* NPCM7XX FAN Tacho registers */
+#define NPCM7XX_FAN_REG_BASE(base, n) (base + ((n) * 0x1000L))
+
+#define NPCM7XX_FAN_REG_TCNT1(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x00)
+#define NPCM7XX_FAN_REG_TCRA(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x02)
+#define NPCM7XX_FAN_REG_TCRB(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x04)
+#define NPCM7XX_FAN_REG_TCNT2(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x06)
+#define NPCM7XX_FAN_REG_TPRSC(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x08)
+#define NPCM7XX_FAN_REG_TCKC(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0A)
+#define NPCM7XX_FAN_REG_TMCTRL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0C)
+#define NPCM7XX_FAN_REG_TICTRL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0E)
+#define NPCM7XX_FAN_REG_TICLR(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x10)
+#define NPCM7XX_FAN_REG_TIEN(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x12)
+#define NPCM7XX_FAN_REG_TCPA(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x14)
+#define NPCM7XX_FAN_REG_TCPB(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x16)
+#define NPCM7XX_FAN_REG_TCPCFG(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x18)
+#define NPCM7XX_FAN_REG_TINASEL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x1A)
+#define NPCM7XX_FAN_REG_TINBSEL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x1C)
+
+#define NPCM7XX_FAN_TCKC_CLKX_NONE 0
+#define NPCM7XX_FAN_TCKC_CLK1_APB BIT(0)
+#define NPCM7XX_FAN_TCKC_CLK2_APB BIT(3)
+
+#define NPCM7XX_FAN_TMCTRL_TBEN BIT(6)
+#define NPCM7XX_FAN_TMCTRL_TAEN BIT(5)
+#define NPCM7XX_FAN_TMCTRL_TBEDG BIT(4)
+#define NPCM7XX_FAN_TMCTRL_TAEDG BIT(3)
+#define NPCM7XX_FAN_TMCTRL_MODE_5 BIT(2)
+
+#define NPCM7XX_FAN_TICLR_CLEAR_ALL GENMASK(5, 0)
+#define NPCM7XX_FAN_TICLR_TFCLR BIT(5)
+#define NPCM7XX_FAN_TICLR_TECLR BIT(4)
+#define NPCM7XX_FAN_TICLR_TDCLR BIT(3)
+#define NPCM7XX_FAN_TICLR_TCCLR BIT(2)
+#define NPCM7XX_FAN_TICLR_TBCLR BIT(1)
+#define NPCM7XX_FAN_TICLR_TACLR BIT(0)
+
+#define NPCM7XX_FAN_TIEN_ENABLE_ALL GENMASK(5, 0)
+#define NPCM7XX_FAN_TIEN_TFIEN BIT(5)
+#define NPCM7XX_FAN_TIEN_TEIEN BIT(4)
+#define NPCM7XX_FAN_TIEN_TDIEN BIT(3)
+#define NPCM7XX_FAN_TIEN_TCIEN BIT(2)
+#define NPCM7XX_FAN_TIEN_TBIEN BIT(1)
+#define NPCM7XX_FAN_TIEN_TAIEN BIT(0)
+
+#define NPCM7XX_FAN_TICTRL_TFPND BIT(5)
+#define NPCM7XX_FAN_TICTRL_TEPND BIT(4)
+#define NPCM7XX_FAN_TICTRL_TDPND BIT(3)
+#define NPCM7XX_FAN_TICTRL_TCPND BIT(2)
+#define NPCM7XX_FAN_TICTRL_TBPND BIT(1)
+#define NPCM7XX_FAN_TICTRL_TAPND BIT(0)
+
+#define NPCM7XX_FAN_TCPCFG_HIBEN BIT(7)
+#define NPCM7XX_FAN_TCPCFG_EQBEN BIT(6)
+#define NPCM7XX_FAN_TCPCFG_LOBEN BIT(5)
+#define NPCM7XX_FAN_TCPCFG_CPBSEL BIT(4)
+#define NPCM7XX_FAN_TCPCFG_HIAEN BIT(3)
+#define NPCM7XX_FAN_TCPCFG_EQAEN BIT(2)
+#define NPCM7XX_FAN_TCPCFG_LOAEN BIT(1)
+#define NPCM7XX_FAN_TCPCFG_CPASEL BIT(0)
+
+/* FAN General Defintion */
+/* Define the maximum FAN channel number */
+#define NPCM7XX_FAN_MAX_MODULE 8
+#define NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE 2
+#define NPCM7XX_FAN_MAX_CHN_NUM 16
+
+/*
+ * Get Fan Tach Timeout (base on clock 214843.75Hz, 1 cnt = 4.654us)
+ * Timeout 94ms ~= 0x5000
+ * (The minimum FAN speed could to support ~640RPM/pulse 1,
+ * 320RPM/pulse 2, ...-- 10.6Hz)
+ */
+#define NPCM7XX_FAN_TIMEOUT 0x5000
+#define NPCM7XX_FAN_TCNT 0xFFFF
+#define NPCM7XX_FAN_TCPA (NPCM7XX_FAN_TCNT - NPCM7XX_FAN_TIMEOUT)
+#define NPCM7XX_FAN_TCPB (NPCM7XX_FAN_TCNT - NPCM7XX_FAN_TIMEOUT)
+
+#define NPCM7XX_FAN_POLL_TIMER_200MS 200
+#define NPCM7XX_FAN_DEFAULT_PULSE_PER_REVOLUTION 2
+#define NPCM7XX_FAN_TINASEL_FANIN_DEFAULT 0
+#define NPCM7XX_FAN_CLK_PRESCALE 255
+
+#define NPCM7XX_FAN_CMPA 0
+#define NPCM7XX_FAN_CMPB 1
+
+/* Obtain the fan number */
+#define NPCM7XX_FAN_INPUT(fan, cmp) ((fan << 1) + (cmp))
+
+/* fan sample status */
+#define FAN_DISABLE 0xFF
+#define FAN_INIT 0x00
+#define FAN_PREPARE_TO_GET_FIRST_CAPTURE 0x01
+#define FAN_ENOUGH_SAMPLE 0x02
+
+struct Fan_Dev {
+ u8 FanStFlag;
+ u8 FanPlsPerRev;
+ u16 FanCnt;
+ u32 FanCntTemp;
+};
+
+struct npcm7xx_cooling_device {
+ char name[THERMAL_NAME_LENGTH];
+ struct npcm7xx_pwm_fan_data *data;
+ struct thermal_cooling_device *tcdev;
+ int pwm_port;
+ u8 *cooling_levels;
+ u8 max_state;
+ u8 cur_state;
+};
+
+struct npcm7xx_pwm_fan_data {
+ void __iomem *pwm_base, *fan_base;
+ unsigned long pwm_clk_freq, fan_clk_freq;
+ struct clk *pwm_clk, *fan_clk;
+ struct mutex npcm7xx_pwm_lock[NPCM7XX_PWM_MAX_MODULES];
+ spinlock_t npcm7xx_fan_lock[NPCM7XX_FAN_MAX_MODULE];
+ int fan_irq[NPCM7XX_FAN_MAX_MODULE];
+ bool pwm_present[NPCM7XX_PWM_MAX_CHN_NUM],
+ fan_present[NPCM7XX_FAN_MAX_CHN_NUM];
+ u32 InputClkFreq;
+ struct timer_list npcm7xx_fan_timer;
+ struct Fan_Dev npcm7xx_fan[NPCM7XX_FAN_MAX_CHN_NUM];
+ struct npcm7xx_cooling_device *cdev[NPCM7XX_PWM_MAX_CHN_NUM];
+ u8 npcm7xx_fan_select;
+};
+
+static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data,
+ int channel, u16 val)
+{
+ u32 PWMCh = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
+ u32 module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
+ u32 u32TmpBuf = 0, ctrl_en_bit, env_bit;
+
+ /*
+ * Config PWM Comparator register for setting duty cycle
+ */
+ if (val < 0 || val > NPCM7XX_PWM_COMPARATOR_MAX)
+ return -EINVAL;
+
+ mutex_lock(&data->npcm7xx_pwm_lock[module]);
+
+ /* write new CMR value */
+ iowrite32(val, NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, PWMCh));
+ u32TmpBuf = ioread32(NPCM7XX_PWM_REG_CR(data->pwm_base, module));
+
+ switch (PWMCh) {
+ case 0:
+ ctrl_en_bit = NPCM7XX_PWM_CTRL_CH0_EN_BIT;
+ env_bit = NPCM7XX_PWM_CTRL_CH0_INV_BIT;
+ break;
+ case 1:
+ ctrl_en_bit = NPCM7XX_PWM_CTRL_CH1_EN_BIT;
+ env_bit = NPCM7XX_PWM_CTRL_CH1_INV_BIT;
+ break;
+ case 2:
+ ctrl_en_bit = NPCM7XX_PWM_CTRL_CH2_EN_BIT;
+ env_bit = NPCM7XX_PWM_CTRL_CH2_INV_BIT;
+ break;
+ case 3:
+ ctrl_en_bit = NPCM7XX_PWM_CTRL_CH3_EN_BIT;
+ env_bit = NPCM7XX_PWM_CTRL_CH3_INV_BIT;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ if (val == 0) {
+ /* Disable PWM */
+ u32TmpBuf &= ~(ctrl_en_bit);
+ u32TmpBuf |= env_bit;
+ } else {
+ /* Enable PWM */
+ u32TmpBuf |= ctrl_en_bit;
+ u32TmpBuf &= ~(env_bit);
+ }
+
+ iowrite32(u32TmpBuf, NPCM7XX_PWM_REG_CR(data->pwm_base, module));
+ mutex_unlock(&data->npcm7xx_pwm_lock[module]);
+
+ return 0;
+}
+
+static inline void npcm7xx_fan_start_capture(struct npcm7xx_pwm_fan_data *data,
+ u8 fan, u8 cmp)
+{
+ u8 fan_id = 0;
+ u8 reg_mode = 0;
+ u8 reg_int = 0;
+ unsigned long flags;
+
+ fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
+
+ /* to check whether any fan tach is enable */
+ if (data->npcm7xx_fan[fan_id].FanStFlag != FAN_DISABLE) {
+ /* reset status */
+ spin_lock_irqsave(&data->npcm7xx_fan_lock[fan], flags);
+
+ data->npcm7xx_fan[fan_id].FanStFlag = FAN_INIT;
+ reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ if (cmp == NPCM7XX_FAN_CMPA) {
+ /* enable interrupt */
+ iowrite8((u8) (reg_int | (NPCM7XX_FAN_TIEN_TAIEN |
+ NPCM7XX_FAN_TIEN_TEIEN)),
+ NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ reg_mode = NPCM7XX_FAN_TCKC_CLK1_APB
+ | ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base,
+ fan));
+
+ /* start to Capture */
+ iowrite8(reg_mode, NPCM7XX_FAN_REG_TCKC(data->fan_base,
+ fan));
+ } else {
+ /* enable interrupt */
+ iowrite8((u8) (reg_int | (NPCM7XX_FAN_TIEN_TBIEN |
+ NPCM7XX_FAN_TIEN_TFIEN)),
+ NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ reg_mode =
+ NPCM7XX_FAN_TCKC_CLK2_APB
+ | ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base,
+ fan));
+
+ /* start to Capture */
+ iowrite8(reg_mode,
+ NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+ }
+
+ spin_unlock_irqrestore(&data->npcm7xx_fan_lock[fan], flags);
+ }
+}
+
+/*
+ * Enable a background timer to poll fan tach value, (200ms * 4)
+ * to polling all fan
+ */
+static void npcm7xx_fan_polling(struct timer_list *t)
+{
+ struct npcm7xx_pwm_fan_data *data;
+ unsigned long flags;
+ int i;
+
+ data = from_timer(data, t, npcm7xx_fan_timer);
+
+ /*
+ * Polling two module per one round,
+ * FAN01 & FAN89 / FAN23 & FAN1011 / FAN45 & FAN1213 / FAN67 & FAN1415
+ */
+ for (i = data->npcm7xx_fan_select; i < NPCM7XX_FAN_MAX_MODULE;
+ i = i+4) {
+ /* clear the flag and reset the counter (TCNT) */
+ spin_lock_irqsave(&data->npcm7xx_fan_lock[i], flags);
+ iowrite8((u8) NPCM7XX_FAN_TICLR_CLEAR_ALL,
+ NPCM7XX_FAN_REG_TICLR(data->fan_base, i));
+ spin_unlock_irqrestore(&data->npcm7xx_fan_lock[i], flags);
+
+ if (data->fan_present[i*2] == true) {
+ spin_lock_irqsave(&data->npcm7xx_fan_lock[i], flags);
+ iowrite16(NPCM7XX_FAN_TCNT,
+ NPCM7XX_FAN_REG_TCNT1(data->fan_base, i));
+ spin_unlock_irqrestore(&data->npcm7xx_fan_lock[i],
+ flags);
+ npcm7xx_fan_start_capture(data, i, NPCM7XX_FAN_CMPA);
+ }
+ if (data->fan_present[(i*2)+1] == true) {
+ spin_lock_irqsave(&data->npcm7xx_fan_lock[i], flags);
+ iowrite16(NPCM7XX_FAN_TCNT,
+ NPCM7XX_FAN_REG_TCNT2(data->fan_base, i));
+ spin_unlock_irqrestore(&data->npcm7xx_fan_lock[i],
+ flags);
+ npcm7xx_fan_start_capture(data, i, NPCM7XX_FAN_CMPB);
+ }
+ }
+
+ data->npcm7xx_fan_select++;
+ data->npcm7xx_fan_select &= 0x3;
+
+ /* reset the timer interval */
+ data->npcm7xx_fan_timer.expires = jiffies +
+ msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
+ add_timer(&data->npcm7xx_fan_timer);
+}
+
+static inline void npcm7xx_fan_compute(struct npcm7xx_pwm_fan_data *data,
+ u8 fan, u8 cmp, u8 fan_id,
+ u8 flag_int, u8 flag_mode,
+ u8 flag_clear)
+{
+ u8 reg_int = 0;
+ u8 reg_mode = 0;
+ u16 fan_cap = 0;
+
+ if (cmp == NPCM7XX_FAN_CMPA)
+ fan_cap = ioread16(NPCM7XX_FAN_REG_TCRA(data->fan_base, fan));
+ else
+ fan_cap = ioread16(NPCM7XX_FAN_REG_TCRB(data->fan_base, fan));
+
+ /* clear capature flag, H/W will auto reset the NPCM7XX_FAN_TCNTx */
+ iowrite8((u8) flag_clear, NPCM7XX_FAN_REG_TICLR(data->fan_base, fan));
+
+ if (data->npcm7xx_fan[fan_id].FanStFlag == FAN_INIT) {
+ /* First capture, drop it */
+ data->npcm7xx_fan[fan_id].FanStFlag =
+ FAN_PREPARE_TO_GET_FIRST_CAPTURE;
+
+ /* reset counter */
+ data->npcm7xx_fan[fan_id].FanCntTemp = 0;
+ } else if (data->npcm7xx_fan[fan_id].FanStFlag <
+ FAN_ENOUGH_SAMPLE) {
+ /*
+ * collect the enough sample,
+ * (ex: 2 pulse fan need to get 2 sample)
+ */
+ data->npcm7xx_fan[fan_id].FanCntTemp +=
+ (NPCM7XX_FAN_TCNT - fan_cap);
+
+ data->npcm7xx_fan[fan_id].FanStFlag++;
+ } else {
+ /* get enough sample or fan disable */
+ if (data->npcm7xx_fan[fan_id].FanStFlag == FAN_ENOUGH_SAMPLE) {
+ data->npcm7xx_fan[fan_id].FanCntTemp +=
+ (NPCM7XX_FAN_TCNT - fan_cap);
+
+ /* compute finial average cnt per pulse */
+ data->npcm7xx_fan[fan_id].FanCnt
+ = data->npcm7xx_fan[fan_id].FanCntTemp /
+ FAN_ENOUGH_SAMPLE;
+
+ data->npcm7xx_fan[fan_id].FanStFlag = FAN_INIT;
+ }
+
+ reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ /* disable interrupt */
+ iowrite8((u8) (reg_int & ~flag_int),
+ NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+ reg_mode = ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+
+ /* stop capturing */
+ iowrite8((u8) (reg_mode & ~flag_mode),
+ NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+
+ }
+}
+
+static inline void npcm7xx_check_cmp(struct npcm7xx_pwm_fan_data *data,
+ u8 fan, u8 cmp, u8 flag)
+{
+ u8 reg_int = 0;
+ u8 reg_mode = 0;
+ u8 flag_timeout;
+ u8 flag_cap;
+ u8 flag_clear;
+ u8 flag_int;
+ u8 flag_mode;
+ u8 fan_id;
+
+ fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
+
+ if (cmp == NPCM7XX_FAN_CMPA) {
+ flag_cap = NPCM7XX_FAN_TICTRL_TAPND;
+ flag_timeout = NPCM7XX_FAN_TICTRL_TEPND;
+ flag_int = NPCM7XX_FAN_TIEN_TAIEN | NPCM7XX_FAN_TIEN_TEIEN;
+ flag_mode = NPCM7XX_FAN_TCKC_CLK1_APB;
+ flag_clear = NPCM7XX_FAN_TICLR_TACLR | NPCM7XX_FAN_TICLR_TECLR;
+ } else {
+ flag_cap = NPCM7XX_FAN_TICTRL_TBPND;
+ flag_timeout = NPCM7XX_FAN_TICTRL_TFPND;
+ flag_int = NPCM7XX_FAN_TIEN_TBIEN | NPCM7XX_FAN_TIEN_TFIEN;
+ flag_mode = NPCM7XX_FAN_TCKC_CLK2_APB;
+ flag_clear = NPCM7XX_FAN_TICLR_TBCLR | NPCM7XX_FAN_TICLR_TFCLR;
+ }
+
+
+ if (flag & flag_timeout) {
+
+ reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ /* disable interrupt */
+ iowrite8((u8) (reg_int & ~flag_int),
+ NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ /* clear interrup flag */
+ iowrite8((u8) flag_clear, NPCM7XX_FAN_REG_TICLR(data->fan_base,
+ fan));
+
+ reg_mode = ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+
+ /* stop capturing */
+ iowrite8((u8) (reg_mode & ~flag_mode),
+ NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+
+ /*
+ * If timeout occurs (NPCM7XX_FAN_TIMEOUT), the fan doesn't
+ * connect or speed is lower than 10.6Hz (320RPM/pulse2).
+ * In these situation, the RPM output should be zero.
+ */
+ data->npcm7xx_fan[fan_id].FanCnt = 0;
+ //DEBUG_MSG("%s : it is timeout fan_id %d\n", __func__, fan_id);
+ } else {
+ /* input capture is occurred */
+ if (flag & flag_cap)
+ npcm7xx_fan_compute(data, fan, cmp, fan_id, flag_int,
+ flag_mode, flag_clear);
+ }
+}
+
+static irqreturn_t npcm7xx_fan_isr(int irq, void *dev_id)
+{
+ struct npcm7xx_pwm_fan_data *data = dev_id;
+ u8 flag = 0;
+ int module;
+ unsigned long flags;
+
+ module = irq - data->fan_irq[0];
+ spin_lock_irqsave(&data->npcm7xx_fan_lock[module], flags);
+
+ flag = ioread8(NPCM7XX_FAN_REG_TICTRL(data->fan_base, module));
+ if (flag > 0) {
+ npcm7xx_check_cmp(data, module, NPCM7XX_FAN_CMPA, flag);
+ npcm7xx_check_cmp(data, module, NPCM7XX_FAN_CMPB, flag);
+ spin_unlock_irqrestore(&data->npcm7xx_fan_lock[module], flags);
+ return IRQ_HANDLED;
+ }
+
+ spin_unlock_irqrestore(&data->npcm7xx_fan_lock[module], flags);
+
+ return IRQ_NONE;
+}
+
+static int npcm7xx_read_pwm(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
+ u32 PWMCh = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
+ u32 module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ mutex_lock(&data->npcm7xx_pwm_lock[module]);
+ *val = (long)ioread32(
+ NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, PWMCh));
+ mutex_unlock(&data->npcm7xx_pwm_lock[module]);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int npcm7xx_write_pwm(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
+ int err = 0;
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ err = npcm7xx_pwm_config_set(data, channel, (u16)val);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+static umode_t npcm7xx_pwm_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct npcm7xx_pwm_fan_data *data = _data;
+
+ if (!data->pwm_present[channel])
+ return 0;
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static int npcm7xx_read_fan(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_fan_input:
+ *val = 0;
+ if (data->npcm7xx_fan[channel].FanCnt <= 0)
+ return data->npcm7xx_fan[channel].FanCnt;
+
+ /* Convert the raw reading to RPM */
+ if ((data->npcm7xx_fan[channel].FanCnt > 0) &&
+ data->npcm7xx_fan[channel].FanPlsPerRev > 0)
+ *val = (long)((data->InputClkFreq * 60)/
+ (data->npcm7xx_fan[channel].FanCnt *
+ data->npcm7xx_fan[channel].FanPlsPerRev));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t npcm7xx_fan_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct npcm7xx_pwm_fan_data *data = _data;
+
+ if (!data->fan_present[channel])
+ return 0;
+
+ switch (attr) {
+ case hwmon_fan_input:
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static int npcm7xx_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_pwm:
+ return npcm7xx_read_pwm(dev, attr, channel, val);
+ case hwmon_fan:
+ return npcm7xx_read_fan(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int npcm7xx_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_pwm:
+ return npcm7xx_write_pwm(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t npcm7xx_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_pwm:
+ return npcm7xx_pwm_is_visible(data, attr, channel);
+ case hwmon_fan:
+ return npcm7xx_fan_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
+
+static const u32 npcm7xx_pwm_config[] = {
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info npcm7xx_pwm = {
+ .type = hwmon_pwm,
+ .config = npcm7xx_pwm_config,
+};
+
+static const u32 npcm7xx_fan_config[] = {
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info npcm7xx_fan = {
+ .type = hwmon_fan,
+ .config = npcm7xx_fan_config,
+};
+
+static const struct hwmon_channel_info *npcm7xx_info[] = {
+ &npcm7xx_pwm,
+ &npcm7xx_fan,
+ NULL
+};
+
+static const struct hwmon_ops npcm7xx_hwmon_ops = {
+ .is_visible = npcm7xx_is_visible,
+ .read = npcm7xx_read,
+ .write = npcm7xx_write,
+};
+
+static const struct hwmon_chip_info npcm7xx_chip_info = {
+ .ops = &npcm7xx_hwmon_ops,
+ .info = npcm7xx_info,
+};
+
+static u32 npcm7xx_pwm_init(struct npcm7xx_pwm_fan_data *data)
+{
+ int m, ch;
+ u32 Prescale_val, output_freq;
+
+ data->pwm_clk_freq = clk_get_rate(data->pwm_clk);
+
+ /* Adjust NPCM7xx PWMs output frequency to ~25Khz */
+ output_freq = data->pwm_clk_freq / PWN_CNT_DEFAULT;
+ Prescale_val = DIV_ROUND_CLOSEST(output_freq, PWM_OUTPUT_FREQ_25KHZ);
+
+ /* If Prescale_val = 0, then the prescale output clock is stopped */
+ if (Prescale_val < MIN_PRESCALE1)
+ Prescale_val = MIN_PRESCALE1;
+ /*
+ * Prescale_val need to decrement in one because in the PWM Prescale
+ * register the Prescale value increment by one
+ */
+ Prescale_val--;
+
+ /* Setting PWM Prescale Register value register to both modules */
+ Prescale_val |= (Prescale_val << NPCM7XX_PWM_PRESCALE_SHIFT_CH01);
+
+ for (m = 0; m < NPCM7XX_PWM_MAX_MODULES ; m++) {
+ iowrite32(Prescale_val, NPCM7XX_PWM_REG_PR(data->pwm_base, m));
+ iowrite32(NPCM7XX_PWM_PRESCALE2_DEFALUT,
+ NPCM7XX_PWM_REG_CSR(data->pwm_base, m));
+ iowrite32(NPCM7XX_PWM_CTRL_MODE_DEFALUT,
+ NPCM7XX_PWM_REG_CR(data->pwm_base, m));
+
+ for (ch = 0; ch < NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE; ch++) {
+ iowrite32(NPCM7XX_PWM_COUNTER_DEFALUT_NUM,
+ NPCM7XX_PWM_REG_CNRx(data->pwm_base, m, ch));
+ }
+ }
+
+ return output_freq / ((Prescale_val & 0xf) + 1);
+}
+
+static void npcm7xx_fan_init(struct npcm7xx_pwm_fan_data *data)
+{
+ int md;
+ int ch;
+ int i;
+ u32 apb_clk_freq;
+
+ for (md = 0; md < NPCM7XX_FAN_MAX_MODULE; md++) {
+
+ /* stop FAN0~7 clock */
+ iowrite8((u8)NPCM7XX_FAN_TCKC_CLKX_NONE,
+ NPCM7XX_FAN_REG_TCKC(data->fan_base, md));
+
+ /* disable all interrupt */
+ iowrite8((u8) 0x00, NPCM7XX_FAN_REG_TIEN(data->fan_base, md));
+
+ /* clear all interrupt */
+ iowrite8((u8) NPCM7XX_FAN_TICLR_CLEAR_ALL,
+ NPCM7XX_FAN_REG_TICLR(data->fan_base, md));
+
+ /* set FAN0~7 clock prescaler */
+ iowrite8((u8) NPCM7XX_FAN_CLK_PRESCALE,
+ NPCM7XX_FAN_REG_TPRSC(data->fan_base, md));
+
+ /* set FAN0~7 mode (high-to-low transition) */
+ iowrite8(
+ (u8) (
+ NPCM7XX_FAN_TMCTRL_MODE_5 |
+ NPCM7XX_FAN_TMCTRL_TBEN |
+ NPCM7XX_FAN_TMCTRL_TAEN
+ ),
+ NPCM7XX_FAN_REG_TMCTRL(data->fan_base, md)
+ );
+
+ /* set FAN0~7 Initial Count/Cap */
+ iowrite16(NPCM7XX_FAN_TCNT,
+ NPCM7XX_FAN_REG_TCNT1(data->fan_base, md));
+ iowrite16(NPCM7XX_FAN_TCNT,
+ NPCM7XX_FAN_REG_TCNT2(data->fan_base, md));
+
+ /* set FAN0~7 compare (equal to count) */
+ iowrite8((u8)(NPCM7XX_FAN_TCPCFG_EQAEN |
+ NPCM7XX_FAN_TCPCFG_EQBEN),
+ NPCM7XX_FAN_REG_TCPCFG(data->fan_base, md));
+
+ /* set FAN0~7 compare value */
+ iowrite16(NPCM7XX_FAN_TCPA,
+ NPCM7XX_FAN_REG_TCPA(data->fan_base, md));
+ iowrite16(NPCM7XX_FAN_TCPB,
+ NPCM7XX_FAN_REG_TCPB(data->fan_base, md));
+
+ /* set FAN0~7 fan input FANIN 0~15 */
+ iowrite8((u8) NPCM7XX_FAN_TINASEL_FANIN_DEFAULT,
+ NPCM7XX_FAN_REG_TINASEL(data->fan_base, md));
+ iowrite8((u8) NPCM7XX_FAN_TINASEL_FANIN_DEFAULT,
+ NPCM7XX_FAN_REG_TINBSEL(data->fan_base, md));
+
+ for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE; i++) {
+ ch = md * NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE + i;
+ data->npcm7xx_fan[ch].FanStFlag = FAN_DISABLE;
+ data->npcm7xx_fan[ch].FanPlsPerRev =
+ NPCM7XX_FAN_DEFAULT_PULSE_PER_REVOLUTION;
+ data->npcm7xx_fan[ch].FanCnt = 0;
+ }
+ }
+
+ apb_clk_freq = clk_get_rate(data->fan_clk);
+
+ /* Fan tach input clock = APB clock / prescalar, default is 255. */
+ data->InputClkFreq = apb_clk_freq / (NPCM7XX_FAN_CLK_PRESCALE + 1);
+}
+
+static int
+npcm7xx_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev,
+ unsigned long *state)
+{
+ struct npcm7xx_cooling_device *cdev = tcdev->devdata;
+
+ *state = cdev->max_state;
+
+ return 0;
+}
+
+static int
+npcm7xx_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev,
+ unsigned long *state)
+{
+ struct npcm7xx_cooling_device *cdev = tcdev->devdata;
+
+ *state = cdev->cur_state;
+
+ return 0;
+}
+
+static int
+npcm7xx_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev,
+ unsigned long state)
+{
+ struct npcm7xx_cooling_device *cdev = tcdev->devdata;
+ int ret;
+
+ if (state > cdev->max_state)
+ return -EINVAL;
+
+ cdev->cur_state = state;
+ ret = npcm7xx_pwm_config_set(cdev->data, cdev->pwm_port,
+ cdev->cooling_levels[cdev->cur_state]);
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops npcm7xx_pwm_cool_ops = {
+ .get_max_state = npcm7xx_pwm_cz_get_max_state,
+ .get_cur_state = npcm7xx_pwm_cz_get_cur_state,
+ .set_cur_state = npcm7xx_pwm_cz_set_cur_state,
+};
+
+static int npcm7xx_create_pwm_cooling(struct device *dev,
+ struct device_node *child,
+ struct npcm7xx_pwm_fan_data *data,
+ u32 pwm_port, u8 num_levels)
+{
+ int ret;
+ struct npcm7xx_cooling_device *cdev;
+
+ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL);
+ if (!cdev->cooling_levels)
+ return -ENOMEM;
+
+ cdev->max_state = num_levels - 1;
+ ret = of_property_read_u8_array(child, "cooling-levels",
+ cdev->cooling_levels,
+ num_levels);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read.\n");
+ return ret;
+ }
+ snprintf(cdev->name, THERMAL_NAME_LENGTH, "%s%d", child->name,
+ pwm_port);
+
+ cdev->tcdev = thermal_of_cooling_device_register(child,
+ cdev->name,
+ cdev,
+ &npcm7xx_pwm_cool_ops);
+ if (IS_ERR(cdev->tcdev))
+ return PTR_ERR(cdev->tcdev);
+
+ cdev->data = data;
+ cdev->pwm_port = pwm_port;
+
+ data->cdev[pwm_port] = cdev;
+
+ return 0;
+}
+
+static int npcm7xx_en_pwm_fan(struct device *dev,
+ struct device_node *child,
+ struct npcm7xx_pwm_fan_data *data)
+{
+ u8 *fan_ch;
+ u8 pwm_port;
+ int ret, fan_cnt;
+ u8 index, ch;
+
+ ret = of_property_read_u8(child, "pwm-ch", &pwm_port);
+ if (ret)
+ return ret;
+
+ data->pwm_present[pwm_port] = true;
+ ret = npcm7xx_pwm_config_set(data, pwm_port,
+ NPCM7XX_PWM_COMPARATOR_DEFALUT_NUM);
+
+ ret = of_property_count_u8_elems(child, "cooling-levels");
+ if (ret > 0) {
+ ret = npcm7xx_create_pwm_cooling(dev, child, data, pwm_port,
+ ret);
+ if (ret)
+ return ret;
+ }
+
+ fan_cnt = of_property_count_u8_elems(child, "fan-ch");
+ if (fan_cnt < 1)
+ return -EINVAL;
+
+ fan_ch = devm_kzalloc(dev, sizeof(*fan_ch) * fan_cnt, GFP_KERNEL);
+ if (!fan_ch)
+ return -ENOMEM;
+
+ ret = of_property_read_u8_array(child, "fan-ch", fan_ch, fan_cnt);
+ if (ret)
+ return ret;
+
+ for (ch = 0; ch < fan_cnt; ch++) {
+ index = fan_ch[ch];
+ data->fan_present[index] = true;
+ data->npcm7xx_fan[index].FanStFlag = FAN_INIT;
+ }
+
+ return 0;
+}
+
+static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np, *child;
+ struct npcm7xx_pwm_fan_data *data;
+ struct resource *res;
+ struct device *hwmon;
+ char name[20];
+ int ret, cnt;
+ u32 output_freq;
+ u32 i;
+
+ np = dev->of_node;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm_base");
+ if (res == NULL) {
+ pr_err("PWM of_address_to_resource fail ret %d\n", ret);
+ return -ENODEV;
+ }
+
+ data->pwm_base = devm_ioremap_resource(dev, res);
+ pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
+ (u32)data->pwm_base, res->start, resource_size(res));
+ if (!data->pwm_base) {
+ pr_err("pwm probe failed: can't read pwm base address\n");
+ return -ENOMEM;
+ }
+
+ data->pwm_clk = devm_clk_get(dev, "clk_apb3");
+ if (IS_ERR(data->pwm_clk)) {
+ pr_err(" pwm probe failed: can't read clk.\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fan_base");
+ if (ret) {
+ pr_err("fan of_address_to_resource fail ret %d\n", ret);
+ return -ENODEV;
+ }
+
+ data->fan_base = devm_ioremap_resource(dev, res);
+ pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
+ (u32)data->fan_base, res->start, resource_size(res));
+
+ if (!data->fan_base) {
+ pr_err("fan probe failed: can't read fan base address.\n");
+ return -ENOMEM;
+ }
+
+ data->fan_clk = devm_clk_get(dev, "clk_apb4");
+ if (IS_ERR(data->fan_clk)) {
+ pr_err(" FAN probe failed: can't read clk.\n");
+ return -ENODEV;
+ }
+
+ output_freq = npcm7xx_pwm_init(data);
+ npcm7xx_fan_init(data);
+
+ for (cnt = 0; cnt < NPCM7XX_PWM_MAX_MODULES ; cnt++)
+ mutex_init(&data->npcm7xx_pwm_lock[cnt]);
+
+ for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) {
+ spin_lock_init(&data->npcm7xx_fan_lock[i]);
+
+ data->fan_irq[i] = platform_get_irq(pdev, i);
+ if (!data->fan_irq[i]) {
+ pr_err("%s - failed to map irq %d\n", __func__, i);
+ ret = -EAGAIN;
+ goto err_irq;
+ }
+
+ sprintf(name, "NPCM7XX-FAN-MD%d", i);
+
+ if (request_irq(data->fan_irq[i], npcm7xx_fan_isr, 0, name,
+ (void *)data)) {
+ pr_err("NPCM7XX: register irq FAN%d failed\n", i);
+ ret = -EAGAIN;
+ goto err_irq;
+ }
+ }
+
+ for_each_child_of_node(np, child) {
+ ret = npcm7xx_en_pwm_fan(dev, child, data);
+ if (ret) {
+ pr_err("npcm7xx_en_pwm_fan failed ret %d\n", ret);
+ of_node_put(child);
+ goto err_irq;
+ }
+ }
+
+ hwmon = devm_hwmon_device_register_with_info(dev, "npcm7xx_pwm_fan",
+ data, &npcm7xx_chip_info,
+ NULL);
+ if (IS_ERR(hwmon)) {
+ pr_err("PWM Driver failed - devm_hwmon_device_register_with_groups failed\n");
+ ret = PTR_ERR(hwmon);
+ goto err_irq;
+ }
+
+ for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM; i++) {
+ if (data->fan_present[i] == true) {
+ /* fan timer initialization */
+ data->npcm7xx_fan_select = 0;
+ data->npcm7xx_fan_timer.expires = jiffies +
+ msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
+ timer_setup(&data->npcm7xx_fan_timer,
+ npcm7xx_fan_polling, 0);
+ add_timer(&data->npcm7xx_fan_timer);
+ break;
+ }
+ }
+
+ pr_info("NPCM7XX PWM-FAN Driver probed, output Freq %dHz[PWM], input Freq %dHz[FAN]\n",
+ output_freq, data->InputClkFreq);
+
+ return 0;
+
+err_irq:
+ for (; i > 0; i--)
+ free_irq(data->fan_irq[i-1], (void *)data);
+
+ return ret;
+}
+
+static const struct of_device_id of_pwm_fan_match_table[] = {
+ { .compatible = "nuvoton,npcm750-pwm-fan", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_pwm_fan_match_table);
+
+static struct platform_driver npcm7xx_pwm_fan_driver = {
+ .probe = npcm7xx_pwm_fan_probe,
+ .driver = {
+ .name = "npcm7xx_pwm_fan",
+ .of_match_table = of_pwm_fan_match_table,
+ },
+};
+
+module_platform_driver(npcm7xx_pwm_fan_driver);
+
+MODULE_DESCRIPTION("Nuvoton NPCM7XX PWM and Fan Tacho driver");
+MODULE_AUTHOR("Tomer Maimon <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
2.14.1


2018-06-19 14:34:34

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] hwmon: npcm750: add NPCM7xx PWM and Fan driver

Hi Tomer,

I love your patch! Perhaps something to improve:

[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.18-rc1 next-20180619]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Tomer-Maimon/hwmon-Add-NPCM7xx-PWM-and-Fan-driver-support/20180619-192033
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
config: ia64-allmodconfig (attached as .config)
compiler: ia64-linux-gcc (GCC) 8.1.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=8.1.0 make.cross ARCH=ia64

All warnings (new ones prefixed by >>):

In file included from include/linux/printk.h:332,
from include/linux/kernel.h:14,
from include/linux/list.h:9,
from include/linux/module.h:9,
from drivers/hwmon/npcm750-pwm-fan.c:4:
drivers/hwmon/npcm750-pwm-fan.c: In function 'npcm7xx_pwm_fan_probe':
>> drivers/hwmon/npcm750-pwm-fan.c:979:4: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
(u32)data->pwm_base, res->start, resource_size(res));
^
include/linux/dynamic_debug.h:128:10: note: in definition of macro 'dynamic_pr_debug'
##__VA_ARGS__); \
^~~~~~~~~~~
drivers/hwmon/npcm750-pwm-fan.c:978:2: note: in expansion of macro 'pr_debug'
pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~
In file included from include/linux/kernel.h:14,
from include/linux/list.h:9,
from include/linux/module.h:9,
from drivers/hwmon/npcm750-pwm-fan.c:4:
>> drivers/hwmon/npcm750-pwm-fan.c:978:11: warning: format '%X' expects argument of type 'unsigned int', but argument 4 has type 'resource_size_t' {aka 'long long unsigned int'} [-Wformat=]
pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/printk.h:288:21: note: in definition of macro 'pr_fmt'
#define pr_fmt(fmt) fmt
^~~
include/linux/printk.h:336:2: note: in expansion of macro 'dynamic_pr_debug'
dynamic_pr_debug(fmt, ##__VA_ARGS__)
^~~~~~~~~~~~~~~~
drivers/hwmon/npcm750-pwm-fan.c:978:2: note: in expansion of macro 'pr_debug'
pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~
drivers/hwmon/npcm750-pwm-fan.c:978:11: warning: format '%X' expects argument of type 'unsigned int', but argument 5 has type 'resource_size_t' {aka 'long long unsigned int'} [-Wformat=]
pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/printk.h:288:21: note: in definition of macro 'pr_fmt'
#define pr_fmt(fmt) fmt
^~~
include/linux/printk.h:336:2: note: in expansion of macro 'dynamic_pr_debug'
dynamic_pr_debug(fmt, ##__VA_ARGS__)
^~~~~~~~~~~~~~~~
drivers/hwmon/npcm750-pwm-fan.c:978:2: note: in expansion of macro 'pr_debug'
pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~
In file included from include/linux/printk.h:332,
from include/linux/kernel.h:14,
from include/linux/list.h:9,
from include/linux/module.h:9,
from drivers/hwmon/npcm750-pwm-fan.c:4:
drivers/hwmon/npcm750-pwm-fan.c:999:4: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
(u32)data->fan_base, res->start, resource_size(res));
^
include/linux/dynamic_debug.h:128:10: note: in definition of macro 'dynamic_pr_debug'
##__VA_ARGS__); \
^~~~~~~~~~~
drivers/hwmon/npcm750-pwm-fan.c:998:2: note: in expansion of macro 'pr_debug'
pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~
In file included from include/linux/kernel.h:14,
from include/linux/list.h:9,
from include/linux/module.h:9,
from drivers/hwmon/npcm750-pwm-fan.c:4:
drivers/hwmon/npcm750-pwm-fan.c:998:11: warning: format '%X' expects argument of type 'unsigned int', but argument 4 has type 'resource_size_t' {aka 'long long unsigned int'} [-Wformat=]
pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/printk.h:288:21: note: in definition of macro 'pr_fmt'
#define pr_fmt(fmt) fmt
^~~
include/linux/printk.h:336:2: note: in expansion of macro 'dynamic_pr_debug'
dynamic_pr_debug(fmt, ##__VA_ARGS__)
^~~~~~~~~~~~~~~~
drivers/hwmon/npcm750-pwm-fan.c:998:2: note: in expansion of macro 'pr_debug'
pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~
drivers/hwmon/npcm750-pwm-fan.c:998:11: warning: format '%X' expects argument of type 'unsigned int', but argument 5 has type 'resource_size_t' {aka 'long long unsigned int'} [-Wformat=]
pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/printk.h:288:21: note: in definition of macro 'pr_fmt'
#define pr_fmt(fmt) fmt
^~~
include/linux/printk.h:336:2: note: in expansion of macro 'dynamic_pr_debug'
dynamic_pr_debug(fmt, ##__VA_ARGS__)
^~~~~~~~~~~~~~~~
drivers/hwmon/npcm750-pwm-fan.c:998:2: note: in expansion of macro 'pr_debug'
pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
^~~~~~~~
>> include/linux/printk.h:304:2: warning: 'ret' may be used uninitialized in this function [-Wmaybe-uninitialized]
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
^~~~~~
drivers/hwmon/npcm750-pwm-fan.c:961:6: note: 'ret' was declared here
int ret, cnt;
^~~

vim +979 drivers/hwmon/npcm750-pwm-fan.c

952
953 static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
954 {
955 struct device *dev = &pdev->dev;
956 struct device_node *np, *child;
957 struct npcm7xx_pwm_fan_data *data;
958 struct resource *res;
959 struct device *hwmon;
960 char name[20];
961 int ret, cnt;
962 u32 output_freq;
963 u32 i;
964
965 np = dev->of_node;
966
967 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
968 if (!data)
969 return -ENOMEM;
970
971 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm_base");
972 if (res == NULL) {
973 pr_err("PWM of_address_to_resource fail ret %d\n", ret);
974 return -ENODEV;
975 }
976
977 data->pwm_base = devm_ioremap_resource(dev, res);
> 978 pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
> 979 (u32)data->pwm_base, res->start, resource_size(res));
980 if (!data->pwm_base) {
981 pr_err("pwm probe failed: can't read pwm base address\n");
982 return -ENOMEM;
983 }
984
985 data->pwm_clk = devm_clk_get(dev, "clk_apb3");
986 if (IS_ERR(data->pwm_clk)) {
987 pr_err(" pwm probe failed: can't read clk.\n");
988 return -ENODEV;
989 }
990
991 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fan_base");
992 if (ret) {
993 pr_err("fan of_address_to_resource fail ret %d\n", ret);
994 return -ENODEV;
995 }
996
997 data->fan_base = devm_ioremap_resource(dev, res);
998 pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
999 (u32)data->fan_base, res->start, resource_size(res));
1000
1001 if (!data->fan_base) {
1002 pr_err("fan probe failed: can't read fan base address.\n");
1003 return -ENOMEM;
1004 }
1005
1006 data->fan_clk = devm_clk_get(dev, "clk_apb4");
1007 if (IS_ERR(data->fan_clk)) {
1008 pr_err(" FAN probe failed: can't read clk.\n");
1009 return -ENODEV;
1010 }
1011
1012 output_freq = npcm7xx_pwm_init(data);
1013 npcm7xx_fan_init(data);
1014
1015 for (cnt = 0; cnt < NPCM7XX_PWM_MAX_MODULES ; cnt++)
1016 mutex_init(&data->npcm7xx_pwm_lock[cnt]);
1017
1018 for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) {
1019 spin_lock_init(&data->npcm7xx_fan_lock[i]);
1020
1021 data->fan_irq[i] = platform_get_irq(pdev, i);
1022 if (!data->fan_irq[i]) {
1023 pr_err("%s - failed to map irq %d\n", __func__, i);
1024 ret = -EAGAIN;
1025 goto err_irq;
1026 }
1027
1028 sprintf(name, "NPCM7XX-FAN-MD%d", i);
1029
1030 if (request_irq(data->fan_irq[i], npcm7xx_fan_isr, 0, name,
1031 (void *)data)) {
1032 pr_err("NPCM7XX: register irq FAN%d failed\n", i);
1033 ret = -EAGAIN;
1034 goto err_irq;
1035 }
1036 }
1037
1038 for_each_child_of_node(np, child) {
1039 ret = npcm7xx_en_pwm_fan(dev, child, data);
1040 if (ret) {
1041 pr_err("npcm7xx_en_pwm_fan failed ret %d\n", ret);
1042 of_node_put(child);
1043 goto err_irq;
1044 }
1045 }
1046
1047 hwmon = devm_hwmon_device_register_with_info(dev, "npcm7xx_pwm_fan",
1048 data, &npcm7xx_chip_info,
1049 NULL);
1050 if (IS_ERR(hwmon)) {
1051 pr_err("PWM Driver failed - devm_hwmon_device_register_with_groups failed\n");
1052 ret = PTR_ERR(hwmon);
1053 goto err_irq;
1054 }
1055
1056 for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM; i++) {
1057 if (data->fan_present[i] == true) {
1058 /* fan timer initialization */
1059 data->npcm7xx_fan_select = 0;
1060 data->npcm7xx_fan_timer.expires = jiffies +
1061 msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
1062 timer_setup(&data->npcm7xx_fan_timer,
1063 npcm7xx_fan_polling, 0);
1064 add_timer(&data->npcm7xx_fan_timer);
1065 break;
1066 }
1067 }
1068
1069 pr_info("NPCM7XX PWM-FAN Driver probed, output Freq %dHz[PWM], input Freq %dHz[FAN]\n",
1070 output_freq, data->InputClkFreq);
1071
1072 return 0;
1073
1074 err_irq:
1075 for (; i > 0; i--)
1076 free_irq(data->fan_irq[i-1], (void *)data);
1077
1078 return ret;
1079 }
1080

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (10.64 kB)
.config.gz (49.73 kB)
Download all attachments

2018-06-19 19:46:27

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] hwmon: npcm750: add NPCM7xx PWM and Fan driver

Hi Tomer,

I love your patch! Perhaps something to improve:

[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.18-rc1 next-20180619]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Tomer-Maimon/hwmon-Add-NPCM7xx-PWM-and-Fan-driver-support/20180619-192033
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
config: i386-allyesconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386

Note: it may well be a FALSE warning. FWIW you are at least aware of it now.
http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings

All warnings (new ones prefixed by >>):

drivers//hwmon/npcm750-pwm-fan.c: In function 'npcm7xx_pwm_fan_probe':
>> drivers//hwmon/npcm750-pwm-fan.c:973:3: warning: 'ret' may be used uninitialized in this function [-Wmaybe-uninitialized]
pr_err("PWM of_address_to_resource fail ret %d\n", ret);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

vim +/ret +973 drivers//hwmon/npcm750-pwm-fan.c

952
953 static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
954 {
955 struct device *dev = &pdev->dev;
956 struct device_node *np, *child;
957 struct npcm7xx_pwm_fan_data *data;
958 struct resource *res;
959 struct device *hwmon;
960 char name[20];
961 int ret, cnt;
962 u32 output_freq;
963 u32 i;
964
965 np = dev->of_node;
966
967 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
968 if (!data)
969 return -ENOMEM;
970
971 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm_base");
972 if (res == NULL) {
> 973 pr_err("PWM of_address_to_resource fail ret %d\n", ret);
974 return -ENODEV;
975 }
976
977 data->pwm_base = devm_ioremap_resource(dev, res);
978 pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
979 (u32)data->pwm_base, res->start, resource_size(res));
980 if (!data->pwm_base) {
981 pr_err("pwm probe failed: can't read pwm base address\n");
982 return -ENOMEM;
983 }
984
985 data->pwm_clk = devm_clk_get(dev, "clk_apb3");
986 if (IS_ERR(data->pwm_clk)) {
987 pr_err(" pwm probe failed: can't read clk.\n");
988 return -ENODEV;
989 }
990
991 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fan_base");
992 if (ret) {
993 pr_err("fan of_address_to_resource fail ret %d\n", ret);
994 return -ENODEV;
995 }
996
997 data->fan_base = devm_ioremap_resource(dev, res);
998 pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
999 (u32)data->fan_base, res->start, resource_size(res));
1000
1001 if (!data->fan_base) {
1002 pr_err("fan probe failed: can't read fan base address.\n");
1003 return -ENOMEM;
1004 }
1005
1006 data->fan_clk = devm_clk_get(dev, "clk_apb4");
1007 if (IS_ERR(data->fan_clk)) {
1008 pr_err(" FAN probe failed: can't read clk.\n");
1009 return -ENODEV;
1010 }
1011
1012 output_freq = npcm7xx_pwm_init(data);
1013 npcm7xx_fan_init(data);
1014
1015 for (cnt = 0; cnt < NPCM7XX_PWM_MAX_MODULES ; cnt++)
1016 mutex_init(&data->npcm7xx_pwm_lock[cnt]);
1017
1018 for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) {
1019 spin_lock_init(&data->npcm7xx_fan_lock[i]);
1020
1021 data->fan_irq[i] = platform_get_irq(pdev, i);
1022 if (!data->fan_irq[i]) {
1023 pr_err("%s - failed to map irq %d\n", __func__, i);
1024 ret = -EAGAIN;
1025 goto err_irq;
1026 }
1027
1028 sprintf(name, "NPCM7XX-FAN-MD%d", i);
1029
1030 if (request_irq(data->fan_irq[i], npcm7xx_fan_isr, 0, name,
1031 (void *)data)) {
1032 pr_err("NPCM7XX: register irq FAN%d failed\n", i);
1033 ret = -EAGAIN;
1034 goto err_irq;
1035 }
1036 }
1037
1038 for_each_child_of_node(np, child) {
1039 ret = npcm7xx_en_pwm_fan(dev, child, data);
1040 if (ret) {
1041 pr_err("npcm7xx_en_pwm_fan failed ret %d\n", ret);
1042 of_node_put(child);
1043 goto err_irq;
1044 }
1045 }
1046
1047 hwmon = devm_hwmon_device_register_with_info(dev, "npcm7xx_pwm_fan",
1048 data, &npcm7xx_chip_info,
1049 NULL);
1050 if (IS_ERR(hwmon)) {
1051 pr_err("PWM Driver failed - devm_hwmon_device_register_with_groups failed\n");
1052 ret = PTR_ERR(hwmon);
1053 goto err_irq;
1054 }
1055
1056 for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM; i++) {
1057 if (data->fan_present[i] == true) {
1058 /* fan timer initialization */
1059 data->npcm7xx_fan_select = 0;
1060 data->npcm7xx_fan_timer.expires = jiffies +
1061 msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
1062 timer_setup(&data->npcm7xx_fan_timer,
1063 npcm7xx_fan_polling, 0);
1064 add_timer(&data->npcm7xx_fan_timer);
1065 break;
1066 }
1067 }
1068
1069 pr_info("NPCM7XX PWM-FAN Driver probed, output Freq %dHz[PWM], input Freq %dHz[FAN]\n",
1070 output_freq, data->InputClkFreq);
1071
1072 return 0;
1073
1074 err_irq:
1075 for (; i > 0; i--)
1076 free_irq(data->fan_irq[i-1], (void *)data);
1077
1078 return ret;
1079 }
1080

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (5.62 kB)
.config.gz (61.99 kB)
Download all attachments

2018-06-20 16:51:14

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] hwmon: npcm750: add NPCM7xx PWM and Fan driver

On Tue, Jun 19, 2018 at 01:53:52PM +0300, Tomer Maimon wrote:
> Add Nuvoton BMC NPCM7xx Pulse Width Modulation (PWM) and Fan tacho driver.
>
> The Nuvoton BMC NPCM7xx has two identical PWM controller modules,
> each module has four PWM controller outputs and eight identical Fan
> controller modules, each module has two Fan controller inputs.
>
> The driver provides a sysfs entries through which the user can
> configure the duty-cycle value (ranging from 0 to 100 percent)

The ABI expects 0..255 for pwm values. That seems to be what the
chip uses as well, so the above statement is a bit misleading.

> and read the fan tacho rpm value.
>
> The driver support cooling device creation, that could be bound
> to a thermal zone for the thermal control.
>
Please fix all the problems reported by 0day. Also, please run
checkpatch.pl --strict and follow its guidance. I don't expect you
to fix everything (you won't have to sign up as maintainer, for example),
but statements such as "xxx == true" or "xxx == NULL" are not necessary,
and macro parameters should be provided in (). For example, "12 * ch"
would expand to "12 * 2 + 1" if ch is "2 + 1".

This is an incomplete review - too many problems. Please fix and resubmit
for another round.

> Signed-off-by: Tomer Maimon <[email protected]>
> ---
> drivers/hwmon/Kconfig | 7 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/npcm750-pwm-fan.c | 1099 +++++++++++++++++++++++++++++++++++++++

We would also expect Documentation/hwmon/npcm750-pwm-fan.

> 3 files changed, 1107 insertions(+)
> create mode 100644 drivers/hwmon/npcm750-pwm-fan.c
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index f10840ad465c..f6c2eff9bb7d 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1888,6 +1888,13 @@ config SENSORS_XGENE
> If you say yes here you get support for the temperature
> and power sensors for APM X-Gene SoC.
>
> +config SENSORS_NPCM7XX
> + tristate "Nuvoton NPCM7XX PWM and Fan driver"
> + depends on THERMAL || THERMAL=n
> + help
> + This driver provides support for Nuvoton NPCM7XX PWM and Fan
> + controllers.
> +
> if ACPI
>
> comment "ACPI drivers"
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index e7d52a36e6c4..004e2ec5b68f 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -174,6 +174,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
> obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
> obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
> obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
> +obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o
>
> obj-$(CONFIG_PMBUS) += pmbus/
>
> diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c
> new file mode 100644
> index 000000000000..82898197686f
> --- /dev/null
> +++ b/drivers/hwmon/npcm750-pwm-fan.c
> @@ -0,0 +1,1099 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2014-2018 Nuvoton Technology corporation.
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/sysfs.h>
> +#include <linux/spinlock.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/thermal.h>

Alphabetic order, please.

> +
> +/* NPCM7XX PWM registers */
> +#define NPCM7XX_PWM_REG_BASE(base, n) (base + ((n) * 0x1000L))
> +
> +#define NPCM7XX_PWM_REG_PR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x00)
> +#define NPCM7XX_PWM_REG_CSR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x04)
> +#define NPCM7XX_PWM_REG_CR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x08)
> +#define NPCM7XX_PWM_REG_CNRx(base, n, ch) \
> + (NPCM7XX_PWM_REG_BASE(base, n) + 0x0C + (12 * ch))
> +#define NPCM7XX_PWM_REG_CMRx(base, n, ch) \
> + (NPCM7XX_PWM_REG_BASE(base, n) + 0x10 + (12 * ch))
> +#define NPCM7XX_PWM_REG_PDRx(base, n, ch) \
> + (NPCM7XX_PWM_REG_BASE(base, n) + 0x14 + (12 * ch))
> +#define NPCM7XX_PWM_REG_PIER(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x3C)
> +#define NPCM7XX_PWM_REG_PIIR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x40)
> +#define NPCM7XX_FAN_REG_BASE(base, n) (base + ((n) * 0x1000L))
> +
> +#define NPCM7XX_PWM_CTRL_CH0_MODE_BIT BIT(3)
> +#define NPCM7XX_PWM_CTRL_CH1_MODE_BIT BIT(11)
> +#define NPCM7XX_PWM_CTRL_CH2_MODE_BIT BIT(15)
> +#define NPCM7XX_PWM_CTRL_CH3_MODE_BIT BIT(19)
> +
> +#define NPCM7XX_PWM_CTRL_CH0_INV_BIT BIT(2)
> +#define NPCM7XX_PWM_CTRL_CH1_INV_BIT BIT(10)
> +#define NPCM7XX_PWM_CTRL_CH2_INV_BIT BIT(14)
> +#define NPCM7XX_PWM_CTRL_CH3_INV_BIT BIT(18)
> +
> +#define NPCM7XX_PWM_CTRL_CH0_EN_BIT BIT(0)
> +#define NPCM7XX_PWM_CTRL_CH1_EN_BIT BIT(8)
> +#define NPCM7XX_PWM_CTRL_CH2_EN_BIT BIT(12)
> +#define NPCM7XX_PWM_CTRL_CH3_EN_BIT BIT(16)
> +
> +/* Define the maximum PWM channel number */
> +#define NPCM7XX_PWM_MAX_CHN_NUM 8
> +#define NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE 4
> +#define NPCM7XX_PWM_MAX_MODULES 2
> +
> +/* Define the Counter Register, value = 100 for match 100% */
> +#define NPCM7XX_PWM_COUNTER_DEFALUT_NUM 255
> +#define NPCM7XX_PWM_COMPARATOR_DEFALUT_NUM 127
> +
> +#define NPCM7XX_PWM_COMPARATOR_MAX 255
> +
> +/* default all PWM channels PRESCALE2 = 1 */
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH0 0x4
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH1 0x40
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH2 0x400
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH3 0x4000
> +
> +#define PWM_OUTPUT_FREQ_25KHZ 25000
> +#define PWN_CNT_DEFAULT 256
> +#define MIN_PRESCALE1 2
> +#define NPCM7XX_PWM_PRESCALE_SHIFT_CH01 8
> +
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT (NPCM7XX_PWM_PRESCALE2_DEFALUT_CH0 | \
> + NPCM7XX_PWM_PRESCALE2_DEFALUT_CH1 | \
> + NPCM7XX_PWM_PRESCALE2_DEFALUT_CH2 | \
> + NPCM7XX_PWM_PRESCALE2_DEFALUT_CH3)
> +
> +#define NPCM7XX_PWM_CTRL_MODE_DEFALUT (NPCM7XX_PWM_CTRL_CH0_MODE_BIT | \
> + NPCM7XX_PWM_CTRL_CH1_MODE_BIT | \
> + NPCM7XX_PWM_CTRL_CH2_MODE_BIT | \
> + NPCM7XX_PWM_CTRL_CH3_MODE_BIT)
> +
> +#define NPCM7XX_PWM_CTRL_EN_DEFALUT (NPCM7XX_PWM_CTRL_CH0_EN_BIT | \
> + NPCM7XX_PWM_CTRL_CH1_EN_BIT | \
> + NPCM7XX_PWM_CTRL_CH2_EN_BIT | \
> + NPCM7XX_PWM_CTRL_CH3_EN_BIT)
> +
> +/* NPCM7XX FAN Tacho registers */
> +#define NPCM7XX_FAN_REG_BASE(base, n) (base + ((n) * 0x1000L))
> +
> +#define NPCM7XX_FAN_REG_TCNT1(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x00)
> +#define NPCM7XX_FAN_REG_TCRA(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x02)
> +#define NPCM7XX_FAN_REG_TCRB(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x04)
> +#define NPCM7XX_FAN_REG_TCNT2(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x06)
> +#define NPCM7XX_FAN_REG_TPRSC(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x08)
> +#define NPCM7XX_FAN_REG_TCKC(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0A)
> +#define NPCM7XX_FAN_REG_TMCTRL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0C)
> +#define NPCM7XX_FAN_REG_TICTRL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0E)
> +#define NPCM7XX_FAN_REG_TICLR(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x10)
> +#define NPCM7XX_FAN_REG_TIEN(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x12)
> +#define NPCM7XX_FAN_REG_TCPA(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x14)
> +#define NPCM7XX_FAN_REG_TCPB(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x16)
> +#define NPCM7XX_FAN_REG_TCPCFG(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x18)
> +#define NPCM7XX_FAN_REG_TINASEL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x1A)
> +#define NPCM7XX_FAN_REG_TINBSEL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x1C)
> +
> +#define NPCM7XX_FAN_TCKC_CLKX_NONE 0
> +#define NPCM7XX_FAN_TCKC_CLK1_APB BIT(0)
> +#define NPCM7XX_FAN_TCKC_CLK2_APB BIT(3)
> +
> +#define NPCM7XX_FAN_TMCTRL_TBEN BIT(6)
> +#define NPCM7XX_FAN_TMCTRL_TAEN BIT(5)
> +#define NPCM7XX_FAN_TMCTRL_TBEDG BIT(4)
> +#define NPCM7XX_FAN_TMCTRL_TAEDG BIT(3)
> +#define NPCM7XX_FAN_TMCTRL_MODE_5 BIT(2)
> +
> +#define NPCM7XX_FAN_TICLR_CLEAR_ALL GENMASK(5, 0)
> +#define NPCM7XX_FAN_TICLR_TFCLR BIT(5)
> +#define NPCM7XX_FAN_TICLR_TECLR BIT(4)
> +#define NPCM7XX_FAN_TICLR_TDCLR BIT(3)
> +#define NPCM7XX_FAN_TICLR_TCCLR BIT(2)
> +#define NPCM7XX_FAN_TICLR_TBCLR BIT(1)
> +#define NPCM7XX_FAN_TICLR_TACLR BIT(0)
> +
> +#define NPCM7XX_FAN_TIEN_ENABLE_ALL GENMASK(5, 0)
> +#define NPCM7XX_FAN_TIEN_TFIEN BIT(5)
> +#define NPCM7XX_FAN_TIEN_TEIEN BIT(4)
> +#define NPCM7XX_FAN_TIEN_TDIEN BIT(3)
> +#define NPCM7XX_FAN_TIEN_TCIEN BIT(2)
> +#define NPCM7XX_FAN_TIEN_TBIEN BIT(1)
> +#define NPCM7XX_FAN_TIEN_TAIEN BIT(0)
> +
> +#define NPCM7XX_FAN_TICTRL_TFPND BIT(5)
> +#define NPCM7XX_FAN_TICTRL_TEPND BIT(4)
> +#define NPCM7XX_FAN_TICTRL_TDPND BIT(3)
> +#define NPCM7XX_FAN_TICTRL_TCPND BIT(2)
> +#define NPCM7XX_FAN_TICTRL_TBPND BIT(1)
> +#define NPCM7XX_FAN_TICTRL_TAPND BIT(0)
> +
> +#define NPCM7XX_FAN_TCPCFG_HIBEN BIT(7)
> +#define NPCM7XX_FAN_TCPCFG_EQBEN BIT(6)
> +#define NPCM7XX_FAN_TCPCFG_LOBEN BIT(5)
> +#define NPCM7XX_FAN_TCPCFG_CPBSEL BIT(4)
> +#define NPCM7XX_FAN_TCPCFG_HIAEN BIT(3)
> +#define NPCM7XX_FAN_TCPCFG_EQAEN BIT(2)
> +#define NPCM7XX_FAN_TCPCFG_LOAEN BIT(1)
> +#define NPCM7XX_FAN_TCPCFG_CPASEL BIT(0)
> +
> +/* FAN General Defintion */
> +/* Define the maximum FAN channel number */
> +#define NPCM7XX_FAN_MAX_MODULE 8
> +#define NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE 2
> +#define NPCM7XX_FAN_MAX_CHN_NUM 16
> +
> +/*
> + * Get Fan Tach Timeout (base on clock 214843.75Hz, 1 cnt = 4.654us)
> + * Timeout 94ms ~= 0x5000
> + * (The minimum FAN speed could to support ~640RPM/pulse 1,
> + * 320RPM/pulse 2, ...-- 10.6Hz)
> + */
> +#define NPCM7XX_FAN_TIMEOUT 0x5000
> +#define NPCM7XX_FAN_TCNT 0xFFFF
> +#define NPCM7XX_FAN_TCPA (NPCM7XX_FAN_TCNT - NPCM7XX_FAN_TIMEOUT)
> +#define NPCM7XX_FAN_TCPB (NPCM7XX_FAN_TCNT - NPCM7XX_FAN_TIMEOUT)
> +
> +#define NPCM7XX_FAN_POLL_TIMER_200MS 200
> +#define NPCM7XX_FAN_DEFAULT_PULSE_PER_REVOLUTION 2
> +#define NPCM7XX_FAN_TINASEL_FANIN_DEFAULT 0
> +#define NPCM7XX_FAN_CLK_PRESCALE 255
> +
> +#define NPCM7XX_FAN_CMPA 0
> +#define NPCM7XX_FAN_CMPB 1
> +
> +/* Obtain the fan number */
> +#define NPCM7XX_FAN_INPUT(fan, cmp) ((fan << 1) + (cmp))
> +
> +/* fan sample status */
> +#define FAN_DISABLE 0xFF
> +#define FAN_INIT 0x00
> +#define FAN_PREPARE_TO_GET_FIRST_CAPTURE 0x01
> +#define FAN_ENOUGH_SAMPLE 0x02
> +
> +struct Fan_Dev {
> + u8 FanStFlag;
> + u8 FanPlsPerRev;
> + u16 FanCnt;
> + u32 FanCntTemp;
> +};
> +
> +struct npcm7xx_cooling_device {
> + char name[THERMAL_NAME_LENGTH];
> + struct npcm7xx_pwm_fan_data *data;
> + struct thermal_cooling_device *tcdev;
> + int pwm_port;
> + u8 *cooling_levels;
> + u8 max_state;
> + u8 cur_state;
> +};
> +
> +struct npcm7xx_pwm_fan_data {
> + void __iomem *pwm_base, *fan_base;
> + unsigned long pwm_clk_freq, fan_clk_freq;
> + struct clk *pwm_clk, *fan_clk;
> + struct mutex npcm7xx_pwm_lock[NPCM7XX_PWM_MAX_MODULES];
> + spinlock_t npcm7xx_fan_lock[NPCM7XX_FAN_MAX_MODULE];
> + int fan_irq[NPCM7XX_FAN_MAX_MODULE];
> + bool pwm_present[NPCM7XX_PWM_MAX_CHN_NUM],
> + fan_present[NPCM7XX_FAN_MAX_CHN_NUM];

Separate declarations please.

> + u32 InputClkFreq;

No CamelCase.

> + struct timer_list npcm7xx_fan_timer;
> + struct Fan_Dev npcm7xx_fan[NPCM7XX_FAN_MAX_CHN_NUM];
> + struct npcm7xx_cooling_device *cdev[NPCM7XX_PWM_MAX_CHN_NUM];
> + u8 npcm7xx_fan_select;
> +};
> +
> +static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data,
> + int channel, u16 val)
> +{
> + u32 PWMCh = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
> + u32 module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
> + u32 u32TmpBuf = 0, ctrl_en_bit, env_bit;

The u32 in u32TmpBuf is quite unnecessary.

> +
> + /*
> + * Config PWM Comparator register for setting duty cycle
> + */
> + if (val < 0 || val > NPCM7XX_PWM_COMPARATOR_MAX)
> + return -EINVAL;

How can a u32 ever be < 0 ?

> +
> + mutex_lock(&data->npcm7xx_pwm_lock[module]);
> +
> + /* write new CMR value */
> + iowrite32(val, NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, PWMCh));
> + u32TmpBuf = ioread32(NPCM7XX_PWM_REG_CR(data->pwm_base, module));
> +
> + switch (PWMCh) {
> + case 0:
> + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH0_EN_BIT;
> + env_bit = NPCM7XX_PWM_CTRL_CH0_INV_BIT;
> + break;
> + case 1:
> + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH1_EN_BIT;
> + env_bit = NPCM7XX_PWM_CTRL_CH1_INV_BIT;
> + break;
> + case 2:
> + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH2_EN_BIT;
> + env_bit = NPCM7XX_PWM_CTRL_CH2_INV_BIT;
> + break;
> + case 3:
> + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH3_EN_BIT;
> + env_bit = NPCM7XX_PWM_CTRL_CH3_INV_BIT;
> + break;
> + default:
> + return -ENODEV;
> + }
> +
> + if (val == 0) {
> + /* Disable PWM */
> + u32TmpBuf &= ~(ctrl_en_bit);

Unnecessary ( )

> + u32TmpBuf |= env_bit;
> + } else {
> + /* Enable PWM */
> + u32TmpBuf |= ctrl_en_bit;
> + u32TmpBuf &= ~(env_bit);

Unnecessary ( )

> + }
> +
> + iowrite32(u32TmpBuf, NPCM7XX_PWM_REG_CR(data->pwm_base, module));
> + mutex_unlock(&data->npcm7xx_pwm_lock[module]);
> +
> + return 0;
> +}
> +
> +static inline void npcm7xx_fan_start_capture(struct npcm7xx_pwm_fan_data *data,
> + u8 fan, u8 cmp)
> +{
> + u8 fan_id = 0;
> + u8 reg_mode = 0;
> + u8 reg_int = 0;
> + unsigned long flags;
> +
> + fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
> +
> + /* to check whether any fan tach is enable */
> + if (data->npcm7xx_fan[fan_id].FanStFlag != FAN_DISABLE) {
> + /* reset status */
> + spin_lock_irqsave(&data->npcm7xx_fan_lock[fan], flags);
> +
> + data->npcm7xx_fan[fan_id].FanStFlag = FAN_INIT;
> + reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + if (cmp == NPCM7XX_FAN_CMPA) {
> + /* enable interrupt */
> + iowrite8((u8) (reg_int | (NPCM7XX_FAN_TIEN_TAIEN |
> + NPCM7XX_FAN_TIEN_TEIEN)),

Is the (u8) typecast really necessary ? Seems unlikely.

> + NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + reg_mode = NPCM7XX_FAN_TCKC_CLK1_APB
> + | ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base,
> + fan));
> +
> + /* start to Capture */
> + iowrite8(reg_mode, NPCM7XX_FAN_REG_TCKC(data->fan_base,
> + fan));
> + } else {
> + /* enable interrupt */
> + iowrite8((u8) (reg_int | (NPCM7XX_FAN_TIEN_TBIEN |
> + NPCM7XX_FAN_TIEN_TFIEN)),

Same as above.

> + NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + reg_mode =
> + NPCM7XX_FAN_TCKC_CLK2_APB
> + | ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base,
> + fan));
> +
> + /* start to Capture */
> + iowrite8(reg_mode,
> + NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
> + }
> +
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[fan], flags);
> + }
> +}
> +
> +/*
> + * Enable a background timer to poll fan tach value, (200ms * 4)
> + * to polling all fan
> + */
> +static void npcm7xx_fan_polling(struct timer_list *t)
> +{
> + struct npcm7xx_pwm_fan_data *data;
> + unsigned long flags;
> + int i;
> +
> + data = from_timer(data, t, npcm7xx_fan_timer);
> +
> + /*
> + * Polling two module per one round,
> + * FAN01 & FAN89 / FAN23 & FAN1011 / FAN45 & FAN1213 / FAN67 & FAN1415
> + */
> + for (i = data->npcm7xx_fan_select; i < NPCM7XX_FAN_MAX_MODULE;
> + i = i+4) {

i + 4

> + /* clear the flag and reset the counter (TCNT) */
> + spin_lock_irqsave(&data->npcm7xx_fan_lock[i], flags);
> + iowrite8((u8) NPCM7XX_FAN_TICLR_CLEAR_ALL,
> + NPCM7XX_FAN_REG_TICLR(data->fan_base, i));
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[i], flags);
> +
> + if (data->fan_present[i*2] == true) {
> + spin_lock_irqsave(&data->npcm7xx_fan_lock[i], flags);
> + iowrite16(NPCM7XX_FAN_TCNT,
> + NPCM7XX_FAN_REG_TCNT1(data->fan_base, i));
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[i],
> + flags);
> + npcm7xx_fan_start_capture(data, i, NPCM7XX_FAN_CMPA);
> + }
> + if (data->fan_present[(i*2)+1] == true) {

(i * 2) + 1

same everywhere - add spaces

> + spin_lock_irqsave(&data->npcm7xx_fan_lock[i], flags);
> + iowrite16(NPCM7XX_FAN_TCNT,
> + NPCM7XX_FAN_REG_TCNT2(data->fan_base, i));
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[i],
> + flags);
> + npcm7xx_fan_start_capture(data, i, NPCM7XX_FAN_CMPB);
> + }
> + }
> +
> + data->npcm7xx_fan_select++;
> + data->npcm7xx_fan_select &= 0x3;
> +
> + /* reset the timer interval */
> + data->npcm7xx_fan_timer.expires = jiffies +
> + msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
> + add_timer(&data->npcm7xx_fan_timer);
> +}
> +
> +static inline void npcm7xx_fan_compute(struct npcm7xx_pwm_fan_data *data,
> + u8 fan, u8 cmp, u8 fan_id,
> + u8 flag_int, u8 flag_mode,
> + u8 flag_clear)
> +{
> + u8 reg_int = 0;
> + u8 reg_mode = 0;
> + u16 fan_cap = 0;
> +
> + if (cmp == NPCM7XX_FAN_CMPA)
> + fan_cap = ioread16(NPCM7XX_FAN_REG_TCRA(data->fan_base, fan));
> + else
> + fan_cap = ioread16(NPCM7XX_FAN_REG_TCRB(data->fan_base, fan));
> +
> + /* clear capature flag, H/W will auto reset the NPCM7XX_FAN_TCNTx */
> + iowrite8((u8) flag_clear, NPCM7XX_FAN_REG_TICLR(data->fan_base, fan));
> +
Now this typecast is definitely unnecessary. Please check the entire code
and drop all unnecessary typecasts.

> + if (data->npcm7xx_fan[fan_id].FanStFlag == FAN_INIT) {
> + /* First capture, drop it */
> + data->npcm7xx_fan[fan_id].FanStFlag =
> + FAN_PREPARE_TO_GET_FIRST_CAPTURE;
> +
> + /* reset counter */
> + data->npcm7xx_fan[fan_id].FanCntTemp = 0;
> + } else if (data->npcm7xx_fan[fan_id].FanStFlag <
> + FAN_ENOUGH_SAMPLE) {
> + /*
> + * collect the enough sample,
> + * (ex: 2 pulse fan need to get 2 sample)
> + */
> + data->npcm7xx_fan[fan_id].FanCntTemp +=
> + (NPCM7XX_FAN_TCNT - fan_cap);
> +
> + data->npcm7xx_fan[fan_id].FanStFlag++;
> + } else {
> + /* get enough sample or fan disable */
> + if (data->npcm7xx_fan[fan_id].FanStFlag == FAN_ENOUGH_SAMPLE) {
> + data->npcm7xx_fan[fan_id].FanCntTemp +=
> + (NPCM7XX_FAN_TCNT - fan_cap);
> +
> + /* compute finial average cnt per pulse */
> + data->npcm7xx_fan[fan_id].FanCnt
> + = data->npcm7xx_fan[fan_id].FanCntTemp /
> + FAN_ENOUGH_SAMPLE;
> +
> + data->npcm7xx_fan[fan_id].FanStFlag = FAN_INIT;
> + }
> +
> + reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + /* disable interrupt */
> + iowrite8((u8) (reg_int & ~flag_int),
> + NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> + reg_mode = ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
> +
> + /* stop capturing */
> + iowrite8((u8) (reg_mode & ~flag_mode),
> + NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
> +
> + }
> +}
> +
> +static inline void npcm7xx_check_cmp(struct npcm7xx_pwm_fan_data *data,
> + u8 fan, u8 cmp, u8 flag)
> +{
> + u8 reg_int = 0;
> + u8 reg_mode = 0;
> + u8 flag_timeout;
> + u8 flag_cap;
> + u8 flag_clear;
> + u8 flag_int;
> + u8 flag_mode;
> + u8 fan_id;
> +
> + fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
> +
> + if (cmp == NPCM7XX_FAN_CMPA) {
> + flag_cap = NPCM7XX_FAN_TICTRL_TAPND;
> + flag_timeout = NPCM7XX_FAN_TICTRL_TEPND;
> + flag_int = NPCM7XX_FAN_TIEN_TAIEN | NPCM7XX_FAN_TIEN_TEIEN;
> + flag_mode = NPCM7XX_FAN_TCKC_CLK1_APB;
> + flag_clear = NPCM7XX_FAN_TICLR_TACLR | NPCM7XX_FAN_TICLR_TECLR;
> + } else {
> + flag_cap = NPCM7XX_FAN_TICTRL_TBPND;
> + flag_timeout = NPCM7XX_FAN_TICTRL_TFPND;
> + flag_int = NPCM7XX_FAN_TIEN_TBIEN | NPCM7XX_FAN_TIEN_TFIEN;
> + flag_mode = NPCM7XX_FAN_TCKC_CLK2_APB;
> + flag_clear = NPCM7XX_FAN_TICLR_TBCLR | NPCM7XX_FAN_TICLR_TFCLR;
> + }
> +
> +
> + if (flag & flag_timeout) {
> +
> + reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + /* disable interrupt */
> + iowrite8((u8) (reg_int & ~flag_int),
> + NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + /* clear interrup flag */
> + iowrite8((u8) flag_clear, NPCM7XX_FAN_REG_TICLR(data->fan_base,
> + fan));
> +
> + reg_mode = ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
> +
> + /* stop capturing */
> + iowrite8((u8) (reg_mode & ~flag_mode),
> + NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
> +
> + /*
> + * If timeout occurs (NPCM7XX_FAN_TIMEOUT), the fan doesn't
> + * connect or speed is lower than 10.6Hz (320RPM/pulse2).
> + * In these situation, the RPM output should be zero.
> + */
> + data->npcm7xx_fan[fan_id].FanCnt = 0;
> + //DEBUG_MSG("%s : it is timeout fan_id %d\n", __func__, fan_id);
> + } else {
> + /* input capture is occurred */
> + if (flag & flag_cap)
> + npcm7xx_fan_compute(data, fan, cmp, fan_id, flag_int,
> + flag_mode, flag_clear);
> + }
> +}
> +
> +static irqreturn_t npcm7xx_fan_isr(int irq, void *dev_id)
> +{
> + struct npcm7xx_pwm_fan_data *data = dev_id;
> + u8 flag = 0;
> + int module;
> + unsigned long flags;
> +
> + module = irq - data->fan_irq[0];
> + spin_lock_irqsave(&data->npcm7xx_fan_lock[module], flags);
> +
> + flag = ioread8(NPCM7XX_FAN_REG_TICTRL(data->fan_base, module));
> + if (flag > 0) {
> + npcm7xx_check_cmp(data, module, NPCM7XX_FAN_CMPA, flag);
> + npcm7xx_check_cmp(data, module, NPCM7XX_FAN_CMPB, flag);
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[module], flags);
> + return IRQ_HANDLED;
> + }
> +
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[module], flags);
> +
> + return IRQ_NONE;
> +}
> +
> +static int npcm7xx_read_pwm(struct device *dev, u32 attr, int channel,
> + long *val)
> +{
> + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
> + u32 PWMCh = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
> + u32 module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);

When would this ever be an ERR_PTR ?

> +
> + switch (attr) {
> + case hwmon_pwm_input:
> + mutex_lock(&data->npcm7xx_pwm_lock[module]);
> + *val = (long)ioread32(

Another unnecessary typecast.

> + NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, PWMCh));
> + mutex_unlock(&data->npcm7xx_pwm_lock[module]);

What is this lock for ?

> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int npcm7xx_write_pwm(struct device *dev, u32 attr, int channel,
> + long val)
> +{
> + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
> + int err = 0;
> +
> + switch (attr) {
> + case hwmon_pwm_input:
> + err = npcm7xx_pwm_config_set(data, channel, (u16)val);

If val == 0x10000 this is curring off the upper bit(s), and converts
negative values into large positive ones.
The validation in npcm7xx_pwm_config_set() is thus quite pointless.

> + break;
> + default:
> + err = -EOPNOTSUPP;
> + break;
> + }
> +
> + return err;
> +}
> +
> +static umode_t npcm7xx_pwm_is_visible(const void *_data, u32 attr, int channel)
> +{
> + const struct npcm7xx_pwm_fan_data *data = _data;
> +
> + if (!data->pwm_present[channel])
> + return 0;
> +
> + switch (attr) {
> + case hwmon_pwm_input:
> + return 0644;
> + default:
> + return 0;
> + }
> +}
> +
> +static int npcm7xx_read_fan(struct device *dev, u32 attr, int channel,
> + long *val)
> +{
> + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
> +
> + switch (attr) {
> + case hwmon_fan_input:
> + *val = 0;
> + if (data->npcm7xx_fan[channel].FanCnt <= 0)
> + return data->npcm7xx_fan[channel].FanCnt;
> +
> + /* Convert the raw reading to RPM */
> + if ((data->npcm7xx_fan[channel].FanCnt > 0) &&
> + data->npcm7xx_fan[channel].FanPlsPerRev > 0)
> + *val = (long)((data->InputClkFreq * 60)/
> + (data->npcm7xx_fan[channel].FanCnt *
> + data->npcm7xx_fan[channel].FanPlsPerRev));
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static umode_t npcm7xx_fan_is_visible(const void *_data, u32 attr, int channel)
> +{
> + const struct npcm7xx_pwm_fan_data *data = _data;
> +
> + if (!data->fan_present[channel])
> + return 0;
> +
> + switch (attr) {
> + case hwmon_fan_input:
> + return 0444;
> + default:
> + return 0;
> + }
> +}
> +
> +static int npcm7xx_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *val)
> +{
> + switch (type) {
> + case hwmon_pwm:
> + return npcm7xx_read_pwm(dev, attr, channel, val);
> + case hwmon_fan:
> + return npcm7xx_read_fan(dev, attr, channel, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int npcm7xx_write(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long val)
> +{
> + switch (type) {
> + case hwmon_pwm:
> + return npcm7xx_write_pwm(dev, attr, channel, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static umode_t npcm7xx_is_visible(const void *data,
> + enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + switch (type) {
> + case hwmon_pwm:
> + return npcm7xx_pwm_is_visible(data, attr, channel);
> + case hwmon_fan:
> + return npcm7xx_fan_is_visible(data, attr, channel);
> + default:
> + return 0;
> + }
> +}
> +
> +static const u32 npcm7xx_pwm_config[] = {
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + 0
> +};
> +
> +static const struct hwmon_channel_info npcm7xx_pwm = {
> + .type = hwmon_pwm,
> + .config = npcm7xx_pwm_config,
> +};
> +
> +static const u32 npcm7xx_fan_config[] = {
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + 0
> +};
> +
> +static const struct hwmon_channel_info npcm7xx_fan = {
> + .type = hwmon_fan,
> + .config = npcm7xx_fan_config,
> +};
> +
> +static const struct hwmon_channel_info *npcm7xx_info[] = {
> + &npcm7xx_pwm,
> + &npcm7xx_fan,
> + NULL
> +};
> +
> +static const struct hwmon_ops npcm7xx_hwmon_ops = {
> + .is_visible = npcm7xx_is_visible,
> + .read = npcm7xx_read,
> + .write = npcm7xx_write,
> +};
> +
> +static const struct hwmon_chip_info npcm7xx_chip_info = {
> + .ops = &npcm7xx_hwmon_ops,
> + .info = npcm7xx_info,
> +};
> +
> +static u32 npcm7xx_pwm_init(struct npcm7xx_pwm_fan_data *data)
> +{
> + int m, ch;
> + u32 Prescale_val, output_freq;
> +
> + data->pwm_clk_freq = clk_get_rate(data->pwm_clk);
> +
> + /* Adjust NPCM7xx PWMs output frequency to ~25Khz */
> + output_freq = data->pwm_clk_freq / PWN_CNT_DEFAULT;
> + Prescale_val = DIV_ROUND_CLOSEST(output_freq, PWM_OUTPUT_FREQ_25KHZ);
> +
> + /* If Prescale_val = 0, then the prescale output clock is stopped */
> + if (Prescale_val < MIN_PRESCALE1)
> + Prescale_val = MIN_PRESCALE1;
> + /*
> + * Prescale_val need to decrement in one because in the PWM Prescale
> + * register the Prescale value increment by one
> + */
> + Prescale_val--;
> +
> + /* Setting PWM Prescale Register value register to both modules */
> + Prescale_val |= (Prescale_val << NPCM7XX_PWM_PRESCALE_SHIFT_CH01);
> +
> + for (m = 0; m < NPCM7XX_PWM_MAX_MODULES ; m++) {
> + iowrite32(Prescale_val, NPCM7XX_PWM_REG_PR(data->pwm_base, m));
> + iowrite32(NPCM7XX_PWM_PRESCALE2_DEFALUT,
> + NPCM7XX_PWM_REG_CSR(data->pwm_base, m));
> + iowrite32(NPCM7XX_PWM_CTRL_MODE_DEFALUT,
> + NPCM7XX_PWM_REG_CR(data->pwm_base, m));
> +
> + for (ch = 0; ch < NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE; ch++) {
> + iowrite32(NPCM7XX_PWM_COUNTER_DEFALUT_NUM,
> + NPCM7XX_PWM_REG_CNRx(data->pwm_base, m, ch));
> + }
> + }
> +
> + return output_freq / ((Prescale_val & 0xf) + 1);
> +}
> +
> +static void npcm7xx_fan_init(struct npcm7xx_pwm_fan_data *data)
> +{
> + int md;
> + int ch;
> + int i;
> + u32 apb_clk_freq;
> +
> + for (md = 0; md < NPCM7XX_FAN_MAX_MODULE; md++) {
> +
> + /* stop FAN0~7 clock */
> + iowrite8((u8)NPCM7XX_FAN_TCKC_CLKX_NONE,
> + NPCM7XX_FAN_REG_TCKC(data->fan_base, md));
> +
> + /* disable all interrupt */
> + iowrite8((u8) 0x00, NPCM7XX_FAN_REG_TIEN(data->fan_base, md));
> +
> + /* clear all interrupt */
> + iowrite8((u8) NPCM7XX_FAN_TICLR_CLEAR_ALL,
> + NPCM7XX_FAN_REG_TICLR(data->fan_base, md));
> +
> + /* set FAN0~7 clock prescaler */
> + iowrite8((u8) NPCM7XX_FAN_CLK_PRESCALE,
> + NPCM7XX_FAN_REG_TPRSC(data->fan_base, md));
> +
> + /* set FAN0~7 mode (high-to-low transition) */
> + iowrite8(
> + (u8) (
> + NPCM7XX_FAN_TMCTRL_MODE_5 |
> + NPCM7XX_FAN_TMCTRL_TBEN |
> + NPCM7XX_FAN_TMCTRL_TAEN
> + ),
> + NPCM7XX_FAN_REG_TMCTRL(data->fan_base, md)
> + );
> +
> + /* set FAN0~7 Initial Count/Cap */
> + iowrite16(NPCM7XX_FAN_TCNT,
> + NPCM7XX_FAN_REG_TCNT1(data->fan_base, md));
> + iowrite16(NPCM7XX_FAN_TCNT,
> + NPCM7XX_FAN_REG_TCNT2(data->fan_base, md));
> +
> + /* set FAN0~7 compare (equal to count) */
> + iowrite8((u8)(NPCM7XX_FAN_TCPCFG_EQAEN |
> + NPCM7XX_FAN_TCPCFG_EQBEN),
> + NPCM7XX_FAN_REG_TCPCFG(data->fan_base, md));
> +
> + /* set FAN0~7 compare value */
> + iowrite16(NPCM7XX_FAN_TCPA,
> + NPCM7XX_FAN_REG_TCPA(data->fan_base, md));
> + iowrite16(NPCM7XX_FAN_TCPB,
> + NPCM7XX_FAN_REG_TCPB(data->fan_base, md));
> +
> + /* set FAN0~7 fan input FANIN 0~15 */
> + iowrite8((u8) NPCM7XX_FAN_TINASEL_FANIN_DEFAULT,
> + NPCM7XX_FAN_REG_TINASEL(data->fan_base, md));
> + iowrite8((u8) NPCM7XX_FAN_TINASEL_FANIN_DEFAULT,
> + NPCM7XX_FAN_REG_TINBSEL(data->fan_base, md));
> +
> + for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE; i++) {
> + ch = md * NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE + i;
> + data->npcm7xx_fan[ch].FanStFlag = FAN_DISABLE;
> + data->npcm7xx_fan[ch].FanPlsPerRev =
> + NPCM7XX_FAN_DEFAULT_PULSE_PER_REVOLUTION;
> + data->npcm7xx_fan[ch].FanCnt = 0;
> + }
> + }
> +
> + apb_clk_freq = clk_get_rate(data->fan_clk);
> +
> + /* Fan tach input clock = APB clock / prescalar, default is 255. */
> + data->InputClkFreq = apb_clk_freq / (NPCM7XX_FAN_CLK_PRESCALE + 1);
> +}
> +
> +static int
> +npcm7xx_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev,
> + unsigned long *state)
> +{
> + struct npcm7xx_cooling_device *cdev = tcdev->devdata;
> +
> + *state = cdev->max_state;
> +
> + return 0;
> +}
> +
> +static int
> +npcm7xx_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev,
> + unsigned long *state)
> +{
> + struct npcm7xx_cooling_device *cdev = tcdev->devdata;
> +
> + *state = cdev->cur_state;
> +
> + return 0;
> +}
> +
> +static int
> +npcm7xx_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev,
> + unsigned long state)
> +{
> + struct npcm7xx_cooling_device *cdev = tcdev->devdata;
> + int ret;
> +
> + if (state > cdev->max_state)
> + return -EINVAL;
> +
> + cdev->cur_state = state;
> + ret = npcm7xx_pwm_config_set(cdev->data, cdev->pwm_port,
> + cdev->cooling_levels[cdev->cur_state]);
> +
> + return ret;
> +}
> +
> +static const struct thermal_cooling_device_ops npcm7xx_pwm_cool_ops = {
> + .get_max_state = npcm7xx_pwm_cz_get_max_state,
> + .get_cur_state = npcm7xx_pwm_cz_get_cur_state,
> + .set_cur_state = npcm7xx_pwm_cz_set_cur_state,
> +};
> +
> +static int npcm7xx_create_pwm_cooling(struct device *dev,
> + struct device_node *child,
> + struct npcm7xx_pwm_fan_data *data,
> + u32 pwm_port, u8 num_levels)
> +{
> + int ret;
> + struct npcm7xx_cooling_device *cdev;
> +
> + cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
> + if (!cdev)
> + return -ENOMEM;
> +
> + cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL);
> + if (!cdev->cooling_levels)
> + return -ENOMEM;
> +
> + cdev->max_state = num_levels - 1;
> + ret = of_property_read_u8_array(child, "cooling-levels",
> + cdev->cooling_levels,
> + num_levels);
> + if (ret) {
> + dev_err(dev, "Property 'cooling-levels' cannot be read.\n");
> + return ret;
> + }
> + snprintf(cdev->name, THERMAL_NAME_LENGTH, "%s%d", child->name,
> + pwm_port);
> +
> + cdev->tcdev = thermal_of_cooling_device_register(child,
> + cdev->name,
> + cdev,
> + &npcm7xx_pwm_cool_ops);
> + if (IS_ERR(cdev->tcdev))
> + return PTR_ERR(cdev->tcdev);
> +
> + cdev->data = data;
> + cdev->pwm_port = pwm_port;
> +
> + data->cdev[pwm_port] = cdev;
> +
> + return 0;
> +}
> +
> +static int npcm7xx_en_pwm_fan(struct device *dev,
> + struct device_node *child,
> + struct npcm7xx_pwm_fan_data *data)
> +{
> + u8 *fan_ch;
> + u8 pwm_port;
> + int ret, fan_cnt;
> + u8 index, ch;
> +
> + ret = of_property_read_u8(child, "pwm-ch", &pwm_port);
> + if (ret)
> + return ret;
> +
> + data->pwm_present[pwm_port] = true;
> + ret = npcm7xx_pwm_config_set(data, pwm_port,
> + NPCM7XX_PWM_COMPARATOR_DEFALUT_NUM);
> +
> + ret = of_property_count_u8_elems(child, "cooling-levels");
> + if (ret > 0) {
> + ret = npcm7xx_create_pwm_cooling(dev, child, data, pwm_port,
> + ret);
> + if (ret)
> + return ret;
> + }
> +
> + fan_cnt = of_property_count_u8_elems(child, "fan-ch");
> + if (fan_cnt < 1)
> + return -EINVAL;
> +
> + fan_ch = devm_kzalloc(dev, sizeof(*fan_ch) * fan_cnt, GFP_KERNEL);
> + if (!fan_ch)
> + return -ENOMEM;
> +
> + ret = of_property_read_u8_array(child, "fan-ch", fan_ch, fan_cnt);
> + if (ret)
> + return ret;
> +
> + for (ch = 0; ch < fan_cnt; ch++) {
> + index = fan_ch[ch];
> + data->fan_present[index] = true;
> + data->npcm7xx_fan[index].FanStFlag = FAN_INIT;
> + }
> +
> + return 0;
> +}
> +
> +static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np, *child;
> + struct npcm7xx_pwm_fan_data *data;
> + struct resource *res;
> + struct device *hwmon;
> + char name[20];
> + int ret, cnt;
> + u32 output_freq;
> + u32 i;
> +
> + np = dev->of_node;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm_base");
> + if (res == NULL) {
> + pr_err("PWM of_address_to_resource fail ret %d\n", ret);

Everywhere: pr_XXX -> dev_XXX.

ret is a random number here.

> + return -ENODEV;
> + }

Unnecessary error message. devm_ioremap_resource() checks for that and displays
an error message.

> +
> + data->pwm_base = devm_ioremap_resource(dev, res);
> + pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
> + (u32)data->pwm_base, res->start, resource_size(res));

No need to reinvent %pR.

> + if (!data->pwm_base) {
> + pr_err("pwm probe failed: can't read pwm base address\n");

devm_ioremap_resource() returns an ERR_PTR. The error message is wrong anyway.
This can be EINVAL, EBUSY, or ENOMEM.

> + return -ENOMEM;

... and the reported error should be returned to the caller.

> + }
> +
> + data->pwm_clk = devm_clk_get(dev, "clk_apb3");
> + if (IS_ERR(data->pwm_clk)) {
> + pr_err(" pwm probe failed: can't read clk.\n");
> + return -ENODEV;

return PTR_ERR(data->pwm_clk);
> + }
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fan_base");
> + if (ret) {

ret is a random number. Have you tested this code ?

> + pr_err("fan of_address_to_resource fail ret %d\n", ret);

Another random number.

> + return -ENODEV;
> + }
> +
> + data->fan_base = devm_ioremap_resource(dev, res);
> + pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
> + (u32)data->fan_base, res->start, resource_size(res));

%pR

> +
> + if (!data->fan_base) {

devm_ioremap_resource() returns PTR_ERR()

> + pr_err("fan probe failed: can't read fan base address.\n");
> + return -ENOMEM;
> + }
> +
> + data->fan_clk = devm_clk_get(dev, "clk_apb4");
> + if (IS_ERR(data->fan_clk)) {
> + pr_err(" FAN probe failed: can't read clk.\n");
> + return -ENODEV;
PTR_ERR()
> + }
> +
> + output_freq = npcm7xx_pwm_init(data);
> + npcm7xx_fan_init(data);
> +
> + for (cnt = 0; cnt < NPCM7XX_PWM_MAX_MODULES ; cnt++)
> + mutex_init(&data->npcm7xx_pwm_lock[cnt]);
> +
> + for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) {
> + spin_lock_init(&data->npcm7xx_fan_lock[i]);
> +
> + data->fan_irq[i] = platform_get_irq(pdev, i);
> + if (!data->fan_irq[i]) {
> + pr_err("%s - failed to map irq %d\n", __func__, i);
> + ret = -EAGAIN;
> + goto err_irq;
> + }
> +
> + sprintf(name, "NPCM7XX-FAN-MD%d", i);
> +
> + if (request_irq(data->fan_irq[i], npcm7xx_fan_isr, 0, name,

No remove function. Either use devm_request_irq() or declare one.

> + (void *)data)) {
> + pr_err("NPCM7XX: register irq FAN%d failed\n", i);
> + ret = -EAGAIN;

This is supposed to work next time ? Why ?

> + goto err_irq;
> + }
> + }
> +
> + for_each_child_of_node(np, child) {
> + ret = npcm7xx_en_pwm_fan(dev, child, data);
> + if (ret) {
> + pr_err("npcm7xx_en_pwm_fan failed ret %d\n", ret);
> + of_node_put(child);
> + goto err_irq;
> + }
> + }
> +
> + hwmon = devm_hwmon_device_register_with_info(dev, "npcm7xx_pwm_fan",
> + data, &npcm7xx_chip_info,
> + NULL);
> + if (IS_ERR(hwmon)) {
> + pr_err("PWM Driver failed - devm_hwmon_device_register_with_groups failed\n");
> + ret = PTR_ERR(hwmon);
> + goto err_irq;
> + }
> +
> + for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM; i++) {
> + if (data->fan_present[i] == true) {
> + /* fan timer initialization */
> + data->npcm7xx_fan_select = 0;
> + data->npcm7xx_fan_timer.expires = jiffies +
> + msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
> + timer_setup(&data->npcm7xx_fan_timer,
> + npcm7xx_fan_polling, 0);
> + add_timer(&data->npcm7xx_fan_timer);
> + break;
> + }
> + }
> +
> + pr_info("NPCM7XX PWM-FAN Driver probed, output Freq %dHz[PWM], input Freq %dHz[FAN]\n",
> + output_freq, data->InputClkFreq);
> +
> + return 0;
> +
> +err_irq:
> + for (; i > 0; i--)
> + free_irq(data->fan_irq[i-1], (void *)data);
> +
> + return ret;
> +}
> +
> +static const struct of_device_id of_pwm_fan_match_table[] = {
> + { .compatible = "nuvoton,npcm750-pwm-fan", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, of_pwm_fan_match_table);
> +
> +static struct platform_driver npcm7xx_pwm_fan_driver = {
> + .probe = npcm7xx_pwm_fan_probe,
> + .driver = {
> + .name = "npcm7xx_pwm_fan",
> + .of_match_table = of_pwm_fan_match_table,
> + },
> +};
> +
> +module_platform_driver(npcm7xx_pwm_fan_driver);
> +
> +MODULE_DESCRIPTION("Nuvoton NPCM7XX PWM and Fan Tacho driver");
> +MODULE_AUTHOR("Tomer Maimon <[email protected]>");
> +MODULE_LICENSE("GPL v2");
> --
> 2.14.1
>

2018-06-20 18:26:05

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] hwmon: npcm750: add NPCM7xx PWM and Fan driver

(adding Julia Lawall and cocci mailing list)

On Wed, 2018-06-20 at 09:48 -0700, Guenter Roeck wrote:
[]
> > +static inline void npcm7xx_fan_start_capture(struct npcm7xx_pwm_fan_data *data,
> > + u8 fan, u8 cmp)
> > +{
> > + u8 fan_id = 0;
> > + u8 reg_mode = 0;
> > + u8 reg_int = 0;
> > + unsigned long flags;
> > +
> > + fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
> > +
> > + /* to check whether any fan tach is enable */
> > + if (data->npcm7xx_fan[fan_id].FanStFlag != FAN_DISABLE) {
> > + /* reset status */
> > + spin_lock_irqsave(&data->npcm7xx_fan_lock[fan], flags);
> > +
> > + data->npcm7xx_fan[fan_id].FanStFlag = FAN_INIT;
> > + reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> > +
> > + if (cmp == NPCM7XX_FAN_CMPA) {
> > + /* enable interrupt */
> > + iowrite8((u8) (reg_int | (NPCM7XX_FAN_TIEN_TAIEN |
> > + NPCM7XX_FAN_TIEN_TEIEN)),
>
> Is the (u8) typecast really necessary ? Seems unlikely.

The cast is not really necessary here as there would
be an implicit cast already.

Some might complain about loss of type safety and
"make W=123" would probably emit something here.

But casts to the same type are not necessary.

A possible coccinelle script to find casts to the
same type is below, but there are some false positives
for things like __force and __user casts

Also, spatch (1.0.4) seems to have a defect for this
when the type is used in operations that change a
smaller type to int or unsigned int.

i.e.: (offset is u16, but offset * 2 is int)

While running the cocci script below:

HANDLING: drivers/net/ethernet/intel/igb/e1000_nvm.c
diff =
diff -u -p a/drivers/net/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c
--- a/drivers/net/ethernet/intel/igb/e1000_nvm.c
+++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c
@@ -335,7 +335,7 @@ s32 igb_read_nvm_spi(struct e1000_hw *hw

/* Send the READ command (opcode + addr) */
igb_shift_out_eec_bits(hw, read_opcode, nvm->opcode_bits);
- igb_shift_out_eec_bits(hw, (u16)(offset*2), nvm->address_bits);
+ igb_shift_out_eec_bits(hw, (offset * 2), nvm->address_bits);

/* Read the data. SPI NVMs increment the address with each byte
* read and will roll over if reading beyond the end. This allows

---

Anyway, here's the cocci script:

$ cat same_typecast.cocci
@@
type T;
T foo;
@@

- (T *)&foo
+ &foo

@@
type T;
T foo;
@@

- (T)foo
+ foo


2018-06-20 19:39:00

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] hwmon: npcm750: add NPCM7xx PWM and Fan driver

On Wed, Jun 20, 2018 at 11:25:08AM -0700, Joe Perches wrote:
> (adding Julia Lawall and cocci mailing list)
>
> On Wed, 2018-06-20 at 09:48 -0700, Guenter Roeck wrote:
> []
> > > +static inline void npcm7xx_fan_start_capture(struct npcm7xx_pwm_fan_data *data,
> > > + u8 fan, u8 cmp)
> > > +{
> > > + u8 fan_id = 0;
> > > + u8 reg_mode = 0;
> > > + u8 reg_int = 0;
> > > + unsigned long flags;
> > > +
> > > + fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
> > > +
> > > + /* to check whether any fan tach is enable */
> > > + if (data->npcm7xx_fan[fan_id].FanStFlag != FAN_DISABLE) {
> > > + /* reset status */
> > > + spin_lock_irqsave(&data->npcm7xx_fan_lock[fan], flags);
> > > +
> > > + data->npcm7xx_fan[fan_id].FanStFlag = FAN_INIT;
> > > + reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> > > +
> > > + if (cmp == NPCM7XX_FAN_CMPA) {
> > > + /* enable interrupt */
> > > + iowrite8((u8) (reg_int | (NPCM7XX_FAN_TIEN_TAIEN |
> > > + NPCM7XX_FAN_TIEN_TEIEN)),
> >
> > Is the (u8) typecast really necessary ? Seems unlikely.
>
> The cast is not really necessary here as there would
> be an implicit cast already.
>
> Some might complain about loss of type safety and
> "make W=123" would probably emit something here.
>
I spent (wasted) some time browsing through the kernel.
Similar typecasts are only used if there is a real type change.
A warning here would not make sense unless NPCM7XX_FAN_TIEN_TAIEN
or NPCM7XX_FAN_TIEN_TEIEN would be outside the u8 range, and then
there would be one anyway.

So, no, I am not going to accept those typecasts. They just make the code
more difficult to read. For example, the code here could have been
simplified to something like

reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
reg_mode = ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
if (cmp == NPCM7XX_FAN_CMPA) {
reg_int |= NPCM7XX_FAN_TIEN_TAIEN | NPCM7XX_FAN_TIEN_TEIEN;
reg_mode |= NPCM7XX_FAN_TCKC_CLK1_APB;
} else {
reg_int |= NPCM7XX_FAN_TIEN_TBIEN | NPCM7XX_FAN_TIEN_TFIEN;
reg_mode |= NPCM7XX_FAN_TCKC_CLK2_APB;
}
iowrite8(reg_int, NPCM7XX_FAN_REG_TIEN(data->fan_base, fan);
iowrite8(reg_mode, NPCM7XX_FAN_REG_TCKC(data->fan_base, fan);

This, in turn, leads to the question if it is really not necessary
to _clear_ those mask bits in the same context.

Guenter

> But casts to the same type are not necessary.
>
> A possible coccinelle script to find casts to the
> same type is below, but there are some false positives
> for things like __force and __user casts
>
> Also, spatch (1.0.4) seems to have a defect for this
> when the type is used in operations that change a
> smaller type to int or unsigned int.
>
> i.e.: (offset is u16, but offset * 2 is int)
>
> While running the cocci script below:
>
> HANDLING: drivers/net/ethernet/intel/igb/e1000_nvm.c
> diff =
> diff -u -p a/drivers/net/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c
> --- a/drivers/net/ethernet/intel/igb/e1000_nvm.c
> +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c
> @@ -335,7 +335,7 @@ s32 igb_read_nvm_spi(struct e1000_hw *hw
>
> /* Send the READ command (opcode + addr) */
> igb_shift_out_eec_bits(hw, read_opcode, nvm->opcode_bits);
> - igb_shift_out_eec_bits(hw, (u16)(offset*2), nvm->address_bits);
> + igb_shift_out_eec_bits(hw, (offset * 2), nvm->address_bits);
>
> /* Read the data. SPI NVMs increment the address with each byte
> * read and will roll over if reading beyond the end. This allows
>
> ---
>
> Anyway, here's the cocci script:
>
> $ cat same_typecast.cocci
> @@
> type T;
> T foo;
> @@
>
> - (T *)&foo
> + &foo
>
> @@
> type T;
> T foo;
> @@
>
> - (T)foo
> + foo
>

2018-06-21 13:19:02

by Julia Lawall

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] hwmon: npcm750: add NPCM7xx PWM and Fan driver



On Wed, 20 Jun 2018, Joe Perches wrote:

> (adding Julia Lawall and cocci mailing list)
>
> On Wed, 2018-06-20 at 09:48 -0700, Guenter Roeck wrote:
> []
> > > +static inline void npcm7xx_fan_start_capture(struct npcm7xx_pwm_fan_data *data,
> > > + u8 fan, u8 cmp)
> > > +{
> > > + u8 fan_id = 0;
> > > + u8 reg_mode = 0;
> > > + u8 reg_int = 0;
> > > + unsigned long flags;
> > > +
> > > + fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
> > > +
> > > + /* to check whether any fan tach is enable */
> > > + if (data->npcm7xx_fan[fan_id].FanStFlag != FAN_DISABLE) {
> > > + /* reset status */
> > > + spin_lock_irqsave(&data->npcm7xx_fan_lock[fan], flags);
> > > +
> > > + data->npcm7xx_fan[fan_id].FanStFlag = FAN_INIT;
> > > + reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> > > +
> > > + if (cmp == NPCM7XX_FAN_CMPA) {
> > > + /* enable interrupt */
> > > + iowrite8((u8) (reg_int | (NPCM7XX_FAN_TIEN_TAIEN |
> > > + NPCM7XX_FAN_TIEN_TEIEN)),
> >
> > Is the (u8) typecast really necessary ? Seems unlikely.
>
> The cast is not really necessary here as there would
> be an implicit cast already.
>
> Some might complain about loss of type safety and
> "make W=123" would probably emit something here.
>
> But casts to the same type are not necessary.
>
> A possible coccinelle script to find casts to the
> same type is below, but there are some false positives
> for things like __force and __user casts
>
> Also, spatch (1.0.4) seems to have a defect for this
> when the type is used in operations that change a
> smaller type to int or unsigned int.
>
> i.e.: (offset is u16, but offset * 2 is int)

Ah. The rule is that the result type is always the larger one?
Unfortunately, Coccinelle doesn't know the size of any type. I could add
some special cases, but that may be more confusing than helpful.

julia

>
> While running the cocci script below:
>
> HANDLING: drivers/net/ethernet/intel/igb/e1000_nvm.c
> diff =
> diff -u -p a/drivers/net/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c
> --- a/drivers/net/ethernet/intel/igb/e1000_nvm.c
> +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c
> @@ -335,7 +335,7 @@ s32 igb_read_nvm_spi(struct e1000_hw *hw
>
> /* Send the READ command (opcode + addr) */
> igb_shift_out_eec_bits(hw, read_opcode, nvm->opcode_bits);
> - igb_shift_out_eec_bits(hw, (u16)(offset*2), nvm->address_bits);
> + igb_shift_out_eec_bits(hw, (offset * 2), nvm->address_bits);
>
> /* Read the data. SPI NVMs increment the address with each byte
> * read and will roll over if reading beyond the end. This allows
>
> ---
>
> Anyway, here's the cocci script:
>
> $ cat same_typecast.cocci
> @@
> type T;
> T foo;
> @@
>
> - (T *)&foo
> + &foo
>
> @@
> type T;
> T foo;
> @@
>
> - (T)foo
> + foo
>
>

2018-06-21 13:50:39

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] hwmon: npcm750: add NPCM7xx PWM and Fan driver

On 06/19/2018 03:53 AM, Tomer Maimon wrote:
> Add Nuvoton BMC NPCM7xx Pulse Width Modulation (PWM) and Fan tacho driver.
>
> The Nuvoton BMC NPCM7xx has two identical PWM controller modules,
> each module has four PWM controller outputs and eight identical Fan
> controller modules, each module has two Fan controller inputs.
>
> The driver provides a sysfs entries through which the user can
> configure the duty-cycle value (ranging from 0 to 100 percent)
> and read the fan tacho rpm value.
>
> The driver support cooling device creation, that could be bound
> to a thermal zone for the thermal control.
>

You ignored most of my previous comments. Why should I review your code
if you ignore most of the feedback ?

> Signed-off-by: Tomer Maimon <[email protected]>
> ---
> drivers/hwmon/Kconfig | 7 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/npcm750-pwm-fan.c | 1099 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1107 insertions(+)
> create mode 100644 drivers/hwmon/npcm750-pwm-fan.c
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index f10840ad465c..f6c2eff9bb7d 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1888,6 +1888,13 @@ config SENSORS_XGENE
> If you say yes here you get support for the temperature
> and power sensors for APM X-Gene SoC.
>
> +config SENSORS_NPCM7XX
> + tristate "Nuvoton NPCM7XX PWM and Fan driver"
> + depends on THERMAL || THERMAL=n
> + help
> + This driver provides support for Nuvoton NPCM7XX PWM and Fan
> + controllers.
> +
> if ACPI
>
> comment "ACPI drivers"
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index e7d52a36e6c4..004e2ec5b68f 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -174,6 +174,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
> obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
> obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
> obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
> +obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o
>
> obj-$(CONFIG_PMBUS) += pmbus/
>
> diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c
> new file mode 100644
> index 000000000000..82898197686f
> --- /dev/null
> +++ b/drivers/hwmon/npcm750-pwm-fan.c
> @@ -0,0 +1,1099 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2014-2018 Nuvoton Technology corporation.
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/sysfs.h>
> +#include <linux/spinlock.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/thermal.h>
> +

I am sure I asked for those to be in alphabetic order.

> +/* NPCM7XX PWM registers */
> +#define NPCM7XX_PWM_REG_BASE(base, n) (base + ((n) * 0x1000L))
> +

(base)

> +#define NPCM7XX_PWM_REG_PR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x00)
> +#define NPCM7XX_PWM_REG_CSR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x04)
> +#define NPCM7XX_PWM_REG_CR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x08)
> +#define NPCM7XX_PWM_REG_CNRx(base, n, ch) \
> + (NPCM7XX_PWM_REG_BASE(base, n) + 0x0C + (12 * ch))
> +#define NPCM7XX_PWM_REG_CMRx(base, n, ch) \
> + (NPCM7XX_PWM_REG_BASE(base, n) + 0x10 + (12 * ch))
> +#define NPCM7XX_PWM_REG_PDRx(base, n, ch) \
> + (NPCM7XX_PWM_REG_BASE(base, n) + 0x14 + (12 * ch))
> +#define NPCM7XX_PWM_REG_PIER(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x3C)
> +#define NPCM7XX_PWM_REG_PIIR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x40)
> +#define NPCM7XX_FAN_REG_BASE(base, n) (base + ((n) * 0x1000L))
> +

(base)

> +#define NPCM7XX_PWM_CTRL_CH0_MODE_BIT BIT(3)
> +#define NPCM7XX_PWM_CTRL_CH1_MODE_BIT BIT(11)
> +#define NPCM7XX_PWM_CTRL_CH2_MODE_BIT BIT(15)
> +#define NPCM7XX_PWM_CTRL_CH3_MODE_BIT BIT(19)
> +
> +#define NPCM7XX_PWM_CTRL_CH0_INV_BIT BIT(2)
> +#define NPCM7XX_PWM_CTRL_CH1_INV_BIT BIT(10)
> +#define NPCM7XX_PWM_CTRL_CH2_INV_BIT BIT(14)
> +#define NPCM7XX_PWM_CTRL_CH3_INV_BIT BIT(18)
> +
> +#define NPCM7XX_PWM_CTRL_CH0_EN_BIT BIT(0)
> +#define NPCM7XX_PWM_CTRL_CH1_EN_BIT BIT(8)
> +#define NPCM7XX_PWM_CTRL_CH2_EN_BIT BIT(12)
> +#define NPCM7XX_PWM_CTRL_CH3_EN_BIT BIT(16)
> +
> +/* Define the maximum PWM channel number */
> +#define NPCM7XX_PWM_MAX_CHN_NUM 8
> +#define NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE 4
> +#define NPCM7XX_PWM_MAX_MODULES 2
> +
> +/* Define the Counter Register, value = 100 for match 100% */
> +#define NPCM7XX_PWM_COUNTER_DEFALUT_NUM 255
> +#define NPCM7XX_PWM_COMPARATOR_DEFALUT_NUM 127
> +
> +#define NPCM7XX_PWM_COMPARATOR_MAX 255
> +
> +/* default all PWM channels PRESCALE2 = 1 */
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH0 0x4
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH1 0x40
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH2 0x400
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH3 0x4000
> +
> +#define PWM_OUTPUT_FREQ_25KHZ 25000
> +#define PWN_CNT_DEFAULT 256
> +#define MIN_PRESCALE1 2
> +#define NPCM7XX_PWM_PRESCALE_SHIFT_CH01 8
> +
> +#define NPCM7XX_PWM_PRESCALE2_DEFALUT (NPCM7XX_PWM_PRESCALE2_DEFALUT_CH0 | \
> + NPCM7XX_PWM_PRESCALE2_DEFALUT_CH1 | \
> + NPCM7XX_PWM_PRESCALE2_DEFALUT_CH2 | \
> + NPCM7XX_PWM_PRESCALE2_DEFALUT_CH3)
> +
> +#define NPCM7XX_PWM_CTRL_MODE_DEFALUT (NPCM7XX_PWM_CTRL_CH0_MODE_BIT | \
> + NPCM7XX_PWM_CTRL_CH1_MODE_BIT | \
> + NPCM7XX_PWM_CTRL_CH2_MODE_BIT | \
> + NPCM7XX_PWM_CTRL_CH3_MODE_BIT)
> +
> +#define NPCM7XX_PWM_CTRL_EN_DEFALUT (NPCM7XX_PWM_CTRL_CH0_EN_BIT | \
> + NPCM7XX_PWM_CTRL_CH1_EN_BIT | \
> + NPCM7XX_PWM_CTRL_CH2_EN_BIT | \
> + NPCM7XX_PWM_CTRL_CH3_EN_BIT)
> +

:1,$s/DEFALUT/DEFAULT/g

Are those defines used anywhere ?

> +/* NPCM7XX FAN Tacho registers */
> +#define NPCM7XX_FAN_REG_BASE(base, n) (base + ((n) * 0x1000L))
> +

(base)

> +#define NPCM7XX_FAN_REG_TCNT1(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x00)
> +#define NPCM7XX_FAN_REG_TCRA(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x02)
> +#define NPCM7XX_FAN_REG_TCRB(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x04)
> +#define NPCM7XX_FAN_REG_TCNT2(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x06)
> +#define NPCM7XX_FAN_REG_TPRSC(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x08)
> +#define NPCM7XX_FAN_REG_TCKC(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0A)
> +#define NPCM7XX_FAN_REG_TMCTRL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0C)
> +#define NPCM7XX_FAN_REG_TICTRL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0E)
> +#define NPCM7XX_FAN_REG_TICLR(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x10)
> +#define NPCM7XX_FAN_REG_TIEN(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x12)
> +#define NPCM7XX_FAN_REG_TCPA(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x14)
> +#define NPCM7XX_FAN_REG_TCPB(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x16)
> +#define NPCM7XX_FAN_REG_TCPCFG(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x18)
> +#define NPCM7XX_FAN_REG_TINASEL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x1A)
> +#define NPCM7XX_FAN_REG_TINBSEL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x1C)
> +
> +#define NPCM7XX_FAN_TCKC_CLKX_NONE 0
> +#define NPCM7XX_FAN_TCKC_CLK1_APB BIT(0)
> +#define NPCM7XX_FAN_TCKC_CLK2_APB BIT(3)
> +
> +#define NPCM7XX_FAN_TMCTRL_TBEN BIT(6)
> +#define NPCM7XX_FAN_TMCTRL_TAEN BIT(5)
> +#define NPCM7XX_FAN_TMCTRL_TBEDG BIT(4)
> +#define NPCM7XX_FAN_TMCTRL_TAEDG BIT(3)
> +#define NPCM7XX_FAN_TMCTRL_MODE_5 BIT(2)
> +
> +#define NPCM7XX_FAN_TICLR_CLEAR_ALL GENMASK(5, 0)
> +#define NPCM7XX_FAN_TICLR_TFCLR BIT(5)
> +#define NPCM7XX_FAN_TICLR_TECLR BIT(4)
> +#define NPCM7XX_FAN_TICLR_TDCLR BIT(3)
> +#define NPCM7XX_FAN_TICLR_TCCLR BIT(2)
> +#define NPCM7XX_FAN_TICLR_TBCLR BIT(1)
> +#define NPCM7XX_FAN_TICLR_TACLR BIT(0)
> +
> +#define NPCM7XX_FAN_TIEN_ENABLE_ALL GENMASK(5, 0)
> +#define NPCM7XX_FAN_TIEN_TFIEN BIT(5)
> +#define NPCM7XX_FAN_TIEN_TEIEN BIT(4)
> +#define NPCM7XX_FAN_TIEN_TDIEN BIT(3)
> +#define NPCM7XX_FAN_TIEN_TCIEN BIT(2)
> +#define NPCM7XX_FAN_TIEN_TBIEN BIT(1)
> +#define NPCM7XX_FAN_TIEN_TAIEN BIT(0)
> +
> +#define NPCM7XX_FAN_TICTRL_TFPND BIT(5)
> +#define NPCM7XX_FAN_TICTRL_TEPND BIT(4)
> +#define NPCM7XX_FAN_TICTRL_TDPND BIT(3)
> +#define NPCM7XX_FAN_TICTRL_TCPND BIT(2)
> +#define NPCM7XX_FAN_TICTRL_TBPND BIT(1)
> +#define NPCM7XX_FAN_TICTRL_TAPND BIT(0)
> +
> +#define NPCM7XX_FAN_TCPCFG_HIBEN BIT(7)
> +#define NPCM7XX_FAN_TCPCFG_EQBEN BIT(6)
> +#define NPCM7XX_FAN_TCPCFG_LOBEN BIT(5)
> +#define NPCM7XX_FAN_TCPCFG_CPBSEL BIT(4)
> +#define NPCM7XX_FAN_TCPCFG_HIAEN BIT(3)
> +#define NPCM7XX_FAN_TCPCFG_EQAEN BIT(2)
> +#define NPCM7XX_FAN_TCPCFG_LOAEN BIT(1)
> +#define NPCM7XX_FAN_TCPCFG_CPASEL BIT(0)
> +
> +/* FAN General Defintion */
> +/* Define the maximum FAN channel number */
> +#define NPCM7XX_FAN_MAX_MODULE 8
> +#define NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE 2
> +#define NPCM7XX_FAN_MAX_CHN_NUM 16
> +
> +/*
> + * Get Fan Tach Timeout (base on clock 214843.75Hz, 1 cnt = 4.654us)
> + * Timeout 94ms ~= 0x5000
> + * (The minimum FAN speed could to support ~640RPM/pulse 1,
> + * 320RPM/pulse 2, ...-- 10.6Hz)
> + */
> +#define NPCM7XX_FAN_TIMEOUT 0x5000
> +#define NPCM7XX_FAN_TCNT 0xFFFF
> +#define NPCM7XX_FAN_TCPA (NPCM7XX_FAN_TCNT - NPCM7XX_FAN_TIMEOUT)
> +#define NPCM7XX_FAN_TCPB (NPCM7XX_FAN_TCNT - NPCM7XX_FAN_TIMEOUT)
> +
> +#define NPCM7XX_FAN_POLL_TIMER_200MS 200
> +#define NPCM7XX_FAN_DEFAULT_PULSE_PER_REVOLUTION 2
> +#define NPCM7XX_FAN_TINASEL_FANIN_DEFAULT 0
> +#define NPCM7XX_FAN_CLK_PRESCALE 255
> +
> +#define NPCM7XX_FAN_CMPA 0
> +#define NPCM7XX_FAN_CMPB 1
> +
> +/* Obtain the fan number */
> +#define NPCM7XX_FAN_INPUT(fan, cmp) ((fan << 1) + (cmp))
> +

(fan)

If you want to be intelligent about () in macros, please at least add () where
a parameter is used in an expression.

> +/* fan sample status */
> +#define FAN_DISABLE 0xFF
> +#define FAN_INIT 0x00
> +#define FAN_PREPARE_TO_GET_FIRST_CAPTURE 0x01
> +#define FAN_ENOUGH_SAMPLE 0x02
> +
> +struct Fan_Dev {
> + u8 FanStFlag;
> + u8 FanPlsPerRev;
> + u16 FanCnt;
> + u32 FanCntTemp;
> +};
> +
> +struct npcm7xx_cooling_device {
> + char name[THERMAL_NAME_LENGTH];
> + struct npcm7xx_pwm_fan_data *data;
> + struct thermal_cooling_device *tcdev;
> + int pwm_port;
> + u8 *cooling_levels;
> + u8 max_state;
> + u8 cur_state;
> +};
> +
> +struct npcm7xx_pwm_fan_data {
> + void __iomem *pwm_base, *fan_base;
> + unsigned long pwm_clk_freq, fan_clk_freq;
> + struct clk *pwm_clk, *fan_clk;
> + struct mutex npcm7xx_pwm_lock[NPCM7XX_PWM_MAX_MODULES];
> + spinlock_t npcm7xx_fan_lock[NPCM7XX_FAN_MAX_MODULE];
> + int fan_irq[NPCM7XX_FAN_MAX_MODULE];
> + bool pwm_present[NPCM7XX_PWM_MAX_CHN_NUM],
> + fan_present[NPCM7XX_FAN_MAX_CHN_NUM];

I am sure I asked to split the declarations.

> + u32 InputClkFreq;
> + struct timer_list npcm7xx_fan_timer;
> + struct Fan_Dev npcm7xx_fan[NPCM7XX_FAN_MAX_CHN_NUM];
> + struct npcm7xx_cooling_device *cdev[NPCM7XX_PWM_MAX_CHN_NUM];
> + u8 npcm7xx_fan_select;
> +};
> +
> +static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data,
> + int channel, u16 val)
> +{
> + u32 PWMCh = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
> + u32 module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
> + u32 u32TmpBuf = 0, ctrl_en_bit, env_bit;
> +

I am sure I asked you to drop the u32 from this variable name, and I am sure
I asked not to use capital letters in variable names.

> + /*
> + * Config PWM Comparator register for setting duty cycle
> + */
> + if (val < 0 || val > NPCM7XX_PWM_COMPARATOR_MAX)
> + return -EINVAL;
> +
> + mutex_lock(&data->npcm7xx_pwm_lock[module]);
> +
> + /* write new CMR value */
> + iowrite32(val, NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, PWMCh));
> + u32TmpBuf = ioread32(NPCM7XX_PWM_REG_CR(data->pwm_base, module));
> +
> + switch (PWMCh) {
> + case 0:
> + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH0_EN_BIT;
> + env_bit = NPCM7XX_PWM_CTRL_CH0_INV_BIT;
> + break;
> + case 1:
> + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH1_EN_BIT;
> + env_bit = NPCM7XX_PWM_CTRL_CH1_INV_BIT;
> + break;
> + case 2:
> + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH2_EN_BIT;
> + env_bit = NPCM7XX_PWM_CTRL_CH2_INV_BIT;
> + break;
> + case 3:
> + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH3_EN_BIT;
> + env_bit = NPCM7XX_PWM_CTRL_CH3_INV_BIT;
> + break;
> + default:
> + return -ENODEV;
> + }
> +
> + if (val == 0) {
> + /* Disable PWM */
> + u32TmpBuf &= ~(ctrl_en_bit);
> + u32TmpBuf |= env_bit;
> + } else {
> + /* Enable PWM */
> + u32TmpBuf |= ctrl_en_bit;
> + u32TmpBuf &= ~(env_bit);
> + }
> +
> + iowrite32(u32TmpBuf, NPCM7XX_PWM_REG_CR(data->pwm_base, module));
> + mutex_unlock(&data->npcm7xx_pwm_lock[module]);
> +
> + return 0;
> +}
> +
> +static inline void npcm7xx_fan_start_capture(struct npcm7xx_pwm_fan_data *data,
> + u8 fan, u8 cmp)
> +{
> + u8 fan_id = 0;
> + u8 reg_mode = 0;
> + u8 reg_int = 0;

Those initializations are unnecessary.

> + unsigned long flags;
> +
> + fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
> +
> + /* to check whether any fan tach is enable */
> + if (data->npcm7xx_fan[fan_id].FanStFlag != FAN_DISABLE) {
> + /* reset status */
> + spin_lock_irqsave(&data->npcm7xx_fan_lock[fan], flags);
> +
> + data->npcm7xx_fan[fan_id].FanStFlag = FAN_INIT;
> + reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +

If the flag bits don't need to be cleared, state that here in a comment.

> + if (cmp == NPCM7XX_FAN_CMPA) {
> + /* enable interrupt */
> + iowrite8((u8) (reg_int | (NPCM7XX_FAN_TIEN_TAIEN |
> + NPCM7XX_FAN_TIEN_TEIEN)),

s/(u8)//

> + NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + reg_mode = NPCM7XX_FAN_TCKC_CLK1_APB
> + | ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base,
> + fan));
> +
> + /* start to Capture */
> + iowrite8(reg_mode, NPCM7XX_FAN_REG_TCKC(data->fan_base,
> + fan));
> + } else {
> + /* enable interrupt */
> + iowrite8((u8) (reg_int | (NPCM7XX_FAN_TIEN_TBIEN |
> + NPCM7XX_FAN_TIEN_TFIEN)),
> + NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + reg_mode =
> + NPCM7XX_FAN_TCKC_CLK2_APB
> + | ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base,
> + fan));
> +
> + /* start to Capture */
> + iowrite8(reg_mode,
> + NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
> + }
> +
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[fan], flags);
> + }
> +}
> +
> +/*
> + * Enable a background timer to poll fan tach value, (200ms * 4)
> + * to polling all fan
> + */
> +static void npcm7xx_fan_polling(struct timer_list *t)
> +{
> + struct npcm7xx_pwm_fan_data *data;
> + unsigned long flags;
> + int i;
> +
> + data = from_timer(data, t, npcm7xx_fan_timer);
> +
> + /*
> + * Polling two module per one round,
> + * FAN01 & FAN89 / FAN23 & FAN1011 / FAN45 & FAN1213 / FAN67 & FAN1415
> + */
> + for (i = data->npcm7xx_fan_select; i < NPCM7XX_FAN_MAX_MODULE;
> + i = i+4) {
> + /* clear the flag and reset the counter (TCNT) */
> + spin_lock_irqsave(&data->npcm7xx_fan_lock[i], flags);
> + iowrite8((u8) NPCM7XX_FAN_TICLR_CLEAR_ALL,
> + NPCM7XX_FAN_REG_TICLR(data->fan_base, i));
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[i], flags);
> +
> + if (data->fan_present[i*2] == true) {
> + spin_lock_irqsave(&data->npcm7xx_fan_lock[i], flags);
> + iowrite16(NPCM7XX_FAN_TCNT,
> + NPCM7XX_FAN_REG_TCNT1(data->fan_base, i));
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[i],
> + flags);
> + npcm7xx_fan_start_capture(data, i, NPCM7XX_FAN_CMPA);
> + }
> + if (data->fan_present[(i*2)+1] == true) {
> + spin_lock_irqsave(&data->npcm7xx_fan_lock[i], flags);
> + iowrite16(NPCM7XX_FAN_TCNT,
> + NPCM7XX_FAN_REG_TCNT2(data->fan_base, i));
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[i],
> + flags);
> + npcm7xx_fan_start_capture(data, i, NPCM7XX_FAN_CMPB);
> + }
> + }
> +
> + data->npcm7xx_fan_select++;
> + data->npcm7xx_fan_select &= 0x3;
> +
> + /* reset the timer interval */
> + data->npcm7xx_fan_timer.expires = jiffies +
> + msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
> + add_timer(&data->npcm7xx_fan_timer);
> +}
> +
> +static inline void npcm7xx_fan_compute(struct npcm7xx_pwm_fan_data *data,
> + u8 fan, u8 cmp, u8 fan_id,
> + u8 flag_int, u8 flag_mode,
> + u8 flag_clear)
> +{
> + u8 reg_int = 0;
> + u8 reg_mode = 0;
> + u16 fan_cap = 0;
> +
> + if (cmp == NPCM7XX_FAN_CMPA)
> + fan_cap = ioread16(NPCM7XX_FAN_REG_TCRA(data->fan_base, fan));
> + else
> + fan_cap = ioread16(NPCM7XX_FAN_REG_TCRB(data->fan_base, fan));
> +
> + /* clear capature flag, H/W will auto reset the NPCM7XX_FAN_TCNTx */
> + iowrite8((u8) flag_clear, NPCM7XX_FAN_REG_TICLR(data->fan_base, fan));
> +
> + if (data->npcm7xx_fan[fan_id].FanStFlag == FAN_INIT) {
> + /* First capture, drop it */
> + data->npcm7xx_fan[fan_id].FanStFlag =
> + FAN_PREPARE_TO_GET_FIRST_CAPTURE;
> +
> + /* reset counter */
> + data->npcm7xx_fan[fan_id].FanCntTemp = 0;
> + } else if (data->npcm7xx_fan[fan_id].FanStFlag <
> + FAN_ENOUGH_SAMPLE) {
> + /*
> + * collect the enough sample,
> + * (ex: 2 pulse fan need to get 2 sample)
> + */
> + data->npcm7xx_fan[fan_id].FanCntTemp +=
> + (NPCM7XX_FAN_TCNT - fan_cap);
> +
> + data->npcm7xx_fan[fan_id].FanStFlag++;
> + } else {
> + /* get enough sample or fan disable */
> + if (data->npcm7xx_fan[fan_id].FanStFlag == FAN_ENOUGH_SAMPLE) {
> + data->npcm7xx_fan[fan_id].FanCntTemp +=
> + (NPCM7XX_FAN_TCNT - fan_cap);
> +
> + /* compute finial average cnt per pulse */
> + data->npcm7xx_fan[fan_id].FanCnt
> + = data->npcm7xx_fan[fan_id].FanCntTemp /
> + FAN_ENOUGH_SAMPLE;
> +
> + data->npcm7xx_fan[fan_id].FanStFlag = FAN_INIT;
> + }
> +
> + reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + /* disable interrupt */
> + iowrite8((u8) (reg_int & ~flag_int),
> + NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> + reg_mode = ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
> +
> + /* stop capturing */
> + iowrite8((u8) (reg_mode & ~flag_mode),
> + NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
> +
> + }
> +}
> +
> +static inline void npcm7xx_check_cmp(struct npcm7xx_pwm_fan_data *data,
> + u8 fan, u8 cmp, u8 flag)
> +{
> + u8 reg_int = 0;
> + u8 reg_mode = 0;
> + u8 flag_timeout;
> + u8 flag_cap;
> + u8 flag_clear;
> + u8 flag_int;
> + u8 flag_mode;
> + u8 fan_id;
> +
> + fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
> +
> + if (cmp == NPCM7XX_FAN_CMPA) {
> + flag_cap = NPCM7XX_FAN_TICTRL_TAPND;
> + flag_timeout = NPCM7XX_FAN_TICTRL_TEPND;
> + flag_int = NPCM7XX_FAN_TIEN_TAIEN | NPCM7XX_FAN_TIEN_TEIEN;
> + flag_mode = NPCM7XX_FAN_TCKC_CLK1_APB;
> + flag_clear = NPCM7XX_FAN_TICLR_TACLR | NPCM7XX_FAN_TICLR_TECLR;
> + } else {
> + flag_cap = NPCM7XX_FAN_TICTRL_TBPND;
> + flag_timeout = NPCM7XX_FAN_TICTRL_TFPND;
> + flag_int = NPCM7XX_FAN_TIEN_TBIEN | NPCM7XX_FAN_TIEN_TFIEN;
> + flag_mode = NPCM7XX_FAN_TCKC_CLK2_APB;
> + flag_clear = NPCM7XX_FAN_TICLR_TBCLR | NPCM7XX_FAN_TICLR_TFCLR;
> + }
> +
> +
> + if (flag & flag_timeout) {
> +
> + reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +
> + /* disable interrupt */
> + iowrite8((u8) (reg_int & ~flag_int),
> + NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
> +

s/(u8)//

> + /* clear interrup flag */
> + iowrite8((u8) flag_clear, NPCM7XX_FAN_REG_TICLR(data->fan_base,
> + fan));
> +
> + reg_mode = ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
> +
> + /* stop capturing */
> + iowrite8((u8) (reg_mode & ~flag_mode),
> + NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));

s/(u8)//

> +
> + /*
> + * If timeout occurs (NPCM7XX_FAN_TIMEOUT), the fan doesn't
> + * connect or speed is lower than 10.6Hz (320RPM/pulse2).
> + * In these situation, the RPM output should be zero.
> + */
> + data->npcm7xx_fan[fan_id].FanCnt = 0;
> + //DEBUG_MSG("%s : it is timeout fan_id %d\n", __func__, fan_id);

No commented out code please.

> + } else {
> + /* input capture is occurred */
> + if (flag & flag_cap)
> + npcm7xx_fan_compute(data, fan, cmp, fan_id, flag_int,
> + flag_mode, flag_clear);
> + }
> +}
> +
> +static irqreturn_t npcm7xx_fan_isr(int irq, void *dev_id)
> +{
> + struct npcm7xx_pwm_fan_data *data = dev_id;
> + u8 flag = 0;
> + int module;
> + unsigned long flags;
> +
> + module = irq - data->fan_irq[0];
> + spin_lock_irqsave(&data->npcm7xx_fan_lock[module], flags);
> +
> + flag = ioread8(NPCM7XX_FAN_REG_TICTRL(data->fan_base, module));
> + if (flag > 0) {
> + npcm7xx_check_cmp(data, module, NPCM7XX_FAN_CMPA, flag);
> + npcm7xx_check_cmp(data, module, NPCM7XX_FAN_CMPB, flag);
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[module], flags);
> + return IRQ_HANDLED;
> + }
> +
> + spin_unlock_irqrestore(&data->npcm7xx_fan_lock[module], flags);
> +
> + return IRQ_NONE;
> +}
> +
> +static int npcm7xx_read_pwm(struct device *dev, u32 attr, int channel,
> + long *val)
> +{
> + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
> + u32 PWMCh = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
> + u32 module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + switch (attr) {
> + case hwmon_pwm_input:
> + mutex_lock(&data->npcm7xx_pwm_lock[module]);
> + *val = (long)ioread32(
> + NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, PWMCh));

s/(long)//

I don't think you will ever see a negative pwm value. If anything, I would
expect a mask here if the register can have upper bits set.

> + mutex_unlock(&data->npcm7xx_pwm_lock[module]);
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int npcm7xx_write_pwm(struct device *dev, u32 attr, int channel,
> + long val)
> +{
> + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
> + int err = 0;
> +
> + switch (attr) {
> + case hwmon_pwm_input:
> + err = npcm7xx_pwm_config_set(data, channel, (u16)val);
> + break;
> + default:
> + err = -EOPNOTSUPP;
> + break;
> + }
> +
> + return err;
> +}
> +
> +static umode_t npcm7xx_pwm_is_visible(const void *_data, u32 attr, int channel)
> +{
> + const struct npcm7xx_pwm_fan_data *data = _data;
> +
> + if (!data->pwm_present[channel])
> + return 0;
> +
> + switch (attr) {
> + case hwmon_pwm_input:
> + return 0644;
> + default:
> + return 0;
> + }
> +}
> +
> +static int npcm7xx_read_fan(struct device *dev, u32 attr, int channel,
> + long *val)
> +{
> + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
> +
> + switch (attr) {
> + case hwmon_fan_input:
> + *val = 0;
> + if (data->npcm7xx_fan[channel].FanCnt <= 0)
> + return data->npcm7xx_fan[channel].FanCnt;
> +
> + /* Convert the raw reading to RPM */
> + if ((data->npcm7xx_fan[channel].FanCnt > 0) &&
> + data->npcm7xx_fan[channel].FanPlsPerRev > 0)
> + *val = (long)((data->InputClkFreq * 60)/
> + (data->npcm7xx_fan[channel].FanCnt *
> + data->npcm7xx_fan[channel].FanPlsPerRev));

I see you really like typecasts. I don't. Please clean up all of them, and please
drop all commented out code.

> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static umode_t npcm7xx_fan_is_visible(const void *_data, u32 attr, int channel)
> +{
> + const struct npcm7xx_pwm_fan_data *data = _data;
> +
> + if (!data->fan_present[channel])
> + return 0;
> +
> + switch (attr) {
> + case hwmon_fan_input:
> + return 0444;
> + default:
> + return 0;
> + }
> +}
> +
> +static int npcm7xx_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *val)
> +{
> + switch (type) {
> + case hwmon_pwm:
> + return npcm7xx_read_pwm(dev, attr, channel, val);
> + case hwmon_fan:
> + return npcm7xx_read_fan(dev, attr, channel, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int npcm7xx_write(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long val)
> +{
> + switch (type) {
> + case hwmon_pwm:
> + return npcm7xx_write_pwm(dev, attr, channel, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static umode_t npcm7xx_is_visible(const void *data,
> + enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + switch (type) {
> + case hwmon_pwm:
> + return npcm7xx_pwm_is_visible(data, attr, channel);
> + case hwmon_fan:
> + return npcm7xx_fan_is_visible(data, attr, channel);
> + default:
> + return 0;
> + }
> +}
> +
> +static const u32 npcm7xx_pwm_config[] = {
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + HWMON_PWM_INPUT,
> + 0
> +};
> +
> +static const struct hwmon_channel_info npcm7xx_pwm = {
> + .type = hwmon_pwm,
> + .config = npcm7xx_pwm_config,
> +};
> +
> +static const u32 npcm7xx_fan_config[] = {
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + HWMON_F_INPUT,
> + 0
> +};
> +
> +static const struct hwmon_channel_info npcm7xx_fan = {
> + .type = hwmon_fan,
> + .config = npcm7xx_fan_config,
> +};
> +
> +static const struct hwmon_channel_info *npcm7xx_info[] = {
> + &npcm7xx_pwm,
> + &npcm7xx_fan,
> + NULL
> +};
> +
> +static const struct hwmon_ops npcm7xx_hwmon_ops = {
> + .is_visible = npcm7xx_is_visible,
> + .read = npcm7xx_read,
> + .write = npcm7xx_write,
> +};
> +
> +static const struct hwmon_chip_info npcm7xx_chip_info = {
> + .ops = &npcm7xx_hwmon_ops,
> + .info = npcm7xx_info,
> +};
> +
> +static u32 npcm7xx_pwm_init(struct npcm7xx_pwm_fan_data *data)
> +{
> + int m, ch;
> + u32 Prescale_val, output_freq;
> +
> + data->pwm_clk_freq = clk_get_rate(data->pwm_clk);
> +
> + /* Adjust NPCM7xx PWMs output frequency to ~25Khz */
> + output_freq = data->pwm_clk_freq / PWN_CNT_DEFAULT;
> + Prescale_val = DIV_ROUND_CLOSEST(output_freq, PWM_OUTPUT_FREQ_25KHZ);
> +
> + /* If Prescale_val = 0, then the prescale output clock is stopped */
> + if (Prescale_val < MIN_PRESCALE1)
> + Prescale_val = MIN_PRESCALE1;
> + /*
> + * Prescale_val need to decrement in one because in the PWM Prescale
> + * register the Prescale value increment by one
> + */
> + Prescale_val--;
> +
> + /* Setting PWM Prescale Register value register to both modules */
> + Prescale_val |= (Prescale_val << NPCM7XX_PWM_PRESCALE_SHIFT_CH01);
> +
> + for (m = 0; m < NPCM7XX_PWM_MAX_MODULES ; m++) {
> + iowrite32(Prescale_val, NPCM7XX_PWM_REG_PR(data->pwm_base, m));
> + iowrite32(NPCM7XX_PWM_PRESCALE2_DEFALUT,
> + NPCM7XX_PWM_REG_CSR(data->pwm_base, m));
> + iowrite32(NPCM7XX_PWM_CTRL_MODE_DEFALUT,
> + NPCM7XX_PWM_REG_CR(data->pwm_base, m));
> +
> + for (ch = 0; ch < NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE; ch++) {
> + iowrite32(NPCM7XX_PWM_COUNTER_DEFALUT_NUM,
> + NPCM7XX_PWM_REG_CNRx(data->pwm_base, m, ch));
> + }
> + }
> +
> + return output_freq / ((Prescale_val & 0xf) + 1);
> +}
> +
> +static void npcm7xx_fan_init(struct npcm7xx_pwm_fan_data *data)
> +{
> + int md;
> + int ch;
> + int i;
> + u32 apb_clk_freq;
> +
> + for (md = 0; md < NPCM7XX_FAN_MAX_MODULE; md++) {
> +
> + /* stop FAN0~7 clock */
> + iowrite8((u8)NPCM7XX_FAN_TCKC_CLKX_NONE,
> + NPCM7XX_FAN_REG_TCKC(data->fan_base, md));
> +
> + /* disable all interrupt */
> + iowrite8((u8) 0x00, NPCM7XX_FAN_REG_TIEN(data->fan_base, md));
> +
> + /* clear all interrupt */
> + iowrite8((u8) NPCM7XX_FAN_TICLR_CLEAR_ALL,
> + NPCM7XX_FAN_REG_TICLR(data->fan_base, md));
> +
> + /* set FAN0~7 clock prescaler */
> + iowrite8((u8) NPCM7XX_FAN_CLK_PRESCALE,
> + NPCM7XX_FAN_REG_TPRSC(data->fan_base, md));
> +
> + /* set FAN0~7 mode (high-to-low transition) */
> + iowrite8(
> + (u8) (
> + NPCM7XX_FAN_TMCTRL_MODE_5 |
> + NPCM7XX_FAN_TMCTRL_TBEN |
> + NPCM7XX_FAN_TMCTRL_TAEN
> + ),
> + NPCM7XX_FAN_REG_TMCTRL(data->fan_base, md)
> + );
> +
> + /* set FAN0~7 Initial Count/Cap */
> + iowrite16(NPCM7XX_FAN_TCNT,
> + NPCM7XX_FAN_REG_TCNT1(data->fan_base, md));
> + iowrite16(NPCM7XX_FAN_TCNT,
> + NPCM7XX_FAN_REG_TCNT2(data->fan_base, md));
> +
> + /* set FAN0~7 compare (equal to count) */
> + iowrite8((u8)(NPCM7XX_FAN_TCPCFG_EQAEN |
> + NPCM7XX_FAN_TCPCFG_EQBEN),
> + NPCM7XX_FAN_REG_TCPCFG(data->fan_base, md));
> +
> + /* set FAN0~7 compare value */
> + iowrite16(NPCM7XX_FAN_TCPA,
> + NPCM7XX_FAN_REG_TCPA(data->fan_base, md));
> + iowrite16(NPCM7XX_FAN_TCPB,
> + NPCM7XX_FAN_REG_TCPB(data->fan_base, md));
> +
> + /* set FAN0~7 fan input FANIN 0~15 */
> + iowrite8((u8) NPCM7XX_FAN_TINASEL_FANIN_DEFAULT,
> + NPCM7XX_FAN_REG_TINASEL(data->fan_base, md));
> + iowrite8((u8) NPCM7XX_FAN_TINASEL_FANIN_DEFAULT,
> + NPCM7XX_FAN_REG_TINBSEL(data->fan_base, md));
> +
> + for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE; i++) {
> + ch = md * NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE + i;
> + data->npcm7xx_fan[ch].FanStFlag = FAN_DISABLE;
> + data->npcm7xx_fan[ch].FanPlsPerRev =
> + NPCM7XX_FAN_DEFAULT_PULSE_PER_REVOLUTION;
> + data->npcm7xx_fan[ch].FanCnt = 0;
> + }
> + }
> +
> + apb_clk_freq = clk_get_rate(data->fan_clk);
> +
> + /* Fan tach input clock = APB clock / prescalar, default is 255. */
> + data->InputClkFreq = apb_clk_freq / (NPCM7XX_FAN_CLK_PRESCALE + 1);
> +}
> +
> +static int
> +npcm7xx_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev,
> + unsigned long *state)
> +{
> + struct npcm7xx_cooling_device *cdev = tcdev->devdata;
> +
> + *state = cdev->max_state;
> +
> + return 0;
> +}
> +
> +static int
> +npcm7xx_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev,
> + unsigned long *state)
> +{
> + struct npcm7xx_cooling_device *cdev = tcdev->devdata;
> +
> + *state = cdev->cur_state;
> +
> + return 0;
> +}
> +
> +static int
> +npcm7xx_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev,
> + unsigned long state)
> +{
> + struct npcm7xx_cooling_device *cdev = tcdev->devdata;
> + int ret;
> +
> + if (state > cdev->max_state)
> + return -EINVAL;
> +
> + cdev->cur_state = state;
> + ret = npcm7xx_pwm_config_set(cdev->data, cdev->pwm_port,
> + cdev->cooling_levels[cdev->cur_state]);
> +
> + return ret;
> +}
> +
> +static const struct thermal_cooling_device_ops npcm7xx_pwm_cool_ops = {
> + .get_max_state = npcm7xx_pwm_cz_get_max_state,
> + .get_cur_state = npcm7xx_pwm_cz_get_cur_state,
> + .set_cur_state = npcm7xx_pwm_cz_set_cur_state,
> +};
> +
> +static int npcm7xx_create_pwm_cooling(struct device *dev,
> + struct device_node *child,
> + struct npcm7xx_pwm_fan_data *data,
> + u32 pwm_port, u8 num_levels)
> +{
> + int ret;
> + struct npcm7xx_cooling_device *cdev;
> +
> + cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
> + if (!cdev)
> + return -ENOMEM;
> +
> + cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL);
> + if (!cdev->cooling_levels)
> + return -ENOMEM;
> +
> + cdev->max_state = num_levels - 1;
> + ret = of_property_read_u8_array(child, "cooling-levels",
> + cdev->cooling_levels,
> + num_levels);
> + if (ret) {
> + dev_err(dev, "Property 'cooling-levels' cannot be read.\n");
> + return ret;
> + }
> + snprintf(cdev->name, THERMAL_NAME_LENGTH, "%s%d", child->name,
> + pwm_port);
> +
> + cdev->tcdev = thermal_of_cooling_device_register(child,
> + cdev->name,
> + cdev,
> + &npcm7xx_pwm_cool_ops);
> + if (IS_ERR(cdev->tcdev))
> + return PTR_ERR(cdev->tcdev);
> +
> + cdev->data = data;
> + cdev->pwm_port = pwm_port;
> +
> + data->cdev[pwm_port] = cdev;
> +
> + return 0;
> +}
> +
> +static int npcm7xx_en_pwm_fan(struct device *dev,
> + struct device_node *child,
> + struct npcm7xx_pwm_fan_data *data)
> +{
> + u8 *fan_ch;
> + u8 pwm_port;
> + int ret, fan_cnt;
> + u8 index, ch;
> +
> + ret = of_property_read_u8(child, "pwm-ch", &pwm_port);
> + if (ret)
> + return ret;
> +
> + data->pwm_present[pwm_port] = true;
> + ret = npcm7xx_pwm_config_set(data, pwm_port,
> + NPCM7XX_PWM_COMPARATOR_DEFALUT_NUM);
> +
> + ret = of_property_count_u8_elems(child, "cooling-levels");
> + if (ret > 0) {
> + ret = npcm7xx_create_pwm_cooling(dev, child, data, pwm_port,
> + ret);
> + if (ret)
> + return ret;
> + }
> +
> + fan_cnt = of_property_count_u8_elems(child, "fan-ch");
> + if (fan_cnt < 1)
> + return -EINVAL;
> +
> + fan_ch = devm_kzalloc(dev, sizeof(*fan_ch) * fan_cnt, GFP_KERNEL);
> + if (!fan_ch)
> + return -ENOMEM;
> +
> + ret = of_property_read_u8_array(child, "fan-ch", fan_ch, fan_cnt);
> + if (ret)
> + return ret;
> +
> + for (ch = 0; ch < fan_cnt; ch++) {
> + index = fan_ch[ch];
> + data->fan_present[index] = true;
> + data->npcm7xx_fan[index].FanStFlag = FAN_INIT;
> + }
> +
> + return 0;
> +}
> +
> +static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np, *child;
> + struct npcm7xx_pwm_fan_data *data;
> + struct resource *res;
> + struct device *hwmon;
> + char name[20];
> + int ret, cnt;
> + u32 output_freq;
> + u32 i;
> +
> + np = dev->of_node;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm_base");
> + if (res == NULL) {
> + pr_err("PWM of_address_to_resource fail ret %d\n", ret);
> + return -ENODEV;
> + }
> +
> + data->pwm_base = devm_ioremap_resource(dev, res);
> + pr_debug("pwm base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
> + (u32)data->pwm_base, res->start, resource_size(res));

I am sure I asked to use %pR. I am also sure I asked to replace the pr_ functions
with dev_ functions.

> + if (!data->pwm_base) {
> + pr_err("pwm probe failed: can't read pwm base address\n");
> + return -ENOMEM;
> + }
> +
> + data->pwm_clk = devm_clk_get(dev, "clk_apb3");
> + if (IS_ERR(data->pwm_clk)) {
> + pr_err(" pwm probe failed: can't read clk.\n");
> + return -ENODEV;
> + }
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fan_base");
> + if (ret) {
> + pr_err("fan of_address_to_resource fail ret %d\n", ret);
> + return -ENODEV;
> + }
> +
> + data->fan_base = devm_ioremap_resource(dev, res);
> + pr_debug("fan base is 0x%08X, res.start 0x%08X , size 0x%08X\n",
> + (u32)data->fan_base, res->start, resource_size(res));
> +

%pR again.

> + if (!data->fan_base) {
> + pr_err("fan probe failed: can't read fan base address.\n");
> + return -ENOMEM;
> + }
> +
> + data->fan_clk = devm_clk_get(dev, "clk_apb4");
> + if (IS_ERR(data->fan_clk)) {
> + pr_err(" FAN probe failed: can't read clk.\n");
> + return -ENODEV;
> + }
> +
> + output_freq = npcm7xx_pwm_init(data);
> + npcm7xx_fan_init(data);
> +
> + for (cnt = 0; cnt < NPCM7XX_PWM_MAX_MODULES ; cnt++)
> + mutex_init(&data->npcm7xx_pwm_lock[cnt]);
> +
> + for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) {
> + spin_lock_init(&data->npcm7xx_fan_lock[i]);
> +
> + data->fan_irq[i] = platform_get_irq(pdev, i);
> + if (!data->fan_irq[i]) {
> + pr_err("%s - failed to map irq %d\n", __func__, i);
> + ret = -EAGAIN;
> + goto err_irq;
> + }
> +
> + sprintf(name, "NPCM7XX-FAN-MD%d", i);
> +
> + if (request_irq(data->fan_irq[i], npcm7xx_fan_isr, 0, name,
> + (void *)data)) {
> + pr_err("NPCM7XX: register irq FAN%d failed\n", i);
> + ret = -EAGAIN;
> + goto err_irq;
> + }
> + }
> +
> + for_each_child_of_node(np, child) {
> + ret = npcm7xx_en_pwm_fan(dev, child, data);
> + if (ret) {
> + pr_err("npcm7xx_en_pwm_fan failed ret %d\n", ret);
> + of_node_put(child);
> + goto err_irq;
> + }
> + }
> +
> + hwmon = devm_hwmon_device_register_with_info(dev, "npcm7xx_pwm_fan",
> + data, &npcm7xx_chip_info,
> + NULL);
> + if (IS_ERR(hwmon)) {
> + pr_err("PWM Driver failed - devm_hwmon_device_register_with_groups failed\n");
> + ret = PTR_ERR(hwmon);
> + goto err_irq;
> + }
> +
> + for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM; i++) {
> + if (data->fan_present[i] == true) {
> + /* fan timer initialization */
> + data->npcm7xx_fan_select = 0;
> + data->npcm7xx_fan_timer.expires = jiffies +
> + msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
> + timer_setup(&data->npcm7xx_fan_timer,
> + npcm7xx_fan_polling, 0);
> + add_timer(&data->npcm7xx_fan_timer);
> + break;
> + }
> + }
> +
> + pr_info("NPCM7XX PWM-FAN Driver probed, output Freq %dHz[PWM], input Freq %dHz[FAN]\n",
> + output_freq, data->InputClkFreq);
> +
> + return 0;
> +
> +err_irq:
> + for (; i > 0; i--)
> + free_irq(data->fan_irq[i-1], (void *)data);
> +
> + return ret;
> +}
> +
> +static const struct of_device_id of_pwm_fan_match_table[] = {
> + { .compatible = "nuvoton,npcm750-pwm-fan", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, of_pwm_fan_match_table);
> +
> +static struct platform_driver npcm7xx_pwm_fan_driver = {
> + .probe = npcm7xx_pwm_fan_probe,
> + .driver = {
> + .name = "npcm7xx_pwm_fan",
> + .of_match_table = of_pwm_fan_match_table,
> + },
> +};
> +
> +module_platform_driver(npcm7xx_pwm_fan_driver);
> +
> +MODULE_DESCRIPTION("Nuvoton NPCM7XX PWM and Fan Tacho driver");
> +MODULE_AUTHOR("Tomer Maimon <[email protected]>");
> +MODULE_LICENSE("GPL v2");
>


2018-06-21 14:51:13

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] hwmon: npcm750: add NPCM7xx PWM and Fan driver

On Thu, 2018-06-21 at 15:17 +0200, Julia Lawall wrote:
> On Wed, 20 Jun 2018, Joe Perches wrote:
> > Also, spatch (1.0.4) seems to have a defect for this
> > when the type is used in operations that change a
> > smaller type to int or unsigned int.
> >
> > i.e.: (offset is u16, but offset * 2 is int)
>
> Ah. The rule is that the result type is always the larger one?

Yes, but not quite, no.

The c90 rules are called "integer promotions" and are
detailed in section 6.3 Conversions

Basically, if any type is smaller than int, all operations
are done as int if possible, or unsigned int if necessary.
If any type is larger than int, then the larger type is used.

If you don't have the c90 standard, this one is close enough.
http://c0x.coding-guidelines.com/6.3.html
(use the next button several times to read the whole section)

Also, section 6.5 details "expressions" where the operands
of things like bit operations use integer promotions.

> Unfortunately, Coccinelle doesn't know the size of any type. I could add
> some special cases, but that may be more confusing than helpful.

Maybe, but when I saw the suggested removal, I was surprised.