diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch | 1107 |
1 files changed, 0 insertions, 1107 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch deleted file mode 100644 index 9515a1a89..000000000 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch +++ /dev/null @@ -1,1107 +0,0 @@ -From a17a084c4889dcc7cb43ef5f93032997cfcee4f0 Mon Sep 17 00:00:00 2001 -From: Vernon Mauery <vernon.mauery@intel.com> -Date: Fri, 27 Sep 2019 13:09:48 -0700 -Subject: [PATCH] arm: ast2600: add pwm_tacho driver from aspeed - -Add the pwm_tacho driver from Aspeed to get pwm working until an -upstream PWM/Tacho driver is available. This was copied from the v5.02 -BSP from Aspeed. - -Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> ---- - arch/arm/boot/dts/aspeed-g6.dtsi | 10 + - drivers/hwmon/Kconfig | 11 + - drivers/hwmon/Makefile | 1 + - drivers/hwmon/aspeed-g6-pwm-tacho.c | 1025 +++++++++++++++++++++++++++++++++++ - 4 files changed, 1047 insertions(+) - create mode 100644 drivers/hwmon/aspeed-g6-pwm-tacho.c - -diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi -index eeace4b7b725..33fcd89db6b8 100644 ---- a/arch/arm/boot/dts/aspeed-g6.dtsi -+++ b/arch/arm/boot/dts/aspeed-g6.dtsi -@@ -278,6 +278,16 @@ - #size-cells = <1>; - ranges; - -+ pwm_tacho: pwm-tacho-controller@1e610000 { -+ compatible = "aspeed,ast2600-pwm-tacho"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x1e610000 0x100>; -+ clocks = <&syscon ASPEED_CLK_AHB>; -+ resets = <&syscon ASPEED_RESET_PWM>; -+ status = "disabled"; -+ }; -+ - syscon: syscon@1e6e2000 { - compatible = "aspeed,ast2600-scu", "syscon", "simple-mfd"; - reg = <0x1e6e2000 0x1000>; -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index e244a7901392..8312b3798b82 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -371,6 +371,17 @@ config SENSORS_ASPEED - This driver can also be built as a module. If so, the module - will be called aspeed_pwm_tacho. - -+config SENSORS_ASPEED_G6 -+ tristate "ASPEED AST2600 PWM and Fan tach driver" -+ depends on THERMAL || THERMAL=n -+ select REGMAP -+ help -+ This driver provides support for ASPEED AST2600 PWM -+ and Fan Tacho controllers. -+ -+ This driver can also be built as a module. If so, the module -+ will be called aspeed_g6_pwm_tacho. -+ - config SENSORS_ATXP1 - tristate "Attansic ATXP1 VID controller" - depends on I2C -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index 1c7ab361adc7..e74ea925fb56 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -50,6 +50,7 @@ obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o - obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o - obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o - obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o -+obj-$(CONFIG_SENSORS_ASPEED_G6) += aspeed-g6-pwm-tacho.o - obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o - obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o - obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o -diff --git a/drivers/hwmon/aspeed-g6-pwm-tacho.c b/drivers/hwmon/aspeed-g6-pwm-tacho.c -new file mode 100644 -index 000000000000..1894f6ad5edb ---- /dev/null -+++ b/drivers/hwmon/aspeed-g6-pwm-tacho.c -@@ -0,0 +1,1025 @@ -+/* -+ * Copyright (C) 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 or later as -+ * published by the Free Software Foundation. -+ */ -+ -+#include <linux/clk.h> -+#include <linux/errno.h> -+#include <linux/gpio/consumer.h> -+#include <linux/delay.h> -+#include <linux/hwmon.h> -+#include <linux/hwmon-sysfs.h> -+#include <linux/io.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/of_platform.h> -+#include <linux/of_device.h> -+#include <linux/platform_device.h> -+#include <linux/sysfs.h> -+#include <linux/reset.h> -+#include <linux/regmap.h> -+#include <linux/thermal.h> -+ -+#define ASPEED_PWM_CTRL 0x00 //PWM0 General Register -+#define ASPEED_PWM_CTRL_CH(x) ((x * 0x10) + 0x00) -+#define PWM_LOAD_AS_WDT BIT(19) //load selection as WDT -+#define PWM_DUTY_LOAD_AS_WDT_EN BIT(18) //enable PWM duty load as WDT -+#define PWM_DUTY_SYNC_DIS BIT(17) //disable PWM duty sync -+#define PWM_CLK_ENABLE BIT(16) //enable PWM clock -+#define PWM_LEVEL_OUTPUT BIT(15) //output PWM level -+#define PWM_INVERSE BIT(14) //inverse PWM pin -+#define PWM_OPEN_DRAIN_EN BIT(13) //enable open-drain -+#define PWM_PIN_EN BIT(12) //enable PWM pin -+#define PWM_CLK_DIV_H_MASK (0xf << 8) //PWM clock division H bit [3:0] -+#define PWM_CLK_DIV_L_MASK (0xff) //PWM clock division H bit [3:0] -+ -+/* -+\xregmid {11:8 }{RW}{PWM clock division H bit [3:0]}{ -+ 0: divide 1 \n -+ 1: divide 2 \n -+ 2: divide 4 \n -+ 3: divide 8 \n -+ ... \n -+ F: divide 32768} -+\xregmid {7 :0 }{RW}{PWM clock division L bit [7:0]}{ -+ 00: divide 1 \n -+ 01: divide 2 \n -+ 02: divide 3 \n -+ 03: divide 4 \n -+ ... \n -+ FF: divide 256} -+*/ -+ -+#define ASPEED_PWM_DUTY_CYCLE 0x04 //PWM0 Duty Cycle Register -+#define ASPEED_PWM_DUTY_CYCLE_CH(x) ((x * 0x10) + 0x04) -+#define PWM_LOOP_BIT_MASK (0xf << 24) //loop bit [7:0] -+#define PWM_PERIOD_BIT (24) //pwm period bit [7:0] -+#define PWM_PERIOD_BIT_MASK (0xff << 24) //pwm period bit [7:0] -+#define PWM_RISING_FALLING_AS_WDT_BIT (16) -+#define PWM_RISING_FALLING_AS_WDT_MASK (0xff << 16) //pwm rising/falling point bit [7:0] as WDT -+#define PWM_RISING_FALLING_MASK (0xffff) -+#define PWM_RISING_FALLING_BIT (8) //pwm falling point bit [7:0] -+#define PWM_RISING_RISING_BIT (0) //pwm rising point bit [7:0] -+ -+#define ASPEED_TACHO_CTRL 0x08 //TACH0 General Register -+#define ASPEED_TACHO_CTRL_CH(x) ((x * 0x10) + 0x08) -+#define TACHO_IER BIT(31) //enable tacho interrupt -+#define TACHO_INVERS_LIMIT BIT(30) //inverse tacho limit comparison -+#define TACHO_LOOPBACK BIT(29) //tacho loopback -+#define TACHO_ENABLE BIT(28) //{enable tacho} -+#define TACHO_DEBOUNCE_BIT (26) //{tacho de-bounce} -+#define TACHO_DEBOUNCE_MASK (0x3 << 26) //{tacho de-bounce} -+#define TECHIO_EDGE_MASK (0x3 << 24) //tacho edge} -+#define TECHIO_EDGE_BIT (24) //tacho edge} -+#define TACHO_CLK_DIV_T_MASK (0xf << 20) -+#define TACHO_CLK_DIV_BIT (20) -+#define TACHO_THRESHOLD_MASK (0xfffff) //tacho threshold bit -+/* -+\xregmid {23:20}{RW}{tacho clock division T bit [3:0]}{ -+ 0: divide 1 \n -+ 1: divide 4 \n -+ 2: divide 16 \n -+ 3: divide 64 \n -+ ... \n -+ B: divide 4194304 \n -+ others: reserved} -+\xregmidb{19 :0 }{RW}{tacho threshold bit [19:0]} -+*/ -+ -+#define ASPEED_TACHO_STS 0x0C //TACH0 Status Register -+#define ASPEED_TACHO_STS_CH(x) ((x * 0x10) + 0x0C) -+#define TACHO_ISR BIT(31) //interrupt status and clear -+#define PWM_OUT BIT(25) //{pwm_out} -+#define PWM_OEN BIT(24) //{pwm_oeN} -+#define TACHO_DEB_INPUT BIT(23) //tacho deB input -+#define TACHO_RAW_INPUT BIT(22) //tacho raw input} -+#define TACHO_VALUE_UPDATE BIT(21) //tacho value updated since the last read -+#define TACHO_FULL_MEASUREMENT BIT(20) //{tacho full measurement} -+#define TACHO_VALUE_MASK 0xfffff //tacho value bit [19:0]} -+ -+#define MAX_CDEV_NAME_LEN 16 -+ -+struct aspeed_pwm_channel_params { -+ int load_wdt_rising_falling_pt; -+ int load_wdt_selection; //0: rising , 1: falling -+ int load_wdt_enable; -+ int duty_sync_enable; -+ int invert_pin; -+ u8 divide_h; -+ u8 divide_l; -+ u8 period; -+ u8 rising; -+ u8 falling; -+}; -+ -+static struct aspeed_pwm_channel_params default_pwm_params[] = { -+ [0] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 1, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [1] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [2] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [3] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [4] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [5] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [6] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [7] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [8] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [9] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [10] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [11] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [12] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [13] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [14] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+ [15] = { -+ .load_wdt_rising_falling_pt = 0x10, -+ .load_wdt_selection = 0, -+ .load_wdt_enable = 0, -+ .duty_sync_enable = 0, -+ .invert_pin = 0, -+ .divide_h = 0x5, -+ .divide_l = 0x6, -+ .period = 0x13, //5% ~~ -+ .rising = 0x00, -+ .falling = 0x0a, -+ }, -+}; -+ -+/* -+ * 5:4 fan tach edge mode selection bit: -+ * 00: falling -+ * 01: rising -+ * 10: both -+ * 11: reserved. -+ */ -+ -+struct aspeed_tacho_channel_params { -+ int limited_inverse; -+ u16 threshold; -+ u8 tacho_edge; -+ u8 tacho_debounce; -+ u8 divide; -+}; -+ -+ -+static struct aspeed_tacho_channel_params default_tacho_params[] = { -+ [0] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [1] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [2] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [3] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [4] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [5] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [6] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [7] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [8] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [9] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [10] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [11] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [12] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [13] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [14] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+ [15] = { -+ .limited_inverse = 0, -+ .threshold = 0, -+ .tacho_edge = 0, -+ .tacho_debounce = 0, -+ .divide = 8, -+ }, -+}; -+ -+struct aspeed_pwm_tachometer_data { -+ struct regmap *regmap; -+ unsigned long clk_freq; -+ struct reset_control *reset; -+ bool pwm_present[16]; -+ bool fan_tach_present[16]; -+ struct aspeed_pwm_channel_params *pwm_channel; -+ struct aspeed_tacho_channel_params *tacho_channel; -+ struct aspeed_cooling_device *cdev[8]; -+ const struct attribute_group *groups[3]; -+}; -+ -+struct aspeed_cooling_device { -+ char name[16]; -+ struct aspeed_pwm_tachometer_data *priv; -+ struct thermal_cooling_device *tcdev; -+ int pwm_channel; -+ u8 *cooling_levels; -+ u8 max_state; -+ u8 cur_state; -+}; -+ -+static int regmap_aspeed_pwm_tachometer_reg_write(void *context, unsigned int reg, -+ unsigned int val) -+{ -+ void __iomem *regs = (void __iomem *)context; -+ -+ writel(val, regs + reg); -+ return 0; -+} -+ -+static int regmap_aspeed_pwm_tachometer_reg_read(void *context, unsigned int reg, -+ unsigned int *val) -+{ -+ void __iomem *regs = (void __iomem *)context; -+ -+ *val = readl(regs + reg); -+ return 0; -+} -+ -+static const struct regmap_config aspeed_pwm_tachometer_regmap_config = { -+ .reg_bits = 32, -+ .val_bits = 32, -+ .reg_stride = 4, -+ .max_register = 0x100, -+ .reg_write = regmap_aspeed_pwm_tachometer_reg_write, -+ .reg_read = regmap_aspeed_pwm_tachometer_reg_read, -+ .fast_io = true, -+}; -+ -+static void aspeed_set_pwm_channel_enable(struct regmap *regmap, u8 pwm_channel, -+ bool enable) -+{ -+ regmap_update_bits(regmap, ASPEED_PWM_CTRL_CH(pwm_channel), (PWM_CLK_ENABLE | PWM_PIN_EN), enable ? (PWM_CLK_ENABLE | PWM_PIN_EN) : 0); -+} -+ -+static void aspeed_set_fan_tach_ch_enable(struct aspeed_pwm_tachometer_data *priv, u8 fan_tach_ch, -+ bool enable) -+{ -+ u32 i = 0, j; -+ u32 divide_val = 0; -+ u32 reg_value = 0; -+ -+ if(enable) { -+ //4 ^ n -+ //check pwm clk and to change tacho devide 25KZ -+ for(i = 0; i < 12; i++) { -+ divide_val = 1; -+ for (j = 1; j <= i; j++) -+ divide_val *= 4; -+// printk("i : %d , priv->clk_freq/divide_val %d ",i, priv->clk_freq/divide_val); -+ if((priv->clk_freq/divide_val) < 250000) -+ break; -+ } -+ i--; -+ divide_val = ((1 << i) * (1 << i)); -+// printk("tacho divide_val %d , i %x max tacho clk %d \n", divide_val, i, priv->clk_freq / divide_val); -+ priv->tacho_channel[fan_tach_ch].divide = i; -+ -+ reg_value = TACHO_ENABLE | -+ (priv->tacho_channel[fan_tach_ch].tacho_edge << TECHIO_EDGE_BIT) | -+ (priv->tacho_channel[fan_tach_ch].divide << TACHO_CLK_DIV_BIT) | -+ (priv->tacho_channel[fan_tach_ch].tacho_debounce << TACHO_DEBOUNCE_BIT); -+ -+ if(priv->tacho_channel[fan_tach_ch].limited_inverse) -+ reg_value |= TACHO_INVERS_LIMIT; -+ -+ if(priv->tacho_channel[fan_tach_ch].threshold) -+ reg_value |= (TACHO_IER | priv->tacho_channel[fan_tach_ch].threshold); -+ -+ regmap_write(priv->regmap, ASPEED_TACHO_CTRL_CH(fan_tach_ch), reg_value); -+ } else -+ regmap_update_bits(priv->regmap, ASPEED_TACHO_CTRL_CH(fan_tach_ch), TACHO_ENABLE, 0); -+} -+ -+static void aspeed_set_pwm_channel_fan_ctrl(struct aspeed_pwm_tachometer_data *priv, -+ u8 index, u8 fan_ctrl) -+{ -+ u32 duty_value, ctrl_value; -+ -+ if (fan_ctrl == 0) { -+ aspeed_set_pwm_channel_enable(priv->regmap, index, false); -+ } else { -+ duty_value = (priv->pwm_channel[index].period << PWM_PERIOD_BIT) | -+ (0 << PWM_RISING_RISING_BIT) | (fan_ctrl << PWM_RISING_FALLING_BIT); -+ -+ ctrl_value = (priv->pwm_channel[index].divide_h << 8) | priv->pwm_channel[index].divide_l; -+ -+ if (priv->pwm_channel[index].load_wdt_enable) { -+ ctrl_value |= PWM_DUTY_LOAD_AS_WDT_EN; -+ if(priv->pwm_channel[index].load_wdt_selection) { -+ ctrl_value |= PWM_LOAD_AS_WDT; -+ duty_value |= (priv->pwm_channel[index].load_wdt_rising_falling_pt << PWM_RISING_FALLING_AS_WDT_BIT); -+ } else { -+ duty_value |= (priv->pwm_channel[index].load_wdt_rising_falling_pt << PWM_RISING_FALLING_AS_WDT_BIT); -+ } -+ } -+ -+ regmap_write(priv->regmap, ASPEED_PWM_DUTY_CYCLE_CH(index), duty_value); -+ -+ regmap_write(priv->regmap, ASPEED_PWM_CTRL_CH(index), ctrl_value); -+// printk("pwm clk is %d \n", priv->clk_freq / (priv->pwm_channel[index].period + 1)); -+ aspeed_set_pwm_channel_enable(priv->regmap, index, true); -+ } -+} -+ -+#define BOTH_EDGES 0x02 /* 10b */ -+ -+static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tachometer_data *priv, -+ u8 fan_tach_ch) -+{ -+ u32 raw_data, tach_div, clk_source, val; -+ u8 mode, both; -+ int i, retries = 3; -+ -+ for(i = 0; i < retries; i++) { -+ regmap_read(priv->regmap, ASPEED_TACHO_STS_CH(fan_tach_ch), &val); -+ if (TACHO_FULL_MEASUREMENT & val) -+ break; -+ } -+ -+ raw_data = val & TACHO_VALUE_MASK; -+ if(raw_data == 0xfffff) -+ return 0; -+ -+ tach_div = priv->tacho_channel[fan_tach_ch].divide; -+ /* -+ * We need the mode to determine if the raw_data is double (from -+ * counting both edges). -+ */ -+ mode = priv->tacho_channel[fan_tach_ch].tacho_edge; -+ both = (mode & BOTH_EDGES) ? 1 : 0; -+// printk("clk %ld, raw_data %x , tach_div %x both %x \n", priv->clk_freq, raw_data, tach_div, both); -+ -+ tach_div = (tach_div * 2) * (0x1 << both); -+ clk_source = priv->clk_freq; -+ -+ if (raw_data == 0) -+ return 0; -+ -+ return (clk_source * 60) / (2 * raw_data * tach_div); -+ -+} -+ -+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); -+ int index = sensor_attr->index; -+ int ret; -+ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); -+ long fan_ctrl; -+ -+ ret = kstrtol(buf, 10, &fan_ctrl); -+ if (ret != 0) -+ return ret; -+ -+ if (fan_ctrl < 0 || fan_ctrl > priv->pwm_channel[index].period) -+ return -EINVAL; -+ -+ if (priv->pwm_channel[index].falling == fan_ctrl) -+ return count; -+ -+ priv->pwm_channel[index].falling = fan_ctrl; -+ aspeed_set_pwm_channel_fan_ctrl(priv, index, fan_ctrl); -+ -+ return count; -+} -+ -+static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); -+ int index = sensor_attr->index; -+ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%u\n", priv->pwm_channel[index].falling); -+} -+ -+static ssize_t show_rpm(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); -+ int index = sensor_attr->index; -+ int rpm; -+ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); -+ -+ rpm = aspeed_get_fan_tach_ch_rpm(priv, index); -+ if (rpm < 0) -+ return rpm; -+ -+ return sprintf(buf, "%d\n", rpm); -+} -+ -+static umode_t pwm_is_visible(struct kobject *kobj, -+ struct attribute *a, int index) -+{ -+ struct device *dev = container_of(kobj, struct device, kobj); -+ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); -+ -+ if (!priv->pwm_present[index]) -+ return 0; -+ return a->mode; -+} -+ -+static umode_t fan_dev_is_visible(struct kobject *kobj, -+ struct attribute *a, int index) -+{ -+ struct device *dev = container_of(kobj, struct device, kobj); -+ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); -+ -+ if (!priv->fan_tach_present[index]) -+ return 0; -+ return a->mode; -+} -+ -+static SENSOR_DEVICE_ATTR(pwm0, 0644, -+ show_pwm, set_pwm, 0); -+static SENSOR_DEVICE_ATTR(pwm1, 0644, -+ show_pwm, set_pwm, 1); -+static SENSOR_DEVICE_ATTR(pwm2, 0644, -+ show_pwm, set_pwm, 2); -+static SENSOR_DEVICE_ATTR(pwm3, 0644, -+ show_pwm, set_pwm, 3); -+static SENSOR_DEVICE_ATTR(pwm4, 0644, -+ show_pwm, set_pwm, 4); -+static SENSOR_DEVICE_ATTR(pwm5, 0644, -+ show_pwm, set_pwm, 5); -+static SENSOR_DEVICE_ATTR(pwm6, 0644, -+ show_pwm, set_pwm, 6); -+static SENSOR_DEVICE_ATTR(pwm7, 0644, -+ show_pwm, set_pwm, 7); -+static SENSOR_DEVICE_ATTR(pwm8, 0644, -+ show_pwm, set_pwm, 8); -+static SENSOR_DEVICE_ATTR(pwm9, 0644, -+ show_pwm, set_pwm, 9); -+static SENSOR_DEVICE_ATTR(pwm10, 0644, -+ show_pwm, set_pwm, 10); -+static SENSOR_DEVICE_ATTR(pwm11, 0644, -+ show_pwm, set_pwm, 11); -+static SENSOR_DEVICE_ATTR(pwm12, 0644, -+ show_pwm, set_pwm, 12); -+static SENSOR_DEVICE_ATTR(pwm13, 0644, -+ show_pwm, set_pwm, 13); -+static SENSOR_DEVICE_ATTR(pwm14, 0644, -+ show_pwm, set_pwm, 14); -+static SENSOR_DEVICE_ATTR(pwm15, 0644, -+ show_pwm, set_pwm, 15); -+static struct attribute *pwm_dev_attrs[] = { -+ &sensor_dev_attr_pwm0.dev_attr.attr, -+ &sensor_dev_attr_pwm1.dev_attr.attr, -+ &sensor_dev_attr_pwm2.dev_attr.attr, -+ &sensor_dev_attr_pwm3.dev_attr.attr, -+ &sensor_dev_attr_pwm4.dev_attr.attr, -+ &sensor_dev_attr_pwm5.dev_attr.attr, -+ &sensor_dev_attr_pwm6.dev_attr.attr, -+ &sensor_dev_attr_pwm7.dev_attr.attr, -+ &sensor_dev_attr_pwm8.dev_attr.attr, -+ &sensor_dev_attr_pwm9.dev_attr.attr, -+ &sensor_dev_attr_pwm10.dev_attr.attr, -+ &sensor_dev_attr_pwm11.dev_attr.attr, -+ &sensor_dev_attr_pwm12.dev_attr.attr, -+ &sensor_dev_attr_pwm13.dev_attr.attr, -+ &sensor_dev_attr_pwm14.dev_attr.attr, -+ &sensor_dev_attr_pwm15.dev_attr.attr, -+ NULL, -+}; -+ -+static const struct attribute_group pwm_dev_group = { -+ .attrs = pwm_dev_attrs, -+ .is_visible = pwm_is_visible, -+}; -+ -+static SENSOR_DEVICE_ATTR(fan0_input, 0444, -+ show_rpm, NULL, 0); -+static SENSOR_DEVICE_ATTR(fan1_input, 0444, -+ show_rpm, NULL, 1); -+static SENSOR_DEVICE_ATTR(fan2_input, 0444, -+ show_rpm, NULL, 2); -+static SENSOR_DEVICE_ATTR(fan3_input, 0444, -+ show_rpm, NULL, 3); -+static SENSOR_DEVICE_ATTR(fan4_input, 0444, -+ show_rpm, NULL, 4); -+static SENSOR_DEVICE_ATTR(fan5_input, 0444, -+ show_rpm, NULL, 5); -+static SENSOR_DEVICE_ATTR(fan6_input, 0444, -+ show_rpm, NULL, 6); -+static SENSOR_DEVICE_ATTR(fan7_input, 0444, -+ show_rpm, NULL, 7); -+static SENSOR_DEVICE_ATTR(fan8_input, 0444, -+ show_rpm, NULL, 8); -+static SENSOR_DEVICE_ATTR(fan9_input, 0444, -+ show_rpm, NULL, 9); -+static SENSOR_DEVICE_ATTR(fan10_input, 0444, -+ show_rpm, NULL, 10); -+static SENSOR_DEVICE_ATTR(fan11_input, 0444, -+ show_rpm, NULL, 11); -+static SENSOR_DEVICE_ATTR(fan12_input, 0444, -+ show_rpm, NULL, 12); -+static SENSOR_DEVICE_ATTR(fan13_input, 0444, -+ show_rpm, NULL, 13); -+static SENSOR_DEVICE_ATTR(fan14_input, 0444, -+ show_rpm, NULL, 14); -+static SENSOR_DEVICE_ATTR(fan15_input, 0444, -+ show_rpm, NULL, 15); -+static struct attribute *fan_dev_attrs[] = { -+ &sensor_dev_attr_fan0_input.dev_attr.attr, -+ &sensor_dev_attr_fan1_input.dev_attr.attr, -+ &sensor_dev_attr_fan2_input.dev_attr.attr, -+ &sensor_dev_attr_fan3_input.dev_attr.attr, -+ &sensor_dev_attr_fan4_input.dev_attr.attr, -+ &sensor_dev_attr_fan5_input.dev_attr.attr, -+ &sensor_dev_attr_fan6_input.dev_attr.attr, -+ &sensor_dev_attr_fan7_input.dev_attr.attr, -+ &sensor_dev_attr_fan8_input.dev_attr.attr, -+ &sensor_dev_attr_fan9_input.dev_attr.attr, -+ &sensor_dev_attr_fan10_input.dev_attr.attr, -+ &sensor_dev_attr_fan11_input.dev_attr.attr, -+ &sensor_dev_attr_fan12_input.dev_attr.attr, -+ &sensor_dev_attr_fan13_input.dev_attr.attr, -+ &sensor_dev_attr_fan14_input.dev_attr.attr, -+ &sensor_dev_attr_fan15_input.dev_attr.attr, -+ NULL -+}; -+ -+static const struct attribute_group fan_dev_group = { -+ .attrs = fan_dev_attrs, -+ .is_visible = fan_dev_is_visible, -+}; -+ -+static void aspeed_create_pwm_channel(struct aspeed_pwm_tachometer_data *priv, -+ u8 pwm_channel) -+{ -+ priv->pwm_present[pwm_channel] = true; -+ -+ //use default -+ aspeed_set_pwm_channel_fan_ctrl(priv, pwm_channel, priv->pwm_channel[pwm_channel].falling); -+} -+ -+static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tachometer_data *priv, -+ u8 *fan_tach_ch, -+ int count) -+{ -+ u8 val, index; -+ -+ for (val = 0; val < count; val++) { -+ index = fan_tach_ch[val]; -+ priv->fan_tach_present[index] = true; -+ aspeed_set_fan_tach_ch_enable(priv, index, true); -+ } -+} -+ -+static int -+aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev, -+ unsigned long *state) -+{ -+ struct aspeed_cooling_device *cdev = tcdev->devdata; -+ -+ *state = cdev->max_state; -+ -+ return 0; -+} -+ -+static int -+aspeed_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev, -+ unsigned long *state) -+{ -+ struct aspeed_cooling_device *cdev = tcdev->devdata; -+ -+ *state = cdev->cur_state; -+ -+ return 0; -+} -+ -+static int -+aspeed_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev, -+ unsigned long state) -+{ -+ struct aspeed_cooling_device *cdev = tcdev->devdata; -+ -+ if (state > cdev->max_state) -+ return -EINVAL; -+ -+ cdev->cur_state = state; -+ cdev->priv->pwm_channel[cdev->pwm_channel].falling = -+ cdev->cooling_levels[cdev->cur_state]; -+ aspeed_set_pwm_channel_fan_ctrl(cdev->priv, cdev->pwm_channel, -+ cdev->cooling_levels[cdev->cur_state]); -+ -+ return 0; -+} -+ -+static const struct thermal_cooling_device_ops aspeed_pwm_cool_ops = { -+ .get_max_state = aspeed_pwm_cz_get_max_state, -+ .get_cur_state = aspeed_pwm_cz_get_cur_state, -+ .set_cur_state = aspeed_pwm_cz_set_cur_state, -+}; -+ -+static int aspeed_create_pwm_cooling(struct device *dev, -+ struct device_node *child, -+ struct aspeed_pwm_tachometer_data *priv, -+ u32 pwm_channel, u8 num_levels) -+{ -+ int ret; -+ struct aspeed_cooling_device *cdev; -+ -+ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL); -+ if (!cdev) -+ return -ENOMEM; -+ -+ cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL); -+ if (!cdev->cooling_levels) -+ return -ENOMEM; -+ -+ cdev->max_state = num_levels - 1; -+ ret = of_property_read_u8_array(child, "cooling-levels", -+ cdev->cooling_levels, -+ num_levels); -+ if (ret) { -+ dev_err(dev, "Property 'cooling-levels' cannot be read.\n"); -+ return ret; -+ } -+ snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%s%d", child->name, pwm_channel); -+ -+ cdev->tcdev = thermal_of_cooling_device_register(child, -+ cdev->name, -+ cdev, -+ &aspeed_pwm_cool_ops); -+ if (IS_ERR(cdev->tcdev)) -+ return PTR_ERR(cdev->tcdev); -+ -+ cdev->priv = priv; -+ cdev->pwm_channel = pwm_channel; -+ -+ priv->cdev[pwm_channel] = cdev; -+ -+ return 0; -+} -+ -+static int aspeed_pwm_create_fan(struct device *dev, -+ struct device_node *child, -+ struct aspeed_pwm_tachometer_data *priv) -+{ -+ u8 *fan_tach_ch; -+ u32 pwm_channel; -+ int ret, count; -+ -+ ret = of_property_read_u32(child, "reg", &pwm_channel); -+ if (ret) -+ return ret; -+ -+ aspeed_create_pwm_channel(priv, (u8)pwm_channel); -+ -+ ret = of_property_count_u8_elems(child, "cooling-levels"); -+ if (ret > 0) { -+ ret = aspeed_create_pwm_cooling(dev, child, priv, pwm_channel, -+ ret); -+ if (ret) -+ return ret; -+ } -+ -+ count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch"); -+ if (count < 1) -+ return -EINVAL; -+ -+ fan_tach_ch = devm_kzalloc(dev, sizeof(*fan_tach_ch) * count, -+ GFP_KERNEL); -+ if (!fan_tach_ch) -+ return -ENOMEM; -+ ret = of_property_read_u8_array(child, "aspeed,fan-tach-ch", -+ fan_tach_ch, count); -+ if (ret) -+ return ret; -+ -+ aspeed_create_fan_tach_channel(priv, fan_tach_ch, count); -+ -+ return 0; -+} -+ -+static int aspeed_pwm_tachometer_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct device_node *np, *child; -+ struct aspeed_pwm_tachometer_data *priv; -+ void __iomem *regs; -+ struct resource *res; -+ struct device *hwmon; -+ struct clk *clk; -+ int ret; -+ -+ np = dev->of_node; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) -+ return -ENOENT; -+ regs = devm_ioremap_resource(dev, res); -+ if (IS_ERR(regs)) -+ return PTR_ERR(regs); -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ priv->pwm_channel = default_pwm_params; -+ priv->tacho_channel = default_tacho_params; -+ priv->regmap = devm_regmap_init(dev, NULL, (__force void *)regs, -+ &aspeed_pwm_tachometer_regmap_config); -+ if (IS_ERR(priv->regmap)) -+ return PTR_ERR(priv->regmap); -+ -+ clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(clk)) -+ return -ENODEV; -+ priv->clk_freq = clk_get_rate(clk); -+ -+ priv->reset = devm_reset_control_get(&pdev->dev, NULL); -+ if (IS_ERR(priv->reset)) { -+ dev_err(&pdev->dev, "can't get aspeed_pwm_tacho reset\n"); -+ return PTR_ERR(priv->reset); -+ } -+ -+ //scu init -+ reset_control_assert(priv->reset); -+ reset_control_deassert(priv->reset); -+ -+ for_each_child_of_node(np, child) { -+ ret = aspeed_pwm_create_fan(dev, child, priv); -+ if (ret) { -+ of_node_put(child); -+ return ret; -+ } -+ } -+ -+ priv->groups[0] = &pwm_dev_group; -+ priv->groups[1] = &fan_dev_group; -+ priv->groups[2] = NULL; -+ hwmon = devm_hwmon_device_register_with_groups(dev, -+ "aspeed_g6_pwm_tacho", -+ priv, priv->groups); -+ -+ return PTR_ERR_OR_ZERO(hwmon); -+} -+ -+static const struct of_device_id of_pwm_tachometer_match_table[] = { -+ { .compatible = "aspeed,ast2600-pwm-tacho", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, of_pwm_tachometer_match_table); -+ -+static struct platform_driver aspeed_pwm_tachometer_driver = { -+ .probe = aspeed_pwm_tachometer_probe, -+ .driver = { -+ .name = "aspeed_g6_pwm_tacho", -+ .of_match_table = of_pwm_tachometer_match_table, -+ }, -+}; -+ -+module_platform_driver(aspeed_pwm_tachometer_driver); -+ -+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); -+MODULE_DESCRIPTION("ASPEED PWM and Fan Tachometer device driver"); -+MODULE_LICENSE("GPL"); --- -2.7.4 - |