[dev-4.7 patch 2/5] hwmon (aspeed) introduce Aspeed SoC pwm-fan driver

Joel Stanley joel at jms.id.au
Wed Aug 17 15:03:46 AEST 2016


Hi Vadim,

Please ensure future submissions have been run through scripts/checkpatch.pl.

I have made some comments below. This driver needs more work before
you can submit it upstream.

On Wed, Aug 10, 2016 at 4:32 PM,  <vadimp at mellanox.com> wrote:
> From: Vadim Pasternak <vadimp at mellanox.com>
>
> The Kconfig controlling compilation of this code are:
> hwmon/aspeed/Kconfig:config SENSORS_ASPEED_PWM_FAN
>
> Porting pwm-fan driver from 3.18 to 4.7.
>
> Signed-off-by: Vadim Pasternak <vadimp at mellanox.com>
> ---
>  drivers/hwmon/Kconfig                 |    2 +
>  drivers/hwmon/Makefile                |    1 +
>  drivers/hwmon/aspeed/Kconfig          |   11 +
>  drivers/hwmon/aspeed/Makefile         |    5 +
>  drivers/hwmon/aspeed/aspeed-pwm-fan.c | 2735 +++++++++++++++++++++++++++++++++
>  5 files changed, 2754 insertions(+)
>  create mode 100644 drivers/hwmon/aspeed/Kconfig
>  create mode 100644 drivers/hwmon/aspeed/Makefile
>  create mode 100644 drivers/hwmon/aspeed/aspeed-pwm-fan.c
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 3b34ba9..b08ea92 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1828,4 +1828,6 @@ config SENSORS_ATK0110
>
>  endif # ACPI
>
> +source drivers/hwmon/aspeed/Kconfig
> +
>  endif # HWMON
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index c0f3201..7220d0c 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -165,6 +165,7 @@ obj-$(CONFIG_SENSORS_WM831X)        += wm831x-hwmon.o
>  obj-$(CONFIG_SENSORS_WM8350)   += wm8350-hwmon.o
>
>  obj-$(CONFIG_PMBUS)            += pmbus/
> +obj-$(CONFIG_MACH_ASPEED_G5)   += aspeed/
>
>  ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
>
> diff --git a/drivers/hwmon/aspeed/Kconfig b/drivers/hwmon/aspeed/Kconfig
> new file mode 100644
> index 0000000..6739973
> --- /dev/null
> +++ b/drivers/hwmon/aspeed/Kconfig
> @@ -0,0 +1,11 @@
> +menu "ASPEED hwmon support"
> +
> +config SENSORS_ASPEED_PWM_FAN
> +        tristate "ASPEED PWM & FAN Tacho Controller Driver"
> +        depends on ARCH_ASPEED
> +        default n
> +        help
> +          This driver provides support for the ASPEED PWM & FAN Tachometer
> +          Controller, which provides an Sensor, fan control.
> +
> +endmenu
> diff --git a/drivers/hwmon/aspeed/Makefile b/drivers/hwmon/aspeed/Makefile
> new file mode 100644
> index 0000000..e372511
> --- /dev/null
> +++ b/drivers/hwmon/aspeed/Makefile
> @@ -0,0 +1,5 @@
> +
> +ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := \
> +        -I$(srctree)/arch/arm/mach-aspeed/include
> +
> +obj-$(CONFIG_SENSORS_ASPEED_PWM_FAN)   += aspeed-pwm-fan.o
> diff --git a/drivers/hwmon/aspeed/aspeed-pwm-fan.c b/drivers/hwmon/aspeed/aspeed-pwm-fan.c
> new file mode 100644
> index 0000000..414ae6d
> --- /dev/null
> +++ b/drivers/hwmon/aspeed/aspeed-pwm-fan.c
> @@ -0,0 +1,2735 @@
> +/*
> + *  drivers/hwmon/aspeed/aspeed-pwm-fan.c
> + *
> + *  ASPEED PWM & Fan Tacho controller driver
> + *
> + *  Copyright (C) 2012-2020  ASPEED Technology Inc.
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License version 2 as
> + *  published by the Free Software Foundation.
> + *
> + *  History:
> + *    2012.08.06: Initial version [Ryan Chen]
> + *    2016.08.06: Porting to kernel 4.7 [Vadim Pasternak vadimp at mellanox.com]
> + */
> +
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/timer.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/hwmon.h>
> +#include <linux/workqueue.h>
> +#include <linux/sysfs.h>
> +#include <linux/err.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <asm/irq.h>
> +#include <asm/mach-types.h>
> +#include <asm/mach/arch.h>
> +#include <mach/reset.h>
> +
> +/* CLK sysfs
> + * 0 : enable
> + * 1 : clk_source
> + *
> + * PWM sysfs A~H (0~7)
> + * 0 - show/store enable
> + * 1 - show/store type
> + * 2 - show/store falling
> + * 3 - show/store rising
> + *
> + * PWM M/N/O Type sysfs
> + * 0 - show/store unit
> + * 1 - show/store division_l
> + * 2 - show/store division_h
> + *
> + * FAN sysfs (0~15)
> + *  show/store enable
> + *  show/store source
> + *  show/store rpm
> + *  show/store alarm
> + *  show/store alarm_en
> + *
> + * Fan M/N/O Type sysfs
> + *  0 - show/store enable
> + *  1 - show/store mode
> + *  2 - show/store unit
> + *  3 - show/store division
> + *  4 - show/store limit
> + */
> +
> +#define AST_SCU_FUN_PIN_CTRL1  0x80    /* Multi-function Pin Control#1 */
> +#define AST_SCU_FUN_PIN_CTRL2  0x84    /* Multi-function Pin Control#2 */
> +#define AST_SCU_FUN_PIN_CTRL3  0x88    /* Multi-function Pin Control#3 */
> +#define AST_SCU_FUN_PIN_CTRL4  0x8C    /* Multi-function Pin Control#4 */
> +#define AST_SCU_FUN_PIN_CTRL5  0x90    /* Multi-function Pin Control#5 */

These SCU registers belong to the pinmux driver. We need to use the
device tree to request the pins to be muxed.

> +#define AST_PWM_GROUP_NUM      (1 + PWM_CH_NUM + 2 * PWM_TYPE_NUM + TACHO_NUM)
> +
> +/* Aspeed PWM & FAN Register Definition */
> +#define AST_PTCR_CTRL          0x00
> +#define AST_PTCR_CLK_CTRL      0x04
> +#define AST_PTCR_DUTY0_CTRL    0x08
> +#define AST_PTCR_DUTY1_CTRL    0x0c
> +#define AST_PTCR_TYPEM_CTRL0   0x10
> +#define AST_PTCR_TYPEM_CTRL1   0x14
> +#define AST_PTCR_TYPEN_CTRL0   0x18
> +#define AST_PTCR_TYPEN_CTRL1   0x1c
> +#define AST_PTCR_TACH_SOURCE   0x20
> +#define AST_PTCR_TRIGGER       0x28
> +#define AST_PTCR_RESULT                0x2c
> +#define AST_PTCR_INTR_CTRL     0x30
> +#define AST_PTCR_INTR_STS      0x34
> +#define AST_PTCR_TYPEM_LIMIT   0x38
> +#define AST_PTCR_TYPEN_LIMIT   0x3C
> +#define AST_PTCR_CTRL_EXT      0x40
> +#define AST_PTCR_CLK_EXT_CTRL  0x44
> +#define AST_PTCR_DUTY2_CTRL    0x48
> +#define AST_PTCR_DUTY3_CTRL    0x4c
> +#define AST_PTCR_TYPEO_CTRL0   0x50
> +#define AST_PTCR_TYPEO_CTRL1   0x54
> +#define AST_PTCR_TACH_SOURCE_EXT 0x60
> +#define AST_PTCR_TYPEO_LIMIT   0x78
> +
> +/* Common definitions */
> +#define FALL_EDGE      (0)
> +#define RISE_EDGE      (0x1)
> +#define BOTH_EDGE      (0x2)
> +#define PWM_TYPE_NUM   3
> +#define PWM_TYPE_M     0x0
> +#define PWM_TYPE_N     0x1
> +#define PWM_TYPE_O     0x2
> +#define PWM_TYPE_MASK  0x3
> +
> +#define TACHO_NUM      16
> +#define PWM_CH_NUM     8
> +#define PWMA           0x0
> +#define PWMB           0x1
> +#define PWMC           0x2
> +#define PWMD           0x3
> +#define PWME           0x4
> +#define PWMF           0x5
> +#define PWMG           0x6
> +#define PWMH           0x7
> +
> +
> +/* AST_PTCR_CTRL:0x00 - PWM-FAN General Control Register */
> +#define AST_PTCR_CTRL_SET_PWMD_TYPE(x) \
> +       ((x & 0x1) << 15 | (x & 0x2) << 6)
> +#define AST_PTCR_CTRL_GET_PWMD_TYPE(x) \
> +       (((x & (0x1 << 7)) >> 6) | ((x & (0x1 << 15)) >> 15))
> +#define AST_PTCR_CTRL_SET_PWMD_TYPE_MASK \
> +       ((0x1 << 7) | (0x1 << 15))
> +
> +#define AST_PTCR_CTRL_SET_PWMC_TYPE(x) \
> +       ((x & 0x1) << 14 | (x & 0x2) << 5)
> +#define AST_PTCR_CTRL_GET_PWMC_TYPE(x) \
> +       (((x & (0x1 << 6)) >> 5) | ((x & (0x1 << 14)) >> 14))
> +#define AST_PTCR_CTRL_SET_PWMC_TYPE_MASK \
> +       ((0x1 << 6) | (0x1 << 14))
> +
> +#define AST_PTCR_CTRL_SET_PWMB_TYPE(x) \
> +       ((x & 0x1)<<13 | (x & 0x2) << 4)
> +#define AST_PTCR_CTRL_GET_PWMB_TYPE(x) \
> +       (((x & (0x1 << 5)) >> 4) | ((x & (0x1 << 13)) >> 13))
> +#define AST_PTCR_CTRL_SET_PWMB_TYPE_MASK \
> +       ((0x1 << 5) | (0x1 << 13))
> +
> +#define AST_PTCR_CTRL_SET_PWMA_TYPE(x) \
> +       ((x & 0x1) << 12 | (x & 0x2) << 3)
> +#define AST_PTCR_CTRL_GET_PWMA_TYPE(x) \
> +       (((x & (0x1 << 4)) >> 3) | ((x & (0x1 << 12)) >> 12))
> +#define AST_PTCR_CTRL_SET_PWMA_TYPE_MASK \
> +       ((0x1 << 4) | (0x1 << 12))
> +
> +#define        AST_PTCR_CTRL_FAN_NUM_EN(x)             (0x1 << (16 + x))
> +
> +#define        AST_PTCR_CTRL_PMWD              (11)
> +#define        AST_PTCR_CTRL_PMWD_EN           (0x1 << 11)
> +#define        AST_PTCR_CTRL_PMWC              (10)
> +#define        AST_PTCR_CTRL_PMWC_EN           (0x1 << 10)
> +#define        AST_PTCR_CTRL_PMWB              (9)
> +#define        AST_PTCR_CTRL_PMWB_EN           (0x1 << 9)
> +#define        AST_PTCR_CTRL_PMWA              (8)
> +#define        AST_PTCR_CTRL_PMWA_EN           (0x1 << 8)
> +#define        AST_PTCR_CTRL_CLK_MCLK          0x2 /* 0:24Mhz, 1:MCLK */
> +#define        AST_PTCR_CTRL_CLK_EN            0x1
> +
> +/* AST_PTCR_CLK_CTRL:0x04 - PWM-FAN Clock Control Register */
> +/* TYPE N */
> +#define AST_PTCR_CLK_CTRL_TYPEN_UNIT           (24)
> +#define AST_PTCR_CLK_CTRL_TYPEN_UNIT_MASK      (0xff << 24)
> +#define AST_PTCR_CLK_CTRL_TYPEN_H              (20)
> +#define AST_PTCR_CLK_CTRL_TYPEN_H_MASK         (0xf << 20)
> +#define AST_PTCR_CLK_CTRL_TYPEN_L              (16)
> +#define AST_PTCR_CLK_CTRL_TYPEN_L_MASK         (0xf << 16)
> +/* TYPE M */
> +#define AST_PTCR_CLK_CTRL_TYPEM_UNIT           (8)
> +#define AST_PTCR_CLK_CTRL_TYPEM_UNIT_MASK      (0xff << 8)
> +#define AST_PTCR_CLK_CTRL_TYPEM_H              (4)
> +#define AST_PTCR_CLK_CTRL_TYPEM_H_MASK         (0xf << 4)
> +#define AST_PTCR_CLK_CTRL_TYPEM_L              (0)
> +#define AST_PTCR_CLK_CTRL_TYPEM_L_MASK         (0xf)
> +
> +
> +/* AST_PTCR_DUTY_CTRL0:0x08 - PWM-FAN duty control 0 register */
> +#define DUTY_CTRL0_PWMB_FALL_POINT             (24)
> +#define DUTY_CTRL0_PWMB_FALL_POINT_MASK                (0xff << 24)
> +#define DUTY_CTRL0_PWMB_RISE_POINT             (16)
> +#define DUTY_CTRL0_PWMB_RISE_POINT_MASK                (0xff << 16)
> +#define DUTY_CTRL0_PWMA_FALL_POINT             (8)
> +#define DUTY_CTRL0_PWMA_FALL_POINT_MASK                (0xff << 8)
> +#define DUTY_CTRL0_PWMA_RISE_POINT             (0)
> +#define DUTY_CTRL0_PWMA_RISE_POINT_MASK                (0xff)
> +
> +
> +/* AST_PTCR_DUTY_CTRL1 : 0x0c - PWM-FAN duty control 1 register */
> +#define DUTY_CTRL1_PWMD_FALL_POINT             (24)
> +#define DUTY_CTRL1_PWMD_FALL_POINT_MASK                (0xff << 24)
> +#define DUTY_CTRL1_PWMD_RISE_POINT             (16)
> +#define DUTY_CTRL1_PWMD_RISE_POINT_MASK                (0xff << 16)
> +#define DUTY_CTRL1_PWMC_FALL_POINT             (8)
> +#define DUTY_CTRL1_PWMC_FALL_POINT_MASK                (0xff << 8)
> +#define DUTY_CTRL1_PWMC_RISE_POINT             (0)
> +#define DUTY_CTRL1_PWMC_RISE_POINT_MASK                (0xff)

There are a lot of defintions here. As many of them are defining the
same bit, common ones can be discarded. Also consider if they are
necessary at all.


> +
> +
> +/* AST_PTCR_TYPEM_CTRL0 : 0x10/0x18/0x50 - Type M/N/O Ctrl 0 Register */
> +#define TYPE_CTRL0_FAN_PERIOD                  (16)
> +#define TYPE_CTRL0_FAN_PERIOD_MASK             (0xffff << 16)
> +#define TYPE_CTRL0_FLAT_EN                     (0x1 << 7)
> +
> +
> +/* 0 : FALL_EDGE,      0x1 : RISE_EDGE , 0x2 :BOTH_EDGE */
> +#define TYPE_CTRL0_FAN_MODE                    (4)
> +#define TYPE_CTRL0_FAN_MODE_MASK               (0x3<<4)
> +#define TYPE_CTRL0_CLK_DIVISION                        (1)
> +#define TYPE_CTRL0_CLK_DIVISION_MASK           (0x7<<1)
> +#define TYPE_CTRL0_FAN_TYPE_EN                 (1)
> +
> +
> +/* AST_PTCR_TYPEM_CTRL1 : 0x14/0x1c/0x54 - Type M/N/O Ctrl 1 Register */
> +#define TYPE_CTRL1_FALL_POINT                  (16)
> +#define TYPE_CTRL1_FALL_POINT_MASK             (0xff << 16)
> +#define TYPE_CTRL1_RISE_POINT                  (0)
> +#define TYPE_CTRL1_RISE_POINT_MASK             (0xff)
> +
> +
> +/* AST_PTCR_TACH_SOURCE : 0x20/0x60 - Tach Source Register
> + * bit [0,1] at 0x20, bit [2] at 0x60
> + */
> +#define TACH_PWM_SOURCE_BIT01(x)               (x * 2)
> +#define TACH_PWM_SOURCE_BIT2(x)                        (x * 2)
> +#define TACH_PWM_SOURCE_MASK_BIT01(x)          (0x3 << (x * 2))
> +#define TACH_PWM_SOURCE_MASK_BIT2(x)           (0x1 << (x * 2))
> +
> +/* AST_PTCR_TRIGGER : 0x28 - Trigger Register */
> +#define TRIGGER_READ_FAN_NUM(x)                        (0x1 << x)
> +
> +/* AST_PTCR_RESULT : 0x2c - Result Register */
> +#define RESULT_STATUS                          (31)
> +#define RESULT_VALUE_MASK                      (0xfffff)
> +
> +/* AST_PTCR_INTR_CTRL : 0x30 - Interrupt Ctrl Register */
> +#define INTR_CTRL_EN_NUM(x)                    (0x1 << x)
> +
> +/* AST_PTCR_INTR_STS : 0x34 - Interrupt Status Register        */
> +#define INTR_CTRL_NUM(x)                       (0x1 << x)
> +
> +/* AST_PTCR_TYPEM_LIMIT, AST_PTCR_TYPEN_LIMIT,AST_PTCR_TYPEO_LIMIT:
> + * 0x38/0x3C/0x78 - Type M / N / O Limit Register
> + */
> +#define FAN_LIMIT_MASK                         (0xfffff)
> +
> +/* AST_PTCR_CTRL_EXT : 0x40 - General Ctrl Extension #1 */
> +#define AST_PTCR_CTRL_SET_PWMH_TYPE(x) \
> +       ((x & 0x1) << 15 | (x & 0x2) << 6)
> +#define AST_PTCR_CTRL_GET_PWMH_TYPE(x) \
> +       (((x & (0x1 << 7))>>6) | ((x & (0x1 << 15)) >> 15))
> +#define AST_PTCR_CTRL_SET_PWMH_TYPE_MASK       ((0x1 << 7) | (0x1 << 15))
> +
> +#define AST_PTCR_CTRL_SET_PWMG_TYPE(x) \
> +       ((x & 0x1)<<14 | (x & 0x2) << 5)
> +#define AST_PTCR_CTRL_GET_PWMG_TYPE(x) \
> +       (((x & (0x1 << 6))>>5) | ((x & (0x1 << 14)) >> 14))
> +#define AST_PTCR_CTRL_SET_PWMG_TYPE_MASK       ((0x1 << 6) | (0x1 << 14))
> +
> +#define AST_PTCR_CTRL_SET_PWMF_TYPE(x) \
> +               ((x & 0x1) << 13 | (x & 0x2) << 4)
> +#define AST_PTCR_CTRL_GET_PWMF_TYPE(x) \
> +       (((x & (0x1 << 5)) >> 4) | ((x & (0x1 << 13)) >> 13))
> +#define AST_PTCR_CTRL_SET_PWMF_TYPE_MASK       ((0x1 << 5) | (0x1 << 13))
> +
> +#define AST_PTCR_CTRL_SET_PWME_TYPE(x) \
> +       ((x & 0x1) << 12 | (x & 0x2) << 3)
> +#define AST_PTCR_CTRL_GET_PWME_TYPE(x) \
> +       (((x & (0x1 << 4)) >> 3) | ((x & (0x1 << 12)) >> 12))
> +#define AST_PTCR_CTRL_SET_PWME_TYPE_MASK       ((0x1 << 4) | (0x1 << 12))
> +
> +#define        AST_PTCR_CTRL_PMWH                      (11)
> +#define        AST_PTCR_CTRL_PMWH_EN                   (0x1 << 11)
> +#define        AST_PTCR_CTRL_PMWG                      (10)
> +#define        AST_PTCR_CTRL_PMWG_EN                   (0x1 << 10)
> +#define        AST_PTCR_CTRL_PMWF                      (9)
> +#define        AST_PTCR_CTRL_PMWF_EN                   (0x1 << 9)
> +#define        AST_PTCR_CTRL_PMWE                      (8)
> +#define        AST_PTCR_CTRL_PMWE_EN                   (0x1 << 8)
> +
> +/* AST_PTCR_CLK_EXT_CTRL : 0x44 - Clock Control Extension #1 */
> +/* TYPE O */
> +#define AST_PTCR_CLK_CTRL_TYPEO_UNIT           (8)
> +#define AST_PTCR_CLK_CTRL_TYPEO_UNIT_MASK      (0xff << 8)
> +#define AST_PTCR_CLK_CTRL_TYPEO_H              (4)
> +#define AST_PTCR_CLK_CTRL_TYPEO_H_MASK         (0xf << 4)
> +#define AST_PTCR_CLK_CTRL_TYPEO_L              (0)
> +#define AST_PTCR_CLK_CTRL_TYPEO_L_MASK         (0xf)
> +
> +/* AST_PTCR_DUTY2_CTRL : 0x48 - Duty Control 2 Register */
> +#define DUTY_CTRL2_PWMF_FALL_POINT             (24)
> +#define DUTY_CTRL2_PWMF_FALL_POINT_MASK                (0xff << 24)
> +#define DUTY_CTRL2_PWMF_RISE_POINT             (16)
> +#define DUTY_CTRL2_PWMF_RISE_POINT_MASK                (0xff << 16)
> +#define DUTY_CTRL2_PWME_FALL_POINT             (8)
> +#define DUTY_CTRL2_PWME_FALL_POINT_MASK                (0xff << 8)
> +#define DUTY_CTRL2_PWME_RISE_POINT             (0)
> +#define DUTY_CTRL2_PWME_RISE_POINT_MASK                (0xff)
> +
> +/* AST_PTCR_DUTY3_CTRL : 0x4c - Duty Control 3 Register */
> +#define DUTY_CTRL3_PWMH_FALL_POINT             (24)
> +#define DUTY_CTRL3_PWMH_FALL_POINT_MASK                (0xff << 24)
> +#define DUTY_CTRL3_PWMH_RISE_POINT             (16)
> +#define DUTY_CTRL3_PWMH_RISE_POINT_MASK                (0xff << 16)
> +#define DUTY_CTRL3_PWMG_FALL_POINT             (8)
> +#define DUTY_CTRL3_PWMG_FALL_POINT_MASK                (0xff << 8)
> +#define DUTY_CTRL3_PWMG_RISE_POINT             (0)
> +#define DUTY_CTRL3_PWMG_RISE_POINT_MASK                (0xff)
> +
> +struct aspeed_pwm_driver_data {
> +       u32 (*get_pwm_clock)(void);
> +};
> +
> +struct aspeed_pwm_tacho_data {
> +       struct device                   *hwmon_dev;
> +       void __iomem                    *reg_base;
> +       int                             irq;
> +       struct aspeed_pwm_driver_data   *aspeed_pwm_data;
> +       const struct attribute_group    *groups[AST_PWM_GROUP_NUM + 1];
> +};
> +
> +struct aspeed_pwm_tacho_data *aspeed_pwm_tacho;
> +
> +static u8
> +aspeed_get_pwm_type(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho, u8 pwm_ch);
> +static u8
> +aspeed_get_pwm_en(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho, u8 pwm_ch);
> +static u8
> +aspeed_get_tacho_type_division(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                              u8 pwm_type);
> +static u16
> +aspeed_get_tacho_type_unit(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_type);
> +static u8
> +aspeed_get_pwm_clock_div_h(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_type);
> +static u8
> +aspeed_get_pwm_clock_div_l(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_type);
> +static u8
> +aspeed_get_pwm_clock_unit(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                         u8 pwm_type);

We don't need to forward declare static prototypes.

> +
> +static inline void
> +aspeed_pwm_tacho_write(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho, u32 val,
> +                      u32 reg)
> +{
> +       writel(val, aspeed_pwm_tacho->reg_base + reg);
> +}
> +
> +static inline u32
> +aspeed_pwm_tacho_read(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho, u32 reg)
> +{
> +       return readl(aspeed_pwm_tacho->reg_base + reg);
> +}
> +
> +/*
> + * (1) The PWM base clock = 24Mhz / (Clock_Division_H D[7:4] in PTCR04 *
> + *     Clock_Division_L D[3:0] in PTCR04)
> + * (2) The frequency of PWM = The PWM base clock / (PWM period D[15:8] in
> + *     PTCR04 + 1)
> + * (3) If a plan to output 25Khz PWM frequency and 10% step of duty cycle,
> + *     suggested to set 0x943 in PTCR04 register.
> + * The PWM frequency = 24Mhz / (16 * 6 * (9 + 1)) = 25Khz
> + * duty cycle settings in the PTCR08 register:
> + * 0x1e786008 D[15:0] = 0x0900, duty = 90%
> + * 0x1e786008 D[15:0] = 0x0902, duty = 70%
> + * ...
> + * 0x1e786008 D[15:0] = 0x0908, duty = 10%
> + * 0x1e786008 D[15:0] = 0x0909, duty = 100%
> + * 0x1e786008 D[15:0] = 0x0000, duty = 100%
> + * (falling) - (rising+1) /unit
> + */
> +static void aspeed_pwm_taco_init(void)
> +{
> +       /* Enable PWM TACH CLK
> +        * Set M/N/O out is 25Khz
> +        * The PWM frequency = 24Mhz / (16 * 6 * (9 + 1)) = 25Khz
> +        */
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x09430943,
> +                              AST_PTCR_CLK_CTRL);

This calculation needs to depend on the clock that is driving this
periperhal. See the upstream Aspeed watchdog driver for how to do
this.

> +#ifdef PWM_TYPE_O

Please no ifdefs. If it is a conditional feature, it must depend on a
device tree property.

> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x0943,
> +                              AST_PTCR_CLK_EXT_CTRL);
> +#endif
> +       /* FULL SPEED at initialize 100% pwm A~H */
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x0, AST_PTCR_DUTY0_CTRL);
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x0, AST_PTCR_DUTY1_CTRL);
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x0, AST_PTCR_DUTY2_CTRL);
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x0, AST_PTCR_DUTY3_CTRL);
> +
> +       /* Set TACO M/N/O initial unit 0x1000, falling , divide 4 , Enable */
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x10000001,
> +                              AST_PTCR_TYPEM_CTRL0);
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x10000001,
> +                              AST_PTCR_TYPEN_CTRL0);
> +#ifdef PWM_TYPE_O
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x10000001,
> +                              AST_PTCR_TYPEO_CTRL0);
> +#endif
> +
> +       /* TACO measure period = 24000000 / 2 / 2 / 256 / 4096 / 1 (only enable
> +        * 1 TACHO) = 5.72Hz, it means that software needs to wait at least 0.2
> +        * sec to get refreshed TACO value. If you will enable more TACO or
> +        * require faster response, you have to control the clock divisor and
> +        * the period to be smaller.
> +        *
> +        * Full Range to do measure unit 0x1000
> +        * PTCRM/N/O[3:1] = 0, Type M/N/O fan tach clock is div 4. -->
> +        * calculate RPM.
> +        */
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x10000000,
> +                              AST_PTCR_TYPEM_CTRL1);
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x10000000,
> +                              AST_PTCR_TYPEN_CTRL1);
> +#ifdef PWM_TYPE_O
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x10000000,
> +                              AST_PTCR_TYPEO_CTRL1);
> +#endif
> +
> +       /* TACO Source Selection, PWMA for fan0~15 */
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x0, AST_PTCR_TACH_SOURCE);
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x0,
> +                              AST_PTCR_TACH_SOURCE_EXT);
> +
> +       /* PWM A~D -> Disable , type M, Tacho 0~15 Disable, CLK source 24Mhz */
> +#ifdef MCLK
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, AST_PTCR_CTRL_CLK_MCLK |
> +                              AST_PTCR_CTRL_CLK_EN, AST_PTCR_CTRL);
> +#else
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, AST_PTCR_CTRL_CLK_EN,
> +                              AST_PTCR_CTRL);
> +#endif
> +}
> +
> +/* index 0 : clk_en , 1: clk_source */
> +static ssize_t aspeed_store_clk(struct device *dev,
> +                               struct device_attribute *attr,
> +                               const char *buf, size_t count)
> +{
> +       unsigned long input_val;
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +       int ret;
> +
> +       ret = kstrtoul(buf, 10, &input_val);
> +       if (ret)
> +               return ret;
> +
> +       if ((input_val > 1) || (input_val < 0))
> +               return -EINVAL;
> +
> +       switch (sensor_attr->nr) {
> +       case 0: /* clk_en */
> +               if (input_val)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                       AST_PTCR_CTRL) | AST_PTCR_CTRL_CLK_EN,
> +                                       AST_PTCR_CTRL);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                       AST_PTCR_CTRL) & ~AST_PTCR_CTRL_CLK_EN,
> +                                       AST_PTCR_CTRL);
> +               break;
> +
> +       case 1: /* clk_source */
> +               if (input_val)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                       AST_PTCR_CTRL) |
> +                                       AST_PTCR_CTRL_CLK_MCLK,
> +                                       AST_PTCR_CTRL);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                       AST_PTCR_CTRL) &
> +                                       ~AST_PTCR_CTRL_CLK_MCLK,
> +                                       AST_PTCR_CTRL);
> +               break;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return count;
> +
> +}
> +
> +
> +static ssize_t aspeed_show_clk(struct device *dev,
> +                              struct device_attribute *attr, char *buf)
> +{
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +
> +       switch (sensor_attr->nr)         {
> +       case 0: /* clk_en */
> +               if (AST_PTCR_CTRL_CLK_EN &
> +                   aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL))
> +                       return sprintf(buf, "1: Enable\n");
> +               else
> +                       return sprintf(buf, "0: Disable\n");
> +               break;
> +
> +       case 1: /* clk_source */
> +               if (AST_PTCR_CTRL_CLK_MCLK &
> +                   aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL))
> +                       return sprintf(buf, "1: MCLK\n");
> +               else
> +                       return sprintf(buf, "0: 24Mhz\n");
> +
> +               break;
> +
> +       default:
> +               return sprintf(buf, "error CLK Index\n");
> +       }
> +}
> +
> +static u32
> +aspeed_get_tacho_measure_period(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                               u8 pwm_type)
> +{
> +       u32 clk, clk_unit, div_h, div_l, tacho_unit, tacho_div;
> +
> +       if (AST_PTCR_CTRL_CLK_MCLK & aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                          AST_PTCR_CTRL))
> +               clk = aspeed_pwm_tacho->aspeed_pwm_data->get_pwm_clock();
> +       else
> +               clk = 24 * 1000 * 1000;
> +
> +       clk_unit = aspeed_get_pwm_clock_unit(aspeed_pwm_tacho, pwm_type);
> +       div_h = aspeed_get_pwm_clock_div_h(aspeed_pwm_tacho, pwm_type);
> +       div_h = 0x1 << div_h;
> +       div_l = aspeed_get_pwm_clock_div_l(aspeed_pwm_tacho, pwm_type);
> +       if (div_l == 0)
> +               div_l = 1;
> +       else
> +               div_l = div_l * 2;
> +
> +       tacho_unit = aspeed_get_tacho_type_unit(aspeed_pwm_tacho, pwm_type);
> +       tacho_div = aspeed_get_tacho_type_division(aspeed_pwm_tacho, pwm_type);
> +
> +       tacho_div = 0x4 << (tacho_div * 2);
> +
> +       return clk/(clk_unit * div_h * div_l * tacho_div * tacho_unit);
> +}
> +
> +static u8
> +aspeed_get_tacho_type_division(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                              u8 pwm_type)
> +{
> +       u32 tmp = 0;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEM_CTRL0);
> +               break;
> +
> +       case PWM_TYPE_N:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEN_CTRL0);
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEO_CTRL0);
> +               break;
> +
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +
> +       }
> +
> +       return ((tmp & TYPE_CTRL0_CLK_DIVISION_MASK) >>
> +               TYPE_CTRL0_CLK_DIVISION);
> +}
> +
> +static void
> +aspeed_set_tacho_type_division(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                              u8 pwm_type, u32 division)
> +{
> +       u32 tmp = 0;
> +
> +       if (division > 0x7)
> +               return;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEM_CTRL0);
> +               break;
> +
> +       case PWM_TYPE_N:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEN_CTRL0);
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEO_CTRL0);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +
> +       tmp &= ~TYPE_CTRL0_CLK_DIVISION_MASK;
> +       tmp |= (division << TYPE_CTRL0_CLK_DIVISION);
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_TYPEM_CTRL0);
> +               break;
> +       case PWM_TYPE_N:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_TYPEN_CTRL0);
> +               break;
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_TYPEO_CTRL0);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +}
> +
> +static u16
> +aspeed_get_tacho_type_unit(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_type)
> +{
> +       u32 tmp = 0;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEM_CTRL0);
> +               break;
> +       case PWM_TYPE_N:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEN_CTRL0);
> +               break;
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEO_CTRL0);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +
> +       return ((tmp & TYPE_CTRL0_FAN_PERIOD_MASK) >> TYPE_CTRL0_FAN_PERIOD);
> +}
> +
> +static void
> +aspeed_set_tacho_type_unit(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_type, u32 unit)
> +{
> +       u32 tmp = 0;
> +
> +       if (unit > 0xffff)
> +               return;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEM_CTRL0);
> +               break;
> +       case PWM_TYPE_N:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEN_CTRL0);
> +               break;
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEO_CTRL0);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +
> +       tmp &= ~TYPE_CTRL0_FAN_PERIOD_MASK;
> +       tmp |= (unit << TYPE_CTRL0_FAN_PERIOD);
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_TYPEM_CTRL0);
> +               break;
> +
> +       case PWM_TYPE_N:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_TYPEN_CTRL0);
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_TYPEO_CTRL0);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +}
> +
> +static u32
> +aspeed_get_tacho_type_mode(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_type)
> +{
> +       u32 tmp = 0;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEM_CTRL0);
> +               break;
> +       case PWM_TYPE_N:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEN_CTRL0);
> +               break;
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEO_CTRL0);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +
> +       return ((tmp & TYPE_CTRL0_FAN_MODE_MASK) >> TYPE_CTRL0_FAN_MODE);
> +}
> +
> +static void
> +aspeed_set_tacho_type_mode(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_type, u32 mode)
> +{
> +       u32 tmp = 0;
> +
> +       if (mode > 0x2)
> +               return;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEM_CTRL0);
> +               break;
> +
> +       case PWM_TYPE_N:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEN_CTRL0);
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEO_CTRL0);
> +               break;
> +
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +
> +       tmp &= ~TYPE_CTRL0_FAN_MODE_MASK;
> +       tmp |= (mode << TYPE_CTRL0_FAN_MODE);
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_TYPEM_CTRL0);
> +               break;
> +
> +       case PWM_TYPE_N:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_TYPEN_CTRL0);
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_TYPEO_CTRL0);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +
> +}
> +
> +static u8
> +aspeed_get_tacho_type_en(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                        u8 pwm_type)
> +{
> +       u8 tmp = 0;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = (TYPE_CTRL0_FAN_TYPE_EN &
> +                      aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_TYPEM_CTRL0));
> +               break;
> +
> +       case PWM_TYPE_N:
> +               tmp = (TYPE_CTRL0_FAN_TYPE_EN &
> +                     aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_TYPEN_CTRL0));
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = (TYPE_CTRL0_FAN_TYPE_EN &
> +                     aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                     AST_PTCR_TYPEO_CTRL0));
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +
> +       return tmp;
> +}
> +
> +static void
> +aspeed_set_tacho_type_en(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                        u8 pwm_type, u32 enable)
> +{
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                                      aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                             AST_PTCR_TYPEM_CTRL0) | enable,
> +                       AST_PTCR_TYPEM_CTRL0);
> +
> +               break;
> +       case PWM_TYPE_N:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_TYPEN_CTRL0)
> +               | enable, AST_PTCR_TYPEN_CTRL0);
> +
> +               break;
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_TYPEO_CTRL0) |
> +                                     enable, AST_PTCR_TYPEO_CTRL0);
> +
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +}
> +
> +static u32
> +aspeed_get_tacho_type_limit(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                           u8 pwm_type)
> +{
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               return (FAN_LIMIT_MASK &
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                             AST_PTCR_TYPEM_LIMIT));
> +
> +       case PWM_TYPE_N:
> +               return (FAN_LIMIT_MASK &
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                             AST_PTCR_TYPEN_LIMIT));
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               return (FAN_LIMIT_MASK &
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                             AST_PTCR_TYPEO_LIMIT));
> +
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +       return 0;
> +}
> +
> +static void
> +aspeed_set_tacho_type_limit(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                           u8 pwm_type, u32 limit)
> +{
> +       if (limit > FAN_LIMIT_MASK)
> +               return;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, limit,
> +                                      AST_PTCR_TYPEM_LIMIT);
> +               break;
> +       case PWM_TYPE_N:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, limit,
> +                                      AST_PTCR_TYPEN_LIMIT);
> +               break;
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, limit,
> +                                      AST_PTCR_TYPEO_LIMIT);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error type\n");
> +               break;
> +       }
> +}
> +
> +static u8
> +aspeed_get_tacho_alarm_en(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                         u8 tacho_ch)
> +{
> +       /* tacho source */
> +       if (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_INTR_CTRL) &
> +                                 INTR_CTRL_EN_NUM(tacho_ch))
> +               return 1;
> +
> +       return 0;
> +}
> +
> +static void
> +aspeed_set_tacho_alarm_en(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                         u8 tacho_ch, u8 enable)
> +{
> +       /* tacho source  */
> +       if (enable == 1)
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                             AST_PTCR_INTR_CTRL) |
> +                       INTR_CTRL_EN_NUM(tacho_ch), AST_PTCR_INTR_CTRL);
> +       else
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                             AST_PTCR_INTR_CTRL) &
> +                       ~(INTR_CTRL_EN_NUM(tacho_ch)), AST_PTCR_INTR_CTRL);
> +}
> +
> +static u8
> +aspeed_get_tacho_alarm(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                      u8 tacho_ch)
> +{
> +       /* tacho source */
> +       if (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_INTR_STS) &
> +                                 INTR_CTRL_NUM(tacho_ch))
> +               return 1;
> +
> +       return 0;
> +}
> +
> +static u8
> +aspeed_get_tacho_en(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho, u8 tacho_ch)
> +{
> +       if (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL) &
> +                                 AST_PTCR_CTRL_FAN_NUM_EN(tacho_ch))
> +               return 1;
> +
> +       return 0;
> +}
> +
> +static void
> +aspeed_set_tacho_en(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                   u8 tacho_ch, u8 enable)
> +{
> +       /* tacho number enable */
> +       if (enable)
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL) |
> +                       AST_PTCR_CTRL_FAN_NUM_EN(tacho_ch), AST_PTCR_CTRL);
> +       else
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL) &
> +                       ~(AST_PTCR_CTRL_FAN_NUM_EN(tacho_ch)), AST_PTCR_CTRL);
> +}
> +
> +static u8
> +aspeed_get_tacho_source(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                       u8 tacho_ch)
> +{
> +       u32 tmp1, tmp2;
> +
> +       /* tacho source */
> +       tmp1 = aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_TACH_SOURCE);
> +       tmp1 &= TACH_PWM_SOURCE_MASK_BIT01(tacho_ch);
> +       tmp1 = tmp1 >> (TACH_PWM_SOURCE_BIT01(tacho_ch));
> +       tmp2 = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                    AST_PTCR_TACH_SOURCE_EXT);
> +       tmp2 &= TACH_PWM_SOURCE_MASK_BIT2(tacho_ch);
> +       tmp2 = tmp2 >> (TACH_PWM_SOURCE_BIT2(tacho_ch));
> +       tmp2 = tmp2 << 2;
> +
> +       return (tmp2 | tmp1);
> +}
> +
> +static void
> +aspeed_set_tacho_source(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                       u8 tacho_ch, u8 tacho_source)
> +{
> +       u32 tmp1, tmp2;
> +
> +       if (tacho_source > 7)
> +               return;
> +
> +       /* tacho source */
> +       tmp1 = aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_TACH_SOURCE);
> +       tmp1 &= ~(TACH_PWM_SOURCE_MASK_BIT01(tacho_ch));
> +       tmp1 |= ((tacho_source & 0x3) << (TACH_PWM_SOURCE_BIT01(tacho_ch)));
> +
> +       tmp2 = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                    AST_PTCR_TACH_SOURCE_EXT);
> +       tmp2 &= ~(TACH_PWM_SOURCE_MASK_BIT2(tacho_ch));
> +       tmp2 |= (((tacho_source & 0x4) >> 2) <<
> +               (TACH_PWM_SOURCE_BIT2(tacho_ch)));
> +
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp1, AST_PTCR_TACH_SOURCE);
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp2,
> +                              AST_PTCR_TACH_SOURCE_EXT);
> +
> +}
> +
> +static u32
> +aspeed_get_tacho_rpm(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                    u8 tacho_ch)
> +{
> +       u32 raw_data, rpm, tacho_clk_div, clk_source, timeout = 0;
> +       u8 tacho_source, pwm_type, tacho_type_en;
> +
> +       if (!(aspeed_get_tacho_en(aspeed_pwm_tacho, tacho_ch)))
> +               return 0;
> +
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0, AST_PTCR_TRIGGER);
> +       aspeed_pwm_tacho_write(aspeed_pwm_tacho, 0x1 << tacho_ch,
> +                              AST_PTCR_TRIGGER);
> +
> +       tacho_source = aspeed_get_tacho_source(aspeed_pwm_tacho, tacho_ch);
> +       pwm_type = aspeed_get_pwm_type(aspeed_pwm_tacho, tacho_source);
> +       tacho_type_en = aspeed_get_tacho_type_en(aspeed_pwm_tacho, pwm_type);
> +
> +       /* check pwm_type and get clock division */
> +       if (!tacho_type_en)
> +               return 0;
> +
> +       /* Wait ready */
> +       while (!(aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_RESULT) &
> +               (0x1 << RESULT_STATUS))) {
> +               timeout++;
> +               if (timeout > 25)
> +                       return 0;
> +       };
> +
> +       raw_data = aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_RESULT) &
> +                  RESULT_VALUE_MASK;
> +       tacho_clk_div = aspeed_get_tacho_type_division(aspeed_pwm_tacho,
> +                                                      pwm_type);
> +
> +       tacho_clk_div = 0x4 << (tacho_clk_div*2);
> +
> +       if (AST_PTCR_CTRL_CLK_MCLK & aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                          AST_PTCR_CTRL))
> +               clk_source = 166 * 1000 * 1000;
> +       else
> +               clk_source = 24 * 1000 * 1000;
> +
> +       rpm = (clk_source * 60) / (2 * raw_data * tacho_clk_div);
> +
> +       return rpm;
> +}
> +
> +static u8
> +aspeed_get_pwm_clock_div_h(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_type)
> +{
> +       u8 tmp = 0;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CLK_CTRL) &
> +                     AST_PTCR_CLK_CTRL_TYPEM_H_MASK) >>
> +                     AST_PTCR_CLK_CTRL_TYPEM_H;
> +               break;
> +
> +       case PWM_TYPE_N:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CLK_CTRL) &
> +                      AST_PTCR_CLK_CTRL_TYPEN_H_MASK) >>
> +                      AST_PTCR_CLK_CTRL_TYPEN_H;
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CLK_EXT_CTRL) &
> +                      AST_PTCR_CLK_CTRL_TYPEO_H_MASK) >>
> +                      AST_PTCR_CLK_CTRL_TYPEO_H;
> +               break;
> +#endif
> +
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel aspeed_get_pwm_clock_div_h %d\n",
> +                       pwm_type);
> +               break;
> +       }
> +       return tmp;
> +}
> +
> +static void
> +aspeed_set_pwm_clock_division_h(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                               u8 pwm_type, u8 div_high)
> +{
> +       if (div_high > 0xf)
> +               return;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CLK_CTRL) &
> +               ~AST_PTCR_CLK_CTRL_TYPEM_H_MASK) |
> +               (div_high << AST_PTCR_CLK_CTRL_TYPEM_H), AST_PTCR_CLK_CTRL);
> +               break;
> +
> +       case PWM_TYPE_N:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CLK_CTRL) &
> +               ~AST_PTCR_CLK_CTRL_TYPEN_H_MASK) |
> +               (div_high << AST_PTCR_CLK_CTRL_TYPEN_H), AST_PTCR_CLK_CTRL);
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                      AST_PTCR_CLK_EXT_CTRL) &
> +               ~AST_PTCR_CLK_CTRL_TYPEO_H_MASK) |
> +               (div_high << AST_PTCR_CLK_CTRL_TYPEO_H),
> +               AST_PTCR_CLK_EXT_CTRL);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel aspeed_get_pwm_type %d\n",
> +                       pwm_type);
> +               break;
> +       }
> +
> +}
> +
> +static u8
> +aspeed_get_pwm_clock_div_l(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_type)
> +{
> +       u8 tmp = 0;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CLK_CTRL) &
> +                     AST_PTCR_CLK_CTRL_TYPEM_L_MASK) >>
> +                     AST_PTCR_CLK_CTRL_TYPEM_L;
> +               break;
> +
> +       case PWM_TYPE_N:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CLK_CTRL) &
> +                     AST_PTCR_CLK_CTRL_TYPEN_L_MASK) >>
> +                     AST_PTCR_CLK_CTRL_TYPEN_L;
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CLK_EXT_CTRL) &
> +                      AST_PTCR_CLK_CTRL_TYPEO_L_MASK) >>
> +                      AST_PTCR_CLK_CTRL_TYPEO_L;
> +               break;
> +
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel aspeed_get_pwm_clock_div_l %d\n",
> +                       pwm_type);
> +               break;
> +       }
> +       return tmp;
> +}
> +
> +static void
> +aspeed_set_pwm_clock_division_l(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                               u8 pwm_type, u8 div_low)
> +{
> +       if (div_low > 0xf)
> +               return;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CLK_CTRL) &
> +               ~AST_PTCR_CLK_CTRL_TYPEM_L_MASK) |
> +               (div_low << AST_PTCR_CLK_CTRL_TYPEM_L), AST_PTCR_CLK_CTRL);
> +               break;
> +
> +       case PWM_TYPE_N:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CLK_CTRL) &
> +               ~AST_PTCR_CLK_CTRL_TYPEN_L_MASK) |
> +               (div_low << AST_PTCR_CLK_CTRL_TYPEN_L), AST_PTCR_CLK_CTRL);
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                      AST_PTCR_CLK_EXT_CTRL) &
> +               ~AST_PTCR_CLK_CTRL_TYPEO_L_MASK) |
> +               (div_low << AST_PTCR_CLK_CTRL_TYPEO_L), AST_PTCR_CLK_EXT_CTRL);
> +               break;
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel aspeed_get_pwm_type %d\n",
> +                       pwm_type);
> +               break;
> +       }
> +}
> +
> +static u8
> +aspeed_get_pwm_clock_unit(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                         u8 pwm_type)
> +{
> +       u8 tmp = 0;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CLK_CTRL) &
> +                     AST_PTCR_CLK_CTRL_TYPEM_UNIT_MASK) >>
> +                     AST_PTCR_CLK_CTRL_TYPEM_UNIT;
> +               break;
> +
> +       case PWM_TYPE_N:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CLK_CTRL) &
> +                     AST_PTCR_CLK_CTRL_TYPEN_UNIT_MASK) >>
> +                     AST_PTCR_CLK_CTRL_TYPEN_UNIT;
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CLK_EXT_CTRL) &
> +                      AST_PTCR_CLK_CTRL_TYPEO_UNIT_MASK) >>
> +                      AST_PTCR_CLK_CTRL_TYPEO_UNIT;
> +               break;
> +
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel aspeed_get_pwm_clock_unit %d\n",
> +                       pwm_type);
> +               break;
> +       }
> +
> +       return tmp;
> +}
> +
> +static void
> +aspeed_set_pwm_clock_unit(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                         u8 pwm_type, u8 unit)
> +{
> +       if (unit > 0xff)
> +               return;
> +
> +       switch (pwm_type) {
> +       case PWM_TYPE_M:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CLK_CTRL) &
> +               ~AST_PTCR_CLK_CTRL_TYPEM_UNIT_MASK) |
> +               (unit << AST_PTCR_CLK_CTRL_TYPEM_UNIT),
> +               AST_PTCR_CLK_CTRL);
> +               break;
> +
> +       case PWM_TYPE_N:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CLK_CTRL) &
> +               ~AST_PTCR_CLK_CTRL_TYPEN_UNIT_MASK) |
> +               (unit << AST_PTCR_CLK_CTRL_TYPEN_UNIT),
> +               AST_PTCR_CLK_CTRL);
> +               break;
> +
> +#ifdef PWM_TYPE_O
> +       case PWM_TYPE_O:
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +               AST_PTCR_CLK_EXT_CTRL) &
> +               ~AST_PTCR_CLK_CTRL_TYPEO_UNIT_MASK) |
> +               (unit << AST_PTCR_CLK_CTRL_TYPEO_UNIT),
> +               AST_PTCR_CLK_EXT_CTRL);
> +               break;
> +
> +#endif
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel aspeed_get_pwm_type %d\n",
> +                       pwm_type);
> +               break;
> +       }
> +}
> +
> +static u32
> +aspeed_get_pwm_clock(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                    u8 pwm_type)
> +{
> +       u32 unit, div_low, div_high, clk_source;
> +
> +       unit = aspeed_get_pwm_clock_unit(aspeed_pwm_tacho, pwm_type);
> +
> +       div_high = aspeed_get_pwm_clock_div_h(aspeed_pwm_tacho, pwm_type);
> +       div_high = (0x1 << div_high);
> +
> +       div_low = aspeed_get_pwm_clock_div_l(aspeed_pwm_tacho, pwm_type);
> +       if (div_low == 0)
> +               div_low = 1;
> +       else
> +               div_low = div_low*2;
> +
> +       if (AST_PTCR_CTRL_CLK_MCLK & aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +           AST_PTCR_CTRL))
> +               clk_source = aspeed_pwm_tacho->aspeed_pwm_data->get_pwm_clock();
> +       else
> +               clk_source = 24 * 1000 * 1000;
> +
> +       return (clk_source / (div_high * div_low * (unit + 1)));
> +}
> +
> +static u8
> +aspeed_get_pwm_en(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho, u8 pwm_ch)
> +{
> +       u8 tmp = 0;
> +
> +       switch (pwm_ch) {
> +       case PWMA:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL) &
> +                     AST_PTCR_CTRL_PMWA_EN) >> AST_PTCR_CTRL_PMWA;
> +               break;
> +
> +       case PWMB:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL) &
> +                     AST_PTCR_CTRL_PMWB_EN) >> AST_PTCR_CTRL_PMWB;
> +               break;
> +
> +       case PWMC:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL) &
> +                     AST_PTCR_CTRL_PMWC_EN) >> AST_PTCR_CTRL_PMWC;
> +               break;
> +
> +       case PWMD:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL) &
> +                     AST_PTCR_CTRL_PMWD_EN) >> AST_PTCR_CTRL_PMWD;
> +               break;
> +
> +       case PWME:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CTRL_EXT) &
> +                     AST_PTCR_CTRL_PMWE_EN) >> AST_PTCR_CTRL_PMWE;
> +               break;
> +
> +       case PWMF:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CTRL_EXT) &
> +                     AST_PTCR_CTRL_PMWF_EN) >> AST_PTCR_CTRL_PMWF;
> +               break;
> +
> +       case PWMG:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CTRL_EXT) &
> +                     AST_PTCR_CTRL_PMWG_EN) >> AST_PTCR_CTRL_PMWG;
> +               break;
> +
> +       case PWMH:
> +               tmp = (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                            AST_PTCR_CTRL_EXT) &
> +                     AST_PTCR_CTRL_PMWH_EN) >> AST_PTCR_CTRL_PMWH;
> +               break;
> +
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel aspeed_get_pwm_type %d\n",
> +                       pwm_ch);
> +               break;
> +       }
> +
> +       return tmp;
> +
> +}
> +
> +static void
> +aspeed_set_pwm_en(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho, u8 pwm_ch,
> +                 u8 enable)
> +{
> +       switch (pwm_ch) {
> +       case PWMA:
> +               if (enable)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                    AST_PTCR_CTRL) |
> +                               AST_PTCR_CTRL_PMWA_EN, AST_PTCR_CTRL);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                     AST_PTCR_CTRL) &
> +                               ~AST_PTCR_CTRL_PMWA_EN, AST_PTCR_CTRL);
> +
> +               break;
> +
> +       case PWMB:
> +               if (enable)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL) |
> +                               AST_PTCR_CTRL_PMWB_EN), AST_PTCR_CTRL);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL) &
> +                               ~AST_PTCR_CTRL_PMWB_EN), AST_PTCR_CTRL);
> +               break;
> +
> +       case PWMC:
> +               if (enable)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL) |
> +                               AST_PTCR_CTRL_PMWC_EN), AST_PTCR_CTRL);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL) &
> +                               ~AST_PTCR_CTRL_PMWC_EN), AST_PTCR_CTRL);
> +
> +               break;
> +
> +       case PWMD:
> +               if (enable)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL) |
> +                               AST_PTCR_CTRL_PMWD_EN), AST_PTCR_CTRL);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL) &
> +                               ~AST_PTCR_CTRL_PMWD_EN), AST_PTCR_CTRL);
> +
> +               break;
> +
> +       case PWME:
> +               if (enable)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL_EXT) |
> +                               AST_PTCR_CTRL_PMWE_EN), AST_PTCR_CTRL_EXT);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL_EXT) &
> +                               ~AST_PTCR_CTRL_PMWE_EN), AST_PTCR_CTRL_EXT);
> +
> +               break;
> +
> +       case PWMF:
> +               if (enable)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL_EXT) |
> +                               AST_PTCR_CTRL_PMWF_EN), AST_PTCR_CTRL_EXT);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL_EXT) &
> +                               ~AST_PTCR_CTRL_PMWF_EN), AST_PTCR_CTRL_EXT);
> +
> +               break;
> +
> +       case PWMG:
> +               if (enable)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL_EXT) |
> +                               AST_PTCR_CTRL_PMWG_EN), AST_PTCR_CTRL_EXT);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL_EXT) &
> +                               ~AST_PTCR_CTRL_PMWG_EN), AST_PTCR_CTRL_EXT);
> +
> +               break;
> +
> +       case PWMH:
> +               if (enable)
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL_EXT) |
> +                               AST_PTCR_CTRL_PMWH_EN), AST_PTCR_CTRL_EXT);
> +               else
> +                       aspeed_pwm_tacho_write(aspeed_pwm_tacho,
> +                               (aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                                      AST_PTCR_CTRL_EXT) &
> +                               ~AST_PTCR_CTRL_PMWH_EN), AST_PTCR_CTRL_EXT);
> +
> +               break;
> +
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel aspeed_get_pwm_type %d\n",
> +                       pwm_ch);
> +               break;
> +       }
> +}
> +
> +static u8
> +aspeed_get_pwm_type(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho, u8 pwm_ch)
> +{
> +       u8 tmp = 0;
> +
> +       switch (pwm_ch) {
> +       case PWMA:
> +               tmp = AST_PTCR_CTRL_GET_PWMA_TYPE(
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                       AST_PTCR_CTRL));
> +               break;
> +
> +       case PWMB:
> +               tmp = AST_PTCR_CTRL_GET_PWMB_TYPE(
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                       AST_PTCR_CTRL));
> +               break;
> +
> +       case PWMC:
> +               tmp = AST_PTCR_CTRL_GET_PWMC_TYPE(
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                       AST_PTCR_CTRL));
> +               break;
> +
> +       case PWMD:
> +               tmp = AST_PTCR_CTRL_GET_PWMD_TYPE(
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                       AST_PTCR_CTRL));
> +               break;
> +
> +       case PWME:
> +               tmp = AST_PTCR_CTRL_GET_PWME_TYPE(
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                       AST_PTCR_CTRL_EXT));
> +               break;
> +
> +       case PWMF:
> +               tmp = AST_PTCR_CTRL_GET_PWMF_TYPE(
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                       AST_PTCR_CTRL_EXT));
> +               break;
> +
> +       case PWMG:
> +               tmp = AST_PTCR_CTRL_GET_PWMG_TYPE(
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                       AST_PTCR_CTRL_EXT));
> +               break;
> +
> +       case PWMH:
> +               tmp = AST_PTCR_CTRL_GET_PWMH_TYPE(
> +                       aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                       AST_PTCR_CTRL_EXT));
> +               break;
> +
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel aspeed_get_pwm_type %d\n",
> +                       pwm_ch);
> +               break;
> +       }
> +
> +       return tmp;
> +}
> +
> +static void
> +aspeed_set_pwm_type(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                   u8 pwm_ch, u8 type)
> +{
> +       u32 tmp1, tmp2;
> +
> +       if (type > 0x2)
> +               return;
> +
> +       tmp1 = aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL);
> +       tmp2 = aspeed_pwm_tacho_read(aspeed_pwm_tacho, AST_PTCR_CTRL_EXT);
> +
> +       switch (pwm_ch) {
> +       case PWMA:
> +               tmp1 &= ~AST_PTCR_CTRL_SET_PWMA_TYPE_MASK;
> +               tmp1 |= AST_PTCR_CTRL_SET_PWMA_TYPE(type);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp1, AST_PTCR_CTRL);
> +               break;
> +
> +       case PWMB:
> +               tmp1 &= ~AST_PTCR_CTRL_SET_PWMB_TYPE_MASK;
> +               tmp1 |= AST_PTCR_CTRL_SET_PWMB_TYPE(type);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp1, AST_PTCR_CTRL);
> +               break;
> +
> +       case PWMC:
> +               tmp1 &= ~AST_PTCR_CTRL_SET_PWMC_TYPE_MASK;
> +               tmp1 |= AST_PTCR_CTRL_SET_PWMC_TYPE(type);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp1, AST_PTCR_CTRL);
> +               break;
> +       case PWMD:
> +               tmp1 &= ~AST_PTCR_CTRL_SET_PWMD_TYPE_MASK;
> +               tmp1 |= AST_PTCR_CTRL_SET_PWMD_TYPE(type);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp1, AST_PTCR_CTRL);
> +               break;
> +
> +       case PWME:
> +               tmp2 &= ~AST_PTCR_CTRL_SET_PWME_TYPE_MASK;
> +               tmp2 |= AST_PTCR_CTRL_SET_PWME_TYPE(type);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp2,
> +                                      AST_PTCR_CTRL_EXT);
> +               break;
> +
> +       case PWMF:
> +               tmp2 &= ~AST_PTCR_CTRL_SET_PWMF_TYPE_MASK;
> +               tmp2 |= AST_PTCR_CTRL_SET_PWMF_TYPE(type);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp2,
> +                                      AST_PTCR_CTRL_EXT);
> +               break;
> +
> +       case PWMG:
> +               tmp2 &= ~AST_PTCR_CTRL_SET_PWMG_TYPE_MASK;
> +               tmp2 |= AST_PTCR_CTRL_SET_PWMG_TYPE(type);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp2,
> +                                      AST_PTCR_CTRL_EXT);
> +               break;
> +
> +       case PWMH:
> +               tmp2 &= ~AST_PTCR_CTRL_SET_PWMH_TYPE_MASK;
> +               tmp2 |= AST_PTCR_CTRL_SET_PWMH_TYPE(type);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp2,
> +                                      AST_PTCR_CTRL_EXT);
> +               break;
> +
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error channel %d\n",
> +                       pwm_ch);
> +               break;
> +       }
> +}
> +
> +static u8
> +aspeed_get_pwm_duty_rising(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_ch)
> +{
> +       u32 tmp = 0;
> +
> +       switch (pwm_ch) {
> +       case PWMA:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY0_CTRL);
> +               tmp &= DUTY_CTRL0_PWMA_RISE_POINT_MASK;
> +               break;
> +
> +       case PWMB:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY0_CTRL);
> +               tmp &= DUTY_CTRL0_PWMB_RISE_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL0_PWMB_RISE_POINT);
> +               break;
> +
> +       case PWMC:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY1_CTRL);
> +               tmp &= DUTY_CTRL1_PWMC_RISE_POINT_MASK;
> +               break;
> +       case PWMD:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY1_CTRL);
> +               tmp &= DUTY_CTRL1_PWMD_RISE_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL1_PWMD_RISE_POINT);
> +               break;
> +
> +       case PWME:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY2_CTRL);
> +               tmp &= DUTY_CTRL2_PWME_RISE_POINT_MASK;
> +               break;
> +
> +       case PWMF:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY2_CTRL);
> +               tmp &= DUTY_CTRL2_PWMF_RISE_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL2_PWMF_RISE_POINT);
> +               break;
> +
> +       case PWMG:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY3_CTRL);
> +               tmp &= DUTY_CTRL3_PWMG_RISE_POINT_MASK;
> +               break;
> +
> +       case PWMH:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY3_CTRL);
> +               tmp &= DUTY_CTRL3_PWMH_RISE_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL3_PWMH_RISE_POINT);
> +               break;
> +
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error pwm channel %d with duty R\n",
> +                       pwm_ch);
> +               break;
> +       }
> +
> +       return tmp;
> +}
> +
> +static void
> +aspeed_set_pwm_duty_rising(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                          u8 pwm_ch, u8 rising)
> +{
> +       u32 tmp = 0;
> +       u32 pwm_type = aspeed_get_pwm_type(aspeed_pwm_tacho, pwm_ch);
> +
> +       if ((rising > 0xff) || (rising >
> +           aspeed_get_pwm_clock_unit(aspeed_pwm_tacho, pwm_type)))
> +               return;
> +
> +       switch (pwm_ch) {
> +       case PWMA:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY0_CTRL);
> +               tmp &= ~DUTY_CTRL0_PWMA_RISE_POINT_MASK;
> +               tmp |= rising;
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY0_CTRL);
> +               break;
> +
> +       case PWMB:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY0_CTRL);
> +               tmp &= ~DUTY_CTRL0_PWMB_RISE_POINT_MASK;
> +               tmp |= (rising << DUTY_CTRL0_PWMB_RISE_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY0_CTRL);
> +               break;
> +
> +       case PWMC:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY1_CTRL);
> +               tmp &= ~DUTY_CTRL1_PWMC_RISE_POINT_MASK;
> +               tmp |= rising;
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY1_CTRL);
> +               break;
> +
> +       case PWMD:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY1_CTRL);
> +               tmp &= ~DUTY_CTRL1_PWMD_RISE_POINT_MASK;
> +               tmp |= (rising << DUTY_CTRL1_PWMD_RISE_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY1_CTRL);
> +               break;
> +
> +       case PWME:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY2_CTRL);
> +               tmp &= ~DUTY_CTRL2_PWME_RISE_POINT_MASK;
> +               tmp |= rising;
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY2_CTRL);
> +               break;
> +
> +       case PWMF:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY2_CTRL);
> +               tmp &= ~DUTY_CTRL2_PWMF_RISE_POINT_MASK;
> +               tmp |= (rising << DUTY_CTRL2_PWMF_RISE_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY2_CTRL);
> +               break;
> +
> +       case PWMG:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY3_CTRL);
> +               tmp &= ~DUTY_CTRL3_PWMG_RISE_POINT_MASK;
> +               tmp |= rising;
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY3_CTRL);
> +               break;
> +
> +       case PWMH:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY3_CTRL);
> +               tmp &= ~DUTY_CTRL3_PWMH_RISE_POINT_MASK;
> +               tmp |= (rising << DUTY_CTRL3_PWMH_RISE_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY3_CTRL);
> +               break;
> +
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error pwm channel %d with duty\n",
> +                       pwm_ch);
> +               break;
> +       }
> +}
> +
> +static u8
> +aspeed_get_pwm_duty_falling(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                           u8 pwm_ch)
> +{
> +       u32 tmp = 0;
> +
> +       switch (pwm_ch) {
> +       case PWMA:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY0_CTRL);
> +               tmp &= DUTY_CTRL0_PWMA_FALL_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL0_PWMA_FALL_POINT);
> +               break;
> +
> +       case PWMB:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY0_CTRL);
> +               tmp &= DUTY_CTRL0_PWMB_FALL_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL0_PWMB_FALL_POINT);
> +               break;
> +
> +       case PWMC:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY1_CTRL);
> +               tmp &= DUTY_CTRL1_PWMC_FALL_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL1_PWMC_FALL_POINT);
> +               break;
> +
> +       case PWMD:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY1_CTRL);
> +               tmp &= DUTY_CTRL1_PWMD_FALL_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL1_PWMD_FALL_POINT);
> +               break;
> +       case PWME:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY2_CTRL);
> +               tmp &= DUTY_CTRL2_PWME_FALL_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL2_PWME_FALL_POINT);
> +               break;
> +
> +       case PWMF:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY2_CTRL);
> +               tmp &= DUTY_CTRL2_PWMF_FALL_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL2_PWMF_FALL_POINT);
> +               break;
> +
> +       case PWMG:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY3_CTRL);
> +               tmp &= DUTY_CTRL3_PWMG_FALL_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL3_PWMG_FALL_POINT);
> +               break;
> +
> +       case PWMH:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY3_CTRL);
> +               tmp &= DUTY_CTRL3_PWMH_FALL_POINT_MASK;
> +               tmp = (tmp >> DUTY_CTRL3_PWMH_FALL_POINT);
> +               break;
> +
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error pwm channel %d with duty F\n",
> +                       pwm_ch);
> +               break;
> +       }
> +
> +       return tmp;
> +}
> +
> +static void
> +aspeed_set_pwm_duty_falling(struct aspeed_pwm_tacho_data *aspeed_pwm_tacho,
> +                           u8 pwm_ch, u8 falling)
> +{
> +       u32 tmp = 0;
> +       u32 pwm_type = aspeed_get_pwm_type(aspeed_pwm_tacho, pwm_ch);
> +
> +       if ((falling > 0xff) || (falling >
> +           aspeed_get_pwm_clock_unit(aspeed_pwm_tacho, pwm_type)))
> +               return;
> +
> +       switch (pwm_ch) {
> +       case PWMA:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY0_CTRL);
> +               tmp &= ~DUTY_CTRL0_PWMA_FALL_POINT_MASK;
> +               tmp |= (falling << DUTY_CTRL0_PWMA_FALL_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY0_CTRL);
> +               break;
> +
> +       case PWMB:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY0_CTRL);
> +               tmp &= ~DUTY_CTRL0_PWMB_FALL_POINT_MASK;
> +               tmp |= (falling << DUTY_CTRL0_PWMB_FALL_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY0_CTRL);
> +               break;
> +
> +       case PWMC:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY1_CTRL);
> +               tmp &= ~DUTY_CTRL1_PWMC_FALL_POINT_MASK;
> +               tmp |= (falling << DUTY_CTRL1_PWMC_FALL_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY1_CTRL);
> +               break;
> +
> +       case PWMD:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY1_CTRL);
> +               tmp &= ~DUTY_CTRL1_PWMD_FALL_POINT_MASK;
> +               tmp |= (falling << DUTY_CTRL1_PWMD_FALL_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY1_CTRL);
> +               break;
> +
> +       case PWME:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY2_CTRL);
> +               tmp &= ~DUTY_CTRL2_PWME_FALL_POINT_MASK;
> +               tmp |= (falling << DUTY_CTRL2_PWME_FALL_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY2_CTRL);
> +               break;
> +
> +       case PWMF:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY2_CTRL);
> +               tmp &= ~DUTY_CTRL2_PWMF_FALL_POINT_MASK;
> +               tmp |= (falling << DUTY_CTRL2_PWMF_FALL_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY2_CTRL);
> +               break;
> +
> +       case PWMG:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY3_CTRL);
> +               tmp &= ~DUTY_CTRL3_PWMG_FALL_POINT_MASK;
> +               tmp |= (falling << DUTY_CTRL3_PWMG_FALL_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY3_CTRL);
> +               break;
> +
> +       case PWMH:
> +               tmp = aspeed_pwm_tacho_read(aspeed_pwm_tacho,
> +                                           AST_PTCR_DUTY3_CTRL);
> +               tmp &= ~DUTY_CTRL3_PWMH_FALL_POINT_MASK;
> +               tmp |= (falling << DUTY_CTRL3_PWMH_FALL_POINT);
> +               aspeed_pwm_tacho_write(aspeed_pwm_tacho, tmp,
> +                                      AST_PTCR_DUTY3_CTRL);
> +               break;
> +
> +       default:
> +               dev_err(aspeed_pwm_tacho->hwmon_dev, "error pwm channel %d with duty\n",
> +                       pwm_ch);
> +               break;
> +       }
> +
> +}
> +
> +/* PWM M/N/O Type sysfs
> + *
> + * Macro defining SENSOR_DEVICE_ATTR for a pwm sysfs entries.
> + * 0 - show/store unit
> + * 1 - show/store division_l
> + * 2 - show/store division_h
> + */
> +static ssize_t
> +aspeed_show_pwm_type_clock(struct device *dev, struct device_attribute *attr,
> +                          char *buf)
> +{
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +
> +       switch (sensor_attr->nr) {
> +       case 0: /* unit : 0~256 */
> +               return sprintf(buf, "%d (0~255)\n",
> +                              aspeed_get_pwm_clock_unit(aspeed_pwm_tacho,
> +                                               sensor_attr->index));
> +
> +       case 1: /* division_l */
> +               return sprintf(buf, "%d (0~15)\n",
> +                              aspeed_get_pwm_clock_div_l(aspeed_pwm_tacho,
> +                                                         sensor_attr->index));
> +
> +       case 2: /* division_h */
> +               return sprintf(buf, "%d (0~15)\n",
> +                              aspeed_get_pwm_clock_div_h(aspeed_pwm_tacho,
> +                                                         sensor_attr->index));
> +
> +       case 3: /* expect clock */
> +               return sprintf(buf, "%d \n",
> +                              aspeed_get_pwm_clock(aspeed_pwm_tacho,
> +                                                   sensor_attr->index));
> +
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return sprintf(buf, "%d : %d\n", sensor_attr->nr, sensor_attr->index);
> +}
> +
> +static ssize_t
> +aspeed_store_pwm_type_clock(struct device *dev, struct device_attribute *attr,
> +                           const char *buf, size_t count)
> +{
> +       unsigned long input_val;
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +       int ret;
> +
> +       ret = kstrtoul(buf, 10, &input_val);
> +       if (ret)
> +               return ret;
> +
> +       switch (sensor_attr->nr) {
> +       case 0: /* unit : 0~256 */
> +               aspeed_set_pwm_clock_unit(aspeed_pwm_tacho, sensor_attr->index,
> +                                         input_val);
> +               break;
> +
> +       case 1: /* division_l */
> +               aspeed_set_pwm_clock_division_l(aspeed_pwm_tacho,
> +                                               sensor_attr->index, input_val);
> +               break;
> +
> +       case 2: /* division_h */
> +               aspeed_set_pwm_clock_division_h(aspeed_pwm_tacho,
> +                                               sensor_attr->index, input_val);
> +               break;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return count;
> +}
> +
> +/* attr
> + * 0 - show/store enable
> + * 1 - show/store type
> + * 2 - show/store falling
> + * 3 - show/store rising
> + */
> +static ssize_t
> +aspeed_show_pwm_speed(struct device *dev, struct device_attribute *attr,
> +                     char *buf)
> +{
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +
> +       switch (sensor_attr->nr) {
> +       case 0: /* enable, disable */
> +               return sprintf(buf, "%d : %s\n",
> +                              aspeed_get_pwm_en(aspeed_pwm_tacho,
> +                                                sensor_attr->index),
> +                              aspeed_get_pwm_en(aspeed_pwm_tacho,
> +                                                sensor_attr->index) ?
> +                              "Enable":"Disable");
> +
> +       case 1: /* pwm type M/N/O */
> +               return sprintf(buf, "%d (0:M/1:N/2:O)\n",
> +                              aspeed_get_pwm_type(aspeed_pwm_tacho,
> +                                                  sensor_attr->index));
> +
> +       case 2: /* rising */
> +               return sprintf(buf, "%x : unit limit (0~%d)\n",
> +                              aspeed_get_pwm_duty_rising(aspeed_pwm_tacho,
> +                                                         sensor_attr->index),
> +                              aspeed_get_pwm_clock_unit(aspeed_pwm_tacho,
> +                                       aspeed_get_pwm_type(aspeed_pwm_tacho,
> +                                               sensor_attr->index)));
> +
> +       case 3: /* falling */
> +               return sprintf(buf, "%x : unit limit (0~%d)\n",
> +                              aspeed_get_pwm_duty_falling(aspeed_pwm_tacho,
> +                                                          sensor_attr->index),
> +                              aspeed_get_pwm_clock_unit(aspeed_pwm_tacho,
> +                                       aspeed_get_pwm_type(aspeed_pwm_tacho,
> +                                               sensor_attr->index)));
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static ssize_t
> +aspeed_store_pwm_speed(struct device *dev, struct device_attribute *attr,
> +                      const char *buf, size_t count)
> +{
> +       unsigned long input_val;
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +       int ret;
> +
> +       ret = kstrtoul(buf, 10, &input_val);
> +       if (ret)
> +               return ret;
> +
> +       switch (sensor_attr->nr) {
> +       case 0: /* enable, disable */
> +               aspeed_set_pwm_en(aspeed_pwm_tacho, sensor_attr->index,
> +                                 input_val);
> +               break;
> +       case 1: /* pwm type M/N/O */
> +               aspeed_set_pwm_type(aspeed_pwm_tacho, sensor_attr->index,
> +                                   input_val);
> +               break;
> +       case 2: /* rising */
> +               aspeed_set_pwm_duty_rising(aspeed_pwm_tacho,
> +                                          sensor_attr->index, input_val);
> +               break;
> +       case 3: /* falling */
> +               aspeed_set_pwm_duty_falling(aspeed_pwm_tacho,
> +                                           sensor_attr->index, input_val);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return count;
> +}
> +
> +/* Fan Type  */
> +/* Fan M/N/O Type sysfs
> + * Macro defining SENSOR_DEVICE_ATTR for a pwm sysfs entries.
> + * 0 - show/store enable
> + * 1 - show/store mode
> + * 2 - show/store unit
> + * 3 - show/store division
> + * 4 - show/store limit
> + */
> +static ssize_t
> +aspeed_show_tacho_type(struct device *dev, struct device_attribute *attr,
> +                      char *buf)
> +{
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +
> +       switch (sensor_attr->nr) {
> +       case 0: /* enable, disable */
> +               return sprintf(buf, "%d : %s\n",
> +                       aspeed_get_tacho_type_en(aspeed_pwm_tacho,
> +                                                sensor_attr->index),
> +                       aspeed_get_tacho_type_en(aspeed_pwm_tacho,
> +                                                sensor_attr->index) ?
> +                       "Enable":"Disable");
> +
> +       case 1: /* fan tacho mode */
> +               switch (aspeed_get_tacho_type_mode(
> +                       aspeed_pwm_tacho, sensor_attr->index)) {
> +               case FALL_EDGE:
> +                       return sprintf(buf, "0: falling\n");
> +               case RISE_EDGE:
> +                       return sprintf(buf, "1: rising\n");
> +               case BOTH_EDGE:
> +                       return sprintf(buf, "2: both\n");
> +               default:
> +                       return sprintf(buf, "3: unknown\n");
> +               }
> +               break;
> +
> +       case 2: /* unit */
> +               return sprintf(buf, "%d (0~65535)\n",
> +                       aspeed_get_tacho_type_unit(aspeed_pwm_tacho,
> +                                                  sensor_attr->index));
> +
> +       case 3: /* division */
> +               return sprintf(buf, "%d (0~7)\n",
> +                       aspeed_get_tacho_type_division(aspeed_pwm_tacho,
> +                                                      sensor_attr->index));
> +
> +       case 4: /* limit */
> +               return sprintf(buf, "%d (0~1048575)\n",
> +                       aspeed_get_tacho_type_limit(aspeed_pwm_tacho,
> +                                                   sensor_attr->index));
> +
> +       case 5: /* measure period */
> +               return sprintf(buf, "%d\n",
> +                       aspeed_get_tacho_measure_period(aspeed_pwm_tacho,
> +                                                       sensor_attr->index));
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static ssize_t
> +aspeed_store_tacho_type(struct device *dev, struct device_attribute *attr,
> +                       const char *buf, size_t count)
> +{
> +       unsigned long input_val;
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +       int ret;
> +
> +       ret = kstrtoul(buf, 10, &input_val);
> +       if (ret)
> +               return ret;
> +
> +       switch (sensor_attr->nr) {
> +       case 0: /* enable, disable */
> +               aspeed_set_tacho_type_en(aspeed_pwm_tacho, sensor_attr->index,
> +                                        input_val);
> +               break;
> +       case 1: /* fan tacho mode */
> +               aspeed_set_tacho_type_mode(aspeed_pwm_tacho,
> +                                          sensor_attr->index, input_val);
> +               break;
> +
> +       case 2: /* unit */
> +               aspeed_set_tacho_type_unit(aspeed_pwm_tacho,
> +                                          sensor_attr->index, input_val);
> +               break;
> +
> +       case 3: /* division */
> +               aspeed_set_tacho_type_division(aspeed_pwm_tacho,
> +                                              sensor_attr->index, input_val);
> +               break;
> +
> +       case 4: /* limit */
> +               aspeed_set_tacho_type_limit(aspeed_pwm_tacho,
> +                                           sensor_attr->index, input_val);
> +               break;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return count;
> +
> +}
> +
> +/* fan detect */
> +/* FAN sysfs
> + * Macro defining SENSOR_DEVICE_ATTR for a tacho sysfs entries.
> + *  - show/store enable
> + *  - show/store source
> + *  - show/store rpm
> + *  - show/store alarm
> + *  - show/store alarm_en
> +*/
> +static ssize_t
> +aspeed_show_tacho_speed(struct device *dev, struct device_attribute *attr,
> +                       char *buf)
> +{
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +
> +       switch (sensor_attr->nr)  {
> +       case 0: /* enable, disable */
> +               return sprintf(buf, "%d : %s\n",
> +                              aspeed_get_tacho_en(aspeed_pwm_tacho,
> +                                                  sensor_attr->index),
> +                              aspeed_get_tacho_en(aspeed_pwm_tacho,
> +                                                  sensor_attr->index) ?
> +                              "Enable":"Disable");
> +
> +       case 1: /* tacho source PWMA~H - 0~7 */
> +               return sprintf(buf, "PWM%d (0~7)\n",
> +                              aspeed_get_tacho_source(aspeed_pwm_tacho,
> +                                                      sensor_attr->index));
> +
> +       case 2: /* rpm */
> +               return sprintf(buf, "%d \n",
> +                              aspeed_get_tacho_rpm(aspeed_pwm_tacho,
> +                                                   sensor_attr->index));
> +
> +       case 3: /* alarm */
> +               return sprintf(buf, "%d \n",
> +                              aspeed_get_tacho_alarm(aspeed_pwm_tacho,
> +                                                     sensor_attr->index));
> +
> +       case 4: /* alarm_en */
> +               return sprintf(buf, "%d : %s\n",
> +                              aspeed_get_tacho_alarm_en(aspeed_pwm_tacho,
> +                                                        sensor_attr->index),
> +                              aspeed_get_tacho_alarm_en(aspeed_pwm_tacho,
> +                                                        sensor_attr->index) ?
> +                              "Enable":"Disable");
> +
> +       default:
> +               return -EINVAL;
> +       }
> +
> +}
> +
> +static ssize_t
> +aspeed_store_tacho_speed(struct device *dev, struct device_attribute *attr,
> +                        const char *buf, size_t count)
> +{
> +       unsigned long input_val;
> +       struct sensor_device_attribute_2 *sensor_attr =
> +                                               to_sensor_dev_attr_2(attr);
> +       int ret;
> +
> +       ret = kstrtoul(buf, 10, &input_val);
> +       if (ret)
> +               return ret;
> +
> +       switch (sensor_attr->nr)  {
> +       case 0: /* enable, disable */
> +               aspeed_set_tacho_en(aspeed_pwm_tacho, sensor_attr->index,
> +                                   input_val);
> +               break;
> +
> +       case 1: /* tacho source PWMA~H - 0~7 */
> +               aspeed_set_tacho_source(aspeed_pwm_tacho, sensor_attr->index,
> +                                       input_val);
> +               break;
> +
> +       case 4: /* alarm_en */
> +               aspeed_set_tacho_alarm_en(aspeed_pwm_tacho, sensor_attr->index,
> +                                         input_val);
> +               break;
> +
> +       case 2: /* rpm */
> +       case 3: /* alarm */
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return count;
> +}
> +
> +/* sysfs attributes */
> +
> +/* CLK sysfs */
> +static SENSOR_DEVICE_ATTR_2
> +(clk_en, S_IRUGO | S_IWUSR, aspeed_show_clk, aspeed_store_clk, 0, 0);
> +static SENSOR_DEVICE_ATTR_2
> +(clk_source, S_IRUGO | S_IWUSR, aspeed_show_clk, aspeed_store_clk, 1, 0);
> +
> +static struct attribute *clk_attributes[] = {
> +       &sensor_dev_attr_clk_source.dev_attr.attr,
> +       &sensor_dev_attr_clk_en.dev_attr.attr,
> +       NULL
> +};
> +
> +static const struct attribute_group clk_attribute_groups = {
> +       .attrs = clk_attributes,
> +};
> +
> +/* PWM M/N/O Type sysfs
> + * Macro defining SENSOR_DEVICE_ATTR for a pwm sysfs entries.
> + * 0 - show/store unit
> + * 1 - show/store division_l
> + * 2 - show/store division_h
> + */
> +#define sysfs_pwm_type(type,  index) \
> +static SENSOR_DEVICE_ATTR_2(pwm_type_##type##_unit, S_IRUGO | S_IWUSR, \
> +       aspeed_show_pwm_type_clock, aspeed_store_pwm_type_clock, 0, index); \
> +static SENSOR_DEVICE_ATTR_2(pwm_type_##type##_division_l, S_IRUGO | S_IWUSR, \
> +       aspeed_show_pwm_type_clock, aspeed_store_pwm_type_clock, 1, index); \
> +static SENSOR_DEVICE_ATTR_2(pwm_type_##type##_division_h, S_IRUGO | S_IWUSR, \
> +       aspeed_show_pwm_type_clock, aspeed_store_pwm_type_clock, 2, index); \
> +static SENSOR_DEVICE_ATTR_2(pwm_type_##type##_clk, S_IRUGO, \
> +       aspeed_show_pwm_type_clock, NULL, 3, index); \
> +static struct attribute *pwm_type_##type##_attributes[] = { \
> +       &sensor_dev_attr_pwm_type_##type##_unit.dev_attr.attr, \
> +       &sensor_dev_attr_pwm_type_##type##_division_l.dev_attr.attr, \
> +       &sensor_dev_attr_pwm_type_##type##_division_h.dev_attr.attr, \
> +       &sensor_dev_attr_pwm_type_##type##_clk.dev_attr.attr, \
> +       NULL \
> +}
> +
> +/*
> + * Create the needed functions for each pwm using the macro defined above
> + * (4 pwms are supported)
> + */
> +sysfs_pwm_type(m, 0);
> +sysfs_pwm_type(n, 1);
> +#ifdef PWM_TYPE_O
> +sysfs_pwm_type(o, 2);
> +#endif
> +
> +static const struct attribute_group pwm_type_attribute_groups[] = {
> +       { .attrs = pwm_type_m_attributes },
> +       { .attrs = pwm_type_n_attributes },
> +#ifdef PWM_TYPE_O
> +       { .attrs = pwm_type_o_attributes },
> +#endif
> +};
> +
> +/* PWM sysfs
> + * Macro defining SENSOR_DEVICE_ATTR for a pwm sysfs entries.
> + * 0 - show/store enable
> + * 1 - show/store type
> + * 2 - show/store rising
> + * 3 - show/store falling
> + */
> +#define sysfs_pwm_speeds_num(index) \
> +static SENSOR_DEVICE_ATTR_2(pwm##index##_en, S_IRUGO | S_IWUSR, \
> +       aspeed_show_pwm_speed, aspeed_store_pwm_speed, 0, index); \
> +static SENSOR_DEVICE_ATTR_2(pwm##index##_type, S_IRUGO | S_IWUSR, \
> +       aspeed_show_pwm_speed, aspeed_store_pwm_speed, 1, index); \
> +static SENSOR_DEVICE_ATTR_2(pwm##index##_rising, S_IRUGO | S_IWUSR, \
> +       aspeed_show_pwm_speed, aspeed_store_pwm_speed, 2, index); \
> +static SENSOR_DEVICE_ATTR_2(pwm##index##_falling, S_IRUGO | S_IWUSR, \
> +       aspeed_show_pwm_speed, aspeed_store_pwm_speed, 3, index); \
> +static struct attribute *pwm##index##_attributes[] = { \
> +       &sensor_dev_attr_pwm##index##_en.dev_attr.attr, \
> +       &sensor_dev_attr_pwm##index##_type.dev_attr.attr, \
> +       &sensor_dev_attr_pwm##index##_rising.dev_attr.attr, \
> +       &sensor_dev_attr_pwm##index##_falling.dev_attr.attr, \
> +       NULL \
> +}
> +
> +/*
> + * Create the needed functions for each pwm using the macro defined above
> + * (4 pwms are supported)
> + */
> +sysfs_pwm_speeds_num(0);
> +sysfs_pwm_speeds_num(1);
> +sysfs_pwm_speeds_num(2);
> +sysfs_pwm_speeds_num(3);
> +sysfs_pwm_speeds_num(4);
> +sysfs_pwm_speeds_num(5);
> +sysfs_pwm_speeds_num(6);
> +sysfs_pwm_speeds_num(7);
> +
> +static const struct attribute_group pwm_attribute_groups[] = {
> +       { .attrs = pwm0_attributes },
> +       { .attrs = pwm1_attributes },
> +       { .attrs = pwm2_attributes },
> +       { .attrs = pwm3_attributes },
> +       { .attrs = pwm4_attributes },
> +       { .attrs = pwm5_attributes },
> +       { .attrs = pwm6_attributes },
> +       { .attrs = pwm7_attributes },
> +};
> +
> +/* Fan M/N/O Type sysfs
> + * Macro defining SENSOR_DEVICE_ATTR for a pwm sysfs entries.
> + *  0 - show/store enable
> + *  1 - show/store mode
> + *  2 - show/store unit
> + *  3 - show/store division
> + *  4 - show/store limit
> + */
> +#define sysfs_tacho_type(type, index) \
> +static SENSOR_DEVICE_ATTR_2(tacho_type_##type##_en, S_IRUGO | S_IWUSR, \
> +       aspeed_show_tacho_type, aspeed_store_tacho_type, 0, index); \
> +static SENSOR_DEVICE_ATTR_2(tacho_type_##type##_mode, S_IRUGO | S_IWUSR, \
> +       aspeed_show_tacho_type, aspeed_store_tacho_type, 1, index); \
> +static SENSOR_DEVICE_ATTR_2(tacho_type_##type##_unit, S_IRUGO | S_IWUSR, \
> +       aspeed_show_tacho_type, aspeed_store_tacho_type, 2, index); \
> +static SENSOR_DEVICE_ATTR_2(tacho_type_##type##_division, S_IRUGO | S_IWUSR, \
> +       aspeed_show_tacho_type, aspeed_store_tacho_type, 3, index); \
> +static SENSOR_DEVICE_ATTR_2(tacho_type_##type##_limit, S_IRUGO | S_IWUSR, \
> +       aspeed_show_tacho_type, aspeed_store_tacho_type, 4, index); \
> +static SENSOR_DEVICE_ATTR_2(tacho_type_##type##_measure_period, \
> +       S_IRUGO | S_IWUSR, \
> +       aspeed_show_tacho_type, aspeed_store_tacho_type, 5, index); \
> +static struct attribute *tacho_type_##type##_attributes[] = { \
> +       &sensor_dev_attr_tacho_type_##type##_en.dev_attr.attr, \
> +       &sensor_dev_attr_tacho_type_##type##_mode.dev_attr.attr, \
> +       &sensor_dev_attr_tacho_type_##type##_unit.dev_attr.attr, \
> +       &sensor_dev_attr_tacho_type_##type##_division.dev_attr.attr, \
> +       &sensor_dev_attr_tacho_type_##type##_limit.dev_attr.attr, \
> +       &sensor_dev_attr_tacho_type_##type##_measure_period.dev_attr.attr, \
> +       NULL \
> +}
> +
> +/*
> + * Create the needed functions for each pwm using the macro defined above
> + * (4 pwms are supported)
> + */
> +sysfs_tacho_type(m, 0);
> +sysfs_tacho_type(n, 1);
> +#ifdef PWM_TYPE_O
> +sysfs_tacho_type(o, 2);
> +#endif
> +
> +static const struct attribute_group tacho_type_attribute_groups[] = {
> +       { .attrs = tacho_type_m_attributes },
> +       { .attrs = tacho_type_n_attributes },
> +#ifdef PWM_TYPE_O
> +       { .attrs = tacho_type_o_attributes },
> +#endif
> +};
> +
> +/* FAN sysfs
> + * Macro defining SENSOR_DEVICE_ATTR for a tacho sysfs entries.
> + *  - show/store enable
> + *  - show/store source
> + *  - show/store rpm
> + *  - show/store alarm
> + *  - show/store alarm_en
> + */
> +#define sysfs_tacho_speeds_num(index) \
> +static SENSOR_DEVICE_ATTR_2(tacho##index##_en, S_IRUGO | S_IWUSR, \
> +       aspeed_show_tacho_speed, aspeed_store_tacho_speed, 0, index); \
> +static SENSOR_DEVICE_ATTR_2(tacho##index##_source, S_IRUGO | S_IWUSR, \
> +       aspeed_show_tacho_speed, aspeed_store_tacho_speed, 1, index); \
> +static SENSOR_DEVICE_ATTR_2(tacho##index##_rpm, S_IRUGO, \
> +       aspeed_show_tacho_speed, NULL, 2, index); \
> +static SENSOR_DEVICE_ATTR_2(tacho##index##_alarm, S_IRUGO, \
> +       aspeed_show_tacho_speed, aspeed_store_tacho_speed, 3, index); \
> +static SENSOR_DEVICE_ATTR_2(tacho##index##_alarm_en, S_IRUGO | S_IWUSR, \
> +       aspeed_show_tacho_speed, aspeed_store_tacho_speed, 4, index); \
> +static struct attribute *tacho##index##_attributes[] = { \
> +       &sensor_dev_attr_tacho##index##_en.dev_attr.attr, \
> +       &sensor_dev_attr_tacho##index##_source.dev_attr.attr, \
> +       &sensor_dev_attr_tacho##index##_rpm.dev_attr.attr, \
> +       &sensor_dev_attr_tacho##index##_alarm.dev_attr.attr, \
> +       &sensor_dev_attr_tacho##index##_alarm_en.dev_attr.attr, \
> +       NULL \
> +}
> +
> +/*
> + * Create the needed functions for each tacho using the macro defined above
> + * (4 tachos are supported)
> + */
> +sysfs_tacho_speeds_num(0);
> +sysfs_tacho_speeds_num(1);
> +sysfs_tacho_speeds_num(2);
> +sysfs_tacho_speeds_num(3);
> +sysfs_tacho_speeds_num(4);
> +sysfs_tacho_speeds_num(5);
> +sysfs_tacho_speeds_num(6);
> +sysfs_tacho_speeds_num(7);
> +sysfs_tacho_speeds_num(8);
> +sysfs_tacho_speeds_num(9);
> +sysfs_tacho_speeds_num(10);
> +sysfs_tacho_speeds_num(11);
> +sysfs_tacho_speeds_num(12);
> +sysfs_tacho_speeds_num(13);
> +sysfs_tacho_speeds_num(14);
> +sysfs_tacho_speeds_num(15);
> +
> +static const struct attribute_group tacho_attribute_groups[] = {
> +       { .attrs = tacho0_attributes },
> +       { .attrs = tacho1_attributes },
> +       { .attrs = tacho2_attributes },
> +       { .attrs = tacho3_attributes },
> +       { .attrs = tacho4_attributes },
> +       { .attrs = tacho5_attributes },
> +       { .attrs = tacho6_attributes },
> +       { .attrs = tacho7_attributes },
> +       { .attrs = tacho8_attributes },
> +       { .attrs = tacho9_attributes },
> +       { .attrs = tacho10_attributes },
> +       { .attrs = tacho11_attributes },
> +       { .attrs = tacho12_attributes },
> +       { .attrs = tacho13_attributes },
> +       { .attrs = tacho14_attributes },
> +       { .attrs = tacho15_attributes },
> +};
> +
> +static int
> +aspeed_pwm_tacho_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int i, j = 0;
> +       int err = 0;
> +
> +       if (!of_device_is_compatible(pdev->dev.of_node,
> +                                    "aspeed,aspeed2500-pwm-fan"))
> +               return -ENODEV;
> +
> +       aspeed_pwm_tacho = devm_kzalloc(&pdev->dev, sizeof(*aspeed_pwm_tacho),
> +                                       GFP_KERNEL);
> +       if (!aspeed_pwm_tacho) {
> +               err = -ENOMEM;
> +               goto out;
> +       }
> +
> +       aspeed_pwm_tacho->aspeed_pwm_data = dev_get_platdata(&pdev->dev);
> +       platform_set_drvdata(pdev, aspeed_pwm_tacho);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res) {
> +               dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n");
> +               err = -ENOENT;
> +               goto out;
> +       }
> +
> +       if (!request_mem_region(res->start, resource_size(res), res->name)) {
> +               dev_err(&pdev->dev, "cannot reserved region\n");
> +               err = -ENXIO;
> +               goto out;
> +       }
> +
> +       aspeed_pwm_tacho->reg_base = ioremap(res->start, resource_size(res));
> +       if (!aspeed_pwm_tacho->reg_base) {
> +               err = -EIO;
> +               goto out_region;
> +       }
> +
> +       aspeed_pwm_tacho->irq = platform_get_irq(pdev, 0);
> +       if (aspeed_pwm_tacho->irq < 0) {
> +               dev_err(&pdev->dev, "no irq specified\n");
> +               err = -ENOENT;
> +               goto out_region;
> +       }
> +
> +       aspeed_pwm_tacho->groups[j++] = &clk_attribute_groups;
> +
> +       for (i = 0; i < PWM_CH_NUM; i++)
> +               aspeed_pwm_tacho->groups[j++] = &pwm_attribute_groups[i];
> +
> +       for (i = 0; i < PWM_TYPE_NUM; i++)
> +               aspeed_pwm_tacho->groups[j++] = &pwm_type_attribute_groups[i];
> +
> +       for (i = 0; i < TACHO_NUM; i++)
> +               aspeed_pwm_tacho->groups[j++] =  &tacho_attribute_groups[i];
> +
> +       for (i = 0; i < PWM_TYPE_NUM; i++)
> +               aspeed_pwm_tacho->groups[j++] =
> +                                       &tacho_type_attribute_groups[i];
> +
> +       aspeed_pwm_tacho->groups[j] = NULL;
> +
> +       aspeed_pwm_tacho->hwmon_dev = devm_hwmon_device_register_with_groups(
> +                                               &pdev->dev, "aspeed_pwm_tacho",
> +                                               aspeed_pwm_tacho,
> +                                               aspeed_pwm_tacho->groups);
> +
> +       if (PTR_ERR_OR_ZERO(aspeed_pwm_tacho->hwmon_dev)) {
> +               dev_err(&pdev->dev, "cannot register hwmon\n");
> +               err = PTR_ERR(aspeed_pwm_tacho->hwmon_dev);
> +               goto out_region;
> +       }
> +
> +       /* SCU Pin-MUX PWM & TACHO */
> +       aspeed_scu_multi_func_reset(AST_SCU_FUN_PIN_CTRL3, 0xcfffff, 0xc000ff);
> +
> +       /* SCU PWM CTRL Reset */
> +       aspeed_toggle_scu_reset(SCU_RESET_PWM, 3);
> +
> +       aspeed_pwm_taco_init();
> +
> +       dev_info(&pdev->dev, "aspeed pwm tacho: driver successfully loaded.\n");
> +
> +       return 0;
> +
> +out_region:
> +       release_mem_region(res->start, res->end - res->start + 1);
> +out:
> +       dev_err(&pdev->dev, "aspeed pwm tacho: driver init failed (err=%d)!\n",
> +               err);
> +       return err;
> +}
> +
> +static int aspeed_pwm_tacho_remove(struct platform_device *pdev)
> +{
> +       struct aspeed_pwm_tacho_data *aspeed_pwm_tacho =
> +                                               platform_get_drvdata(pdev);
> +       struct resource *res;
> +
> +       platform_set_drvdata(pdev, NULL);
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       iounmap(aspeed_pwm_tacho->reg_base);
> +       release_mem_region(res->start, res->end - res->start + 1);
> +       dev_info(&pdev->dev, "aspeed_pwm_tacho: driver unloaded.\n");
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int
> +aspeed_pwm_tacho_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +       dev_info(&pdev->dev, "aspeed_pwm_tacho_suspend\n");
> +
> +       return 0;
> +}
> +
> +static int
> +aspeed_pwm_tacho_resume(struct platform_device *pdev)
> +{
> +       aspeed_pwm_taco_init();
> +       return 0;
> +}
> +
> +#else
> +#define aspeed_pwm_tacho_suspend        NULL
> +#define aspeed_pwm_tacho_resume         NULL
> +#endif
> +
> +static const struct of_device_id aspeed_pwm_fan_of_table[] = {
> +       { .compatible = "aspeed,aspeed2500-pwm-fan", },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_pwm_fan_of_table);
> +
> +static struct platform_driver aspeed_pwm_tacho_driver = {
> +       .remove         = aspeed_pwm_tacho_remove,
> +       .suspend        = aspeed_pwm_tacho_suspend,
> +       .resume         = aspeed_pwm_tacho_resume,
> +       .driver         = {
> +               .name   = KBUILD_MODNAME,
> +               .owner  = THIS_MODULE,
> +               .of_match_table = aspeed_pwm_fan_of_table,
> +       },
> +};
> +module_platform_driver_probe(aspeed_pwm_tacho_driver, aspeed_pwm_tacho_probe);
> +
> +MODULE_AUTHOR("Ryan Chen <ryan_chen at aspeedtech.com>");
> +MODULE_DESCRIPTION("ASPEED PWM TACHO driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:aspeed-pwm-fan");
> --
> 2.1.4
>
> _______________________________________________
> openbmc mailing list
> openbmc at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/openbmc


More information about the openbmc mailing list