From: Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>
To: openbmc@lists.ozlabs.org, joel@jms.id.au
Cc: Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>
Subject: [PATCH linux v2 2/3] drivers: hwmon: ASPEED AST2500 PWM driver
Date: Tue, 8 Nov 2016 18:11:44 -0800 [thread overview]
Message-ID: <1478657505-23109-3-git-send-email-jaghu@google.com> (raw)
In-Reply-To: <1478657505-23109-1-git-send-email-jaghu@google.com>
The ASPEED AST2500 PWM controller can support upto 8 PWM output ports.
There are three different PWM sources(types M, N and O)
and each PWM output port can be assigned a particular PWM source.
There is a sysfs file through which the user can control
the duty cycle of a particular PWM port. The duty cycle can range from 0 to
100 percent.
v2:
- Merged the drivers for PWM controller and PWM device as one PWM driver.
Signed-off-by: Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>
---
drivers/hwmon/Kconfig | 5 +
drivers/hwmon/Makefile | 2 +-
drivers/hwmon/aspeed_ast2500_pwm.c | 662 +++++++++++++++++++++++++++++++++++++
drivers/hwmon/aspeed_ast2500_pwm.h | 128 +++++++
4 files changed, 796 insertions(+), 1 deletion(-)
create mode 100644 drivers/hwmon/aspeed_ast2500_pwm.c
create mode 100644 drivers/hwmon/aspeed_ast2500_pwm.h
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 3b34ba9..7edd94e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1800,6 +1800,11 @@ config SENSORS_ULTRA45
This driver provides support for the Ultra45 workstation environmental
sensors.
+config ASPEED_AST2500_PWM
+ tristate "ASPEED AST2500 PWM driver"
+ help
+ This driver provides support for ASPEED AST2500 PWM output ports.
+
if ACPI
comment "ACPI drivers"
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index c0f3201..23529c2 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -163,7 +163,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
-
+obj-$(CONFIG_ASPEED_AST2500_PWM) += aspeed_ast2500_pwm.o
obj-$(CONFIG_PMBUS) += pmbus/
ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
diff --git a/drivers/hwmon/aspeed_ast2500_pwm.c b/drivers/hwmon/aspeed_ast2500_pwm.c
new file mode 100644
index 0000000..fda49d7
--- /dev/null
+++ b/drivers/hwmon/aspeed_ast2500_pwm.c
@@ -0,0 +1,662 @@
+/*
+ * Aspeed AST2500 PWM device driver
+ * * Copyright (c) 2016 Google, 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/sysfs.h>
+
+#include "aspeed_pwm.h"
+
+struct ast_pwm_port_data {
+ u8 pwm_port;
+ u8 pwm_enable;
+ u8 pwm_type;
+ u8 pwm_duty_cycle;
+ void __iomem *base;
+};
+
+struct ast_pwm_controller_data {
+ void __iomem *base;
+};
+
+static inline void
+ast_pwm_write(void __iomem *base, u32 val, u32 reg)
+{
+ writel(val, base + reg);
+}
+
+static inline u32
+ast_pwm_read(void __iomem *base, u32 reg)
+{
+ u32 val = readl(base + reg);
+ return val;
+}
+
+static void
+ast_set_pwm_clock_enable(struct ast_pwm_controller_data *priv, u8 val)
+{
+ if (val) {
+ ast_pwm_write(priv->base,
+ ast_pwm_read(priv->base, AST_PTCR_CTRL) |
+ AST_PTCR_CTRL_CLK_EN, AST_PTCR_CTRL);
+ } else {
+ ast_pwm_write(priv->base,
+ ast_pwm_read(priv->base, AST_PTCR_CTRL) &
+ ~AST_PTCR_CTRL_CLK_EN, AST_PTCR_CTRL);
+ }
+}
+
+static void
+ast_set_pwm_clock_source(struct ast_pwm_controller_data *priv, u8 val)
+{
+ if (val) {
+ ast_pwm_write(priv->base,
+ ast_pwm_read(priv->base, AST_PTCR_CTRL) |
+ AST_PTCR_CTRL_CLK_SRC, AST_PTCR_CTRL);
+ } else {
+ ast_pwm_write(priv->base,
+ ast_pwm_read(priv->base, AST_PTCR_CTRL) &
+ ~AST_PTCR_CTRL_CLK_SRC, AST_PTCR_CTRL);
+ }
+}
+
+static void
+ast_set_pwm_clock_division_h(struct ast_pwm_controller_data *priv,
+ u8 pwm_type, u8 div_high)
+{
+ switch (pwm_type) {
+ case PWM_TYPE_M:
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base, 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:
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base, AST_PTCR_CLK_CTRL) &
+ ~AST_PTCR_CLK_CTRL_TYPEN_H_MASK) |
+ (div_high << AST_PTCR_CLK_CTRL_TYPEN_H),
+ AST_PTCR_CLK_CTRL);
+ break;
+ case PWM_TYPE_O:
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base, 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;
+ }
+}
+
+static void
+ast_set_pwm_clock_division_l(struct ast_pwm_controller_data *priv,
+ u8 pwm_type, u8 div_low)
+{
+ switch (pwm_type) {
+ case PWM_TYPE_M:
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base, 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:
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base, AST_PTCR_CLK_CTRL) &
+ ~AST_PTCR_CLK_CTRL_TYPEN_L_MASK) |
+ (div_low << AST_PTCR_CLK_CTRL_TYPEN_L),
+ AST_PTCR_CLK_CTRL);
+ break;
+ case PWM_TYPE_O:
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base, 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;
+ }
+}
+
+static void
+ast_set_pwm_clock_unit(struct ast_pwm_controller_data *priv, u8 pwm_type,
+ u8 unit)
+{
+ switch (pwm_type) {
+ case PWM_TYPE_M:
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base, 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:
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base, AST_PTCR_CLK_CTRL) &
+ ~AST_PTCR_CLK_CTRL_TYPEN_UNIT_MASK) |
+ (unit << AST_PTCR_CLK_CTRL_TYPEN_UNIT),
+ AST_PTCR_CLK_CTRL);
+ break;
+ case PWM_TYPE_O:
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base, 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;
+ }
+}
+
+static void
+ast_set_pwm_enable(struct ast_pwm_port_data *priv, u8 enable)
+{
+ u8 pwm_ch = priv->pwm_port;
+
+ switch (pwm_ch) {
+ case PWMA:
+ if (enable)
+ ast_pwm_write(priv->base,
+ ast_pwm_read(priv->base,
+ AST_PTCR_CTRL) |
+ AST_PTCR_CTRL_PWMA_EN,
+ AST_PTCR_CTRL);
+ else
+ ast_pwm_write(priv->base,
+ ast_pwm_read(priv->base,
+ AST_PTCR_CTRL) &
+ ~AST_PTCR_CTRL_PWMA_EN,
+ AST_PTCR_CTRL);
+ break;
+ case PWMB:
+ if (enable)
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL) |
+ AST_PTCR_CTRL_PWMB_EN),
+ AST_PTCR_CTRL);
+ else
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL) &
+ ~AST_PTCR_CTRL_PWMB_EN),
+ AST_PTCR_CTRL);
+ break;
+ case PWMC:
+ if (enable)
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL) |
+ AST_PTCR_CTRL_PWMC_EN),
+ AST_PTCR_CTRL);
+ else
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL) &
+ ~AST_PTCR_CTRL_PWMC_EN),
+ AST_PTCR_CTRL);
+ break;
+ case PWMD:
+ if (enable)
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL) |
+ AST_PTCR_CTRL_PWMD_EN),
+ AST_PTCR_CTRL);
+ else
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL) &
+ ~AST_PTCR_CTRL_PWMD_EN),
+ AST_PTCR_CTRL);
+ break;
+ case PWME:
+ if (enable)
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL_EXT) |
+ AST_PTCR_CTRL_PWME_EN),
+ AST_PTCR_CTRL_EXT);
+ else
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL_EXT) &
+ ~AST_PTCR_CTRL_PWME_EN),
+ AST_PTCR_CTRL_EXT);
+ break;
+ case PWMF:
+ if (enable)
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL_EXT) |
+ AST_PTCR_CTRL_PWMF_EN),
+ AST_PTCR_CTRL_EXT);
+ else
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL_EXT) &
+ ~AST_PTCR_CTRL_PWMF_EN),
+ AST_PTCR_CTRL_EXT);
+ break;
+ case PWMG:
+ if (enable)
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL_EXT) |
+ AST_PTCR_CTRL_PWMG_EN),
+ AST_PTCR_CTRL_EXT);
+ else
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL_EXT) &
+ ~AST_PTCR_CTRL_PWMG_EN),
+ AST_PTCR_CTRL_EXT);
+ break;
+ case PWMH:
+ if (enable)
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL_EXT) |
+ AST_PTCR_CTRL_PWMH_EN),
+ AST_PTCR_CTRL_EXT);
+ else
+ ast_pwm_write(priv->base,
+ (ast_pwm_read(priv->base,
+ AST_PTCR_CTRL_EXT) &
+ ~AST_PTCR_CTRL_PWMH_EN),
+ AST_PTCR_CTRL_EXT);
+ break;
+ }
+}
+
+static void
+ast_set_pwm_type(struct ast_pwm_port_data *priv, u8 type)
+{
+ u32 tmp1, tmp2;
+ u8 pwm_ch = priv->pwm_port;
+
+ tmp1 = ast_pwm_read(priv->base, AST_PTCR_CTRL);
+ tmp2 = ast_pwm_read(priv->base, 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);
+ ast_pwm_write(priv->base, tmp1, AST_PTCR_CTRL);
+
+ break;
+ case PWMB:
+ tmp1 &= ~AST_PTCR_CTRL_SET_PWMB_TYPE_MASK;
+ tmp1 |= AST_PTCR_CTRL_SET_PWMB_TYPE(type);
+ ast_pwm_write(priv->base, tmp1, AST_PTCR_CTRL);
+ break;
+ case PWMC:
+ tmp1 &= ~AST_PTCR_CTRL_SET_PWMC_TYPE_MASK;
+ tmp1 |= AST_PTCR_CTRL_SET_PWMC_TYPE(type);
+ ast_pwm_write(priv->base, tmp1, AST_PTCR_CTRL);
+ break;
+ case PWMD:
+ tmp1 &= ~AST_PTCR_CTRL_SET_PWMD_TYPE_MASK;
+ tmp1 |= AST_PTCR_CTRL_SET_PWMD_TYPE(type);
+ ast_pwm_write(priv->base, tmp1, AST_PTCR_CTRL);
+ break;
+ case PWME:
+ tmp2 &= ~AST_PTCR_CTRL_SET_PWME_TYPE_MASK;
+ tmp2 |= AST_PTCR_CTRL_SET_PWME_TYPE(type);
+ ast_pwm_write(priv->base, tmp2, AST_PTCR_CTRL_EXT);
+ break;
+ case PWMF:
+ tmp2 &= ~AST_PTCR_CTRL_SET_PWMF_TYPE_MASK;
+ tmp2 |= AST_PTCR_CTRL_SET_PWMF_TYPE(type);
+ ast_pwm_write(priv->base, tmp2, AST_PTCR_CTRL_EXT);
+ break;
+ case PWMG:
+ tmp2 &= ~AST_PTCR_CTRL_SET_PWMG_TYPE_MASK;
+ tmp2 |= AST_PTCR_CTRL_SET_PWMG_TYPE(type);
+ ast_pwm_write(priv->base, tmp2, AST_PTCR_CTRL_EXT);
+ break;
+ case PWMH:
+ tmp2 &= ~AST_PTCR_CTRL_SET_PWMH_TYPE_MASK;
+ tmp2 |= AST_PTCR_CTRL_SET_PWMH_TYPE(type);
+ ast_pwm_write(priv->base, tmp2, AST_PTCR_CTRL_EXT);
+ break;
+ }
+}
+
+static void
+ast_set_pwm_duty_rising(struct ast_pwm_port_data *priv, u8 rising)
+{
+ u32 tmp = 0;
+ u8 pwm_ch = priv->pwm_port;
+
+ switch (pwm_ch) {
+ case PWMA:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY0_CTRL);
+ tmp &= ~DUTY_CTRL_PWM1_RISE_POINT_MASK;
+ tmp |= rising;
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY0_CTRL);
+ break;
+ case PWMB:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY0_CTRL);
+ tmp &= ~DUTY_CTRL_PWM2_RISE_POINT_MASK;
+ tmp |= (rising << DUTY_CTRL_PWM2_RISE_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY0_CTRL);
+ break;
+ case PWMC:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY1_CTRL);
+ tmp &= ~DUTY_CTRL_PWM1_RISE_POINT_MASK;
+ tmp |= rising;
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY1_CTRL);
+ break;
+ case PWMD:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY1_CTRL);
+ tmp &= ~DUTY_CTRL_PWM2_RISE_POINT_MASK;
+ tmp |= (rising << DUTY_CTRL_PWM2_RISE_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY1_CTRL);
+ break;
+ case PWME:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY2_CTRL);
+ tmp &= ~DUTY_CTRL_PWM1_RISE_POINT_MASK;
+ tmp |= rising;
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY2_CTRL);
+ break;
+ case PWMF:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY2_CTRL);
+ tmp &= ~DUTY_CTRL_PWM2_RISE_POINT_MASK;
+ tmp |= (rising << DUTY_CTRL_PWM2_RISE_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY2_CTRL);
+ break;
+ case PWMG:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY3_CTRL);
+ tmp &= ~DUTY_CTRL_PWM1_RISE_POINT_MASK;
+ tmp |= rising;
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY3_CTRL);
+ break;
+ case PWMH:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY3_CTRL);
+ tmp &= ~DUTY_CTRL_PWM2_RISE_POINT_MASK;
+ tmp |= (rising << DUTY_CTRL_PWM2_RISE_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY3_CTRL);
+ break;
+ }
+}
+
+static void
+ast_set_pwm_duty_falling(struct ast_pwm_port_data *priv, u8 falling)
+{
+ u32 tmp = 0;
+ u8 pwm_ch = priv->pwm_port;
+
+ switch (pwm_ch) {
+ case PWMA:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY0_CTRL);
+ tmp &= ~DUTY_CTRL_PWM1_FALL_POINT_MASK;
+ tmp |= (falling << DUTY_CTRL_PWM1_FALL_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY0_CTRL);
+ break;
+ case PWMB:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY0_CTRL);
+ tmp &= ~DUTY_CTRL_PWM2_FALL_POINT_MASK;
+ tmp |= (falling << DUTY_CTRL_PWM2_FALL_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY0_CTRL);
+ break;
+ case PWMC:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY1_CTRL);
+ tmp &= ~DUTY_CTRL_PWM1_FALL_POINT_MASK;
+ tmp |= (falling << DUTY_CTRL_PWM1_FALL_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY1_CTRL);
+ break;
+ case PWMD:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY1_CTRL);
+ tmp &= ~DUTY_CTRL_PWM2_FALL_POINT_MASK;
+ tmp |= (falling << DUTY_CTRL_PWM2_FALL_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY1_CTRL);
+ break;
+ case PWME:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY2_CTRL);
+ tmp &= ~DUTY_CTRL_PWM1_FALL_POINT_MASK;
+ tmp |= (falling << DUTY_CTRL_PWM1_FALL_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY2_CTRL);
+ break;
+ case PWMF:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY2_CTRL);
+ tmp &= ~DUTY_CTRL_PWM2_FALL_POINT_MASK;
+ tmp |= (falling << DUTY_CTRL_PWM2_FALL_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY2_CTRL);
+ break;
+ case PWMG:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY3_CTRL);
+ tmp &= ~DUTY_CTRL_PWM1_FALL_POINT_MASK;
+ tmp |= (falling << DUTY_CTRL_PWM1_FALL_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY3_CTRL);
+ break;
+ case PWMH:
+ tmp = ast_pwm_read(priv->base, AST_PTCR_DUTY3_CTRL);
+ tmp &= ~DUTY_CTRL_PWM2_FALL_POINT_MASK;
+ tmp |= (falling << DUTY_CTRL_PWM2_FALL_POINT);
+ ast_pwm_write(priv->base, tmp, AST_PTCR_DUTY3_CTRL);
+ break;
+ }
+}
+
+static u8
+ast_get_pwm_clock_unit(struct ast_pwm_port_data *priv, u8 pwm_type)
+{
+ u8 tmp = 0;
+
+ switch (pwm_type) {
+ case PWM_TYPE_M:
+ tmp = (ast_pwm_read(priv->base, AST_PTCR_CLK_CTRL) &
+ AST_PTCR_CLK_CTRL_TYPEM_UNIT_MASK) >>
+ AST_PTCR_CLK_CTRL_TYPEM_UNIT;
+ break;
+ case PWM_TYPE_N:
+ tmp = (ast_pwm_read(priv->base, AST_PTCR_CLK_CTRL) &
+ AST_PTCR_CLK_CTRL_TYPEN_UNIT_MASK) >>
+ AST_PTCR_CLK_CTRL_TYPEN_UNIT;
+ break;
+ case PWM_TYPE_O:
+ tmp = (ast_pwm_read(priv->base, AST_PTCR_CLK_EXT_CTRL) &
+ AST_PTCR_CLK_CTRL_TYPEO_UNIT_MASK) >>
+ AST_PTCR_CLK_CTRL_TYPEO_UNIT;
+ break;
+ }
+ return tmp;
+}
+
+static void
+ast_set_pwm_duty_cycle(struct ast_pwm_port_data *priv, u8 duty_cycle)
+{
+ u8 period;
+ u8 dc_time_on;
+
+ period = ast_get_pwm_clock_unit(priv, priv->pwm_type);
+ period += 1;
+
+ dc_time_on = (duty_cycle * period) / 100;
+ if (dc_time_on == 0) {
+ ast_set_pwm_enable(priv, 0);
+ } else {
+ if (dc_time_on == period) {
+ dc_time_on = 0;
+ }
+ ast_set_pwm_duty_rising(priv, 0);
+ ast_set_pwm_duty_falling(priv, dc_time_on);
+ ast_set_pwm_enable(priv, 1);
+ }
+}
+
+static ssize_t
+set_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct ast_pwm_port_data *priv = dev_get_drvdata(dev);
+ u8 duty_cycle;
+ int ret;
+
+ ret = kstrtou8(buf, 10, &duty_cycle);
+ if (ret)
+ return -EINVAL;
+
+ if (duty_cycle < 0 || duty_cycle > 100)
+ return -EINVAL;
+
+ priv->pwm_duty_cycle = duty_cycle;
+ ast_set_pwm_duty_cycle(priv, duty_cycle);
+ return count;
+}
+
+static ssize_t
+show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ast_pwm_port_data *priv = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", priv->pwm_duty_cycle);
+}
+
+static SENSOR_DEVICE_ATTR(pwm, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0);
+
+static struct attribute *pwm_dev_attrs[] = {
+ &sensor_dev_attr_pwm.dev_attr.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(pwm_dev);
+
+static int
+aspeed_create_pwm_port(struct device *dev, struct device_node *child,
+ void __iomem *base)
+{
+ struct device *hwmon;
+ u8 val;
+ struct ast_pwm_port_data *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ hwmon = devm_hwmon_device_register_with_groups(dev, "pwm_port",
+ priv, pwm_dev_groups);
+ if (IS_ERR(hwmon)) {
+ dev_err(dev, "Failed to register hwmon device\n");
+ return PTR_ERR(hwmon);
+ }
+ priv->base = base;
+
+ of_property_read_u8(child, "pwm_port", &val);
+ priv->pwm_port = val;
+
+ of_property_read_u8(child, "pwm_enable", &val);
+ priv->pwm_enable = val;
+ ast_set_pwm_enable(priv, val);
+
+ of_property_read_u8(child, "pwm_type", &val);
+ priv->pwm_type = val;
+ ast_set_pwm_type(priv, val);
+
+ of_property_read_u8(child, "pwm_duty_cycle_percent", &val);
+ priv->pwm_duty_cycle = val;
+ ast_set_pwm_duty_cycle(priv, val);
+ return 0;
+}
+
+static int
+aspeed_ast2500_pwm_probe(struct platform_device *pdev)
+{
+ u32 buf[4];
+ struct device_node *np, *child;
+ struct resource *res;
+ u8 val;
+ int err;
+ struct ast_pwm_controller_data *priv;
+
+ np = pdev->dev.of_node;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ast_pwm_controller_data),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ return -ENOENT;
+ }
+
+ priv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!priv->base)
+ return -ENOMEM;
+
+ of_property_read_u8(np, "clock_enable", &val);
+ ast_set_pwm_clock_enable(priv, val);
+ of_property_read_u8(np, "clock_source", &val);
+ ast_set_pwm_clock_source(priv, val);
+
+ of_property_read_u32_array(np, "typem_pwm_clock", buf, 4);
+ if (buf[0] == 1) {
+ ast_set_pwm_clock_division_l(priv, PWM_TYPE_M, buf[1]);
+ ast_set_pwm_clock_division_h(priv, PWM_TYPE_M, buf[2]);
+ ast_set_pwm_clock_unit(priv, PWM_TYPE_M, buf[3]);
+ }
+
+ of_property_read_u32_array(np, "typen_pwm_clock", buf, 4);
+ if (buf[0] == 1) {
+ ast_set_pwm_clock_division_l(priv, PWM_TYPE_N, buf[1]);
+ ast_set_pwm_clock_division_h(priv, PWM_TYPE_N, buf[2]);
+ ast_set_pwm_clock_unit(priv, PWM_TYPE_N, buf[3]);
+ }
+
+ of_property_read_u32_array(np, "typeo_pwm_clock", buf, 4);
+ if (buf[0] == 1) {
+ ast_set_pwm_clock_division_l(priv, PWM_TYPE_O, buf[1]);
+ ast_set_pwm_clock_division_h(priv, PWM_TYPE_O, buf[2]);
+ ast_set_pwm_clock_unit(priv, PWM_TYPE_O, buf[3]);
+ }
+
+ for_each_child_of_node(np, child) {
+ aspeed_create_pwm_port(&pdev->dev,
+ child, priv->base);
+ of_node_put(child);
+ }
+ of_node_put(np);
+ return 0;
+}
+
+static int
+aspeed_ast2500_pwm_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id of_pwm_match_table[] = {
+ { .compatible = "aspeed,ast2500-pwm", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_pwm_match_table);
+
+static struct platform_driver aspeed_ast2500_pwm_driver = {
+ .probe = aspeed_ast2500_pwm_probe,
+ .remove = aspeed_ast2500_pwm_remove,
+ .driver = {
+ .name = "aspeed_ast2500_pwm",
+ .owner = THIS_MODULE,
+ .of_match_table = of_pwm_match_table,
+ },
+};
+
+module_platform_driver(aspeed_ast2500_pwm_driver);
+
+MODULE_AUTHOR("Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>");
+MODULE_DESCRIPTION("ASPEED AST2500 PWM device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/aspeed_ast2500_pwm.h b/drivers/hwmon/aspeed_ast2500_pwm.h
new file mode 100644
index 0000000..1986fd2
--- /dev/null
+++ b/drivers/hwmon/aspeed_ast2500_pwm.h
@@ -0,0 +1,128 @@
+/*
+ * ASPEED AST2500 PWM header file
+ * * Copyright (c) 2016 Google, 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.
+ *
+ */
+
+#ifndef __ASPEED_PWM_H
+#define __ASPEED_PWM_H __FILE__
+
+/* AST 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_CTRL_EXT 0x40
+#define AST_PTCR_CLK_EXT_CTRL 0x44
+#define AST_PTCR_DUTY2_CTRL 0x48
+#define AST_PTCR_DUTY3_CTRL 0x4c
+
+/* COMMON Definition */
+#define PWM_TYPE_M 0x0
+#define PWM_TYPE_N 0x1
+#define PWM_TYPE_O 0x2
+
+#define PWMA 0x0
+#define PWMB 0x1
+#define PWMC 0x2
+#define PWMD 0x3
+#define PWME 0x4
+#define PWMF 0x5
+#define PWMG 0x6
+#define PWMH 0x7
+
+#define DUTY_CTRL_PWM2_FALL_POINT (24)
+#define DUTY_CTRL_PWM2_FALL_POINT_MASK (0xff<<24)
+#define DUTY_CTRL_PWM2_RISE_POINT (16)
+#define DUTY_CTRL_PWM2_RISE_POINT_MASK (0xff<<16)
+#define DUTY_CTRL_PWM1_FALL_POINT (8)
+#define DUTY_CTRL_PWM1_FALL_POINT_MASK (0xff<<8)
+#define DUTY_CTRL_PWM1_RISE_POINT (0)
+#define DUTY_CTRL_PWM1_RISE_POINT_MASK (0xff)
+
+/* AST_PTCR_CTRL : 0x00 - General Control Register */
+#define AST_PTCR_CTRL_SET_PWMD_TYPE(x) ((x & 0x1) << 15 | \
+ (x & 0x2) << 6)
+#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_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_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_SET_PWMA_TYPE_MASK ((0x1 << 4) | (0x1 << 12))
+
+#define AST_PTCR_CTRL_PWMD (11)
+#define AST_PTCR_CTRL_PWMD_EN (0x1 << 11)
+#define AST_PTCR_CTRL_PWMC (10)
+#define AST_PTCR_CTRL_PWMC_EN (0x1 << 10)
+#define AST_PTCR_CTRL_PWMB (9)
+#define AST_PTCR_CTRL_PWMB_EN (0x1 << 9)
+#define AST_PTCR_CTRL_PWMA (8)
+#define AST_PTCR_CTRL_PWMA_EN (0x1 << 8)
+
+/*0:24Mhz, 1:MCLK */
+#define AST_PTCR_CTRL_CLK_SRC 0x2
+#define AST_PTCR_CTRL_CLK_EN 0x1
+
+/* AST_PTCR_CLK_CTRL : 0x04 - 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_CTRL_EXT : 0x40 - General Control Extension #1 Register */
+#define AST_PTCR_CTRL_SET_PWMH_TYPE(x) ((x & 0x1) << 15 | \
+ (x & 0x2) << 6)
+#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_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_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_SET_PWME_TYPE_MASK ((0x1 << 4) | (0x1 << 12))
+
+#define AST_PTCR_CTRL_PWMH (11)
+#define AST_PTCR_CTRL_PWMH_EN (0x1 << 11)
+#define AST_PTCR_CTRL_PWMG (10)
+#define AST_PTCR_CTRL_PWMG_EN (0x1 << 10)
+#define AST_PTCR_CTRL_PWMF (9)
+#define AST_PTCR_CTRL_PWMF_EN (0x1 << 9)
+#define AST_PTCR_CTRL_PWME (8)
+#define AST_PTCR_CTRL_PWME_EN (0x1 << 8)
+
+/* AST_PTCR_CLK_EXT_CTRL : 0x44 - Clock Control Extension #1 Register */
+/* 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)
+
+#endif /* __ASPEED_PWM_H */
--
2.8.0.rc3.226.g39d4020
next prev parent reply other threads:[~2016-11-09 2:12 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-11-09 2:11 [PATCH linux v2 0/3] ASPEED AST2500 PWM support Jaghathiswari Rankappagounder Natarajan
2016-11-09 2:11 ` [PATCH linux v2 1/3] devicetree: binding documentation update for ASPEED AST2500 PWM driver Jaghathiswari Rankappagounder Natarajan
2016-11-10 0:35 ` Joel Stanley
2016-11-21 7:50 ` Jaghathiswari Rankappagounder Natarajan
2016-11-09 2:11 ` Jaghathiswari Rankappagounder Natarajan [this message]
2016-11-09 7:15 ` [PATCH linux v2 2/3] drivers: hwmon: " Joel Stanley
2016-11-24 9:26 ` Jaghathiswari Rankappagounder Natarajan
2016-11-28 5:14 ` Joel Stanley
2016-11-09 2:11 ` [PATCH linux v2 3/3] devicetree : Add support in Zaius platform for 4 PWM output ports Jaghathiswari Rankappagounder Natarajan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1478657505-23109-3-git-send-email-jaghu@google.com \
--to=jaghu@google.com \
--cc=joel@jms.id.au \
--cc=openbmc@lists.ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.