From a772d7bdde659d689fda47accc0f50bb6ce047d1 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Mon, 17 May 2021 13:11:24 -0700 Subject: [PATCH] Add Aspeed PWM uclass driver This commit adds Aspeed PWM uclass driver to set default FAN speed in u-boot. Signed-off-by: Jae Hyun Yoo --- arch/arm/dts/ast2600-intel.dts | 11 ++ arch/arm/dts/ast2600.dtsi | 88 ++++++++++++ board/aspeed/ast2600_intel/intel.c | 49 +++++++ drivers/pinctrl/aspeed/pinctrl_ast2600.c | 130 ++++++++++++++++- drivers/pwm/Kconfig | 8 ++ drivers/pwm/Makefile | 1 + drivers/pwm/aspeed_pwm.c | 172 +++++++++++++++++++++++ 7 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 drivers/pwm/aspeed_pwm.c diff --git a/arch/arm/dts/ast2600-intel.dts b/arch/arm/dts/ast2600-intel.dts index 7cae636554b6..a76193716d34 100644 --- a/arch/arm/dts/ast2600-intel.dts +++ b/arch/arm/dts/ast2600-intel.dts @@ -64,6 +64,17 @@ }; }; +&pwm { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default + &pinctrl_pwm2_default &pinctrl_pwm3_default + &pinctrl_pwm4_default &pinctrl_pwm5_default + &pinctrl_pwm12g1_default &pinctrl_pwm13g1_default + &pinctrl_pwm14g1_default &pinctrl_pwm15g1_default>; +}; + &gpio0 { status = "okay"; }; diff --git a/arch/arm/dts/ast2600.dtsi b/arch/arm/dts/ast2600.dtsi index 9c4282515d55..3ff5bf2e16e1 100644 --- a/arch/arm/dts/ast2600.dtsi +++ b/arch/arm/dts/ast2600.dtsi @@ -265,6 +265,14 @@ #size-cells = <1>; ranges; + pwm: pwm-controller@1e610000 { + compatible = "aspeed,ast2600-pwm"; + reg = <0x1e610000 0x100>; + clocks = <&scu ASPEED_CLK_AHB>; + resets = <&rst ASPEED_RESET_PWM>; + status = "disabled"; + }; + syscon: syscon@1e6e2000 { compatible = "aspeed,g6-scu", "syscon", "simple-mfd"; reg = <0x1e6e2000 0x1000>; @@ -1598,6 +1606,86 @@ groups = "PWM7"; }; + pinctrl_pwm8g1_default: pwm8g1_default { + function = "PWM8G1"; + groups = "PWM8G1"; + }; + + pinctrl_pwm9g1_default: pwm9g1_default { + function = "PWM9G1"; + groups = "PWM9G1"; + }; + + pinctrl_pwm10g1_default: pwm10g1_default { + function = "PWM10G1"; + groups = "PWM10G1"; + }; + + pinctrl_pwm11g1_default: pwm11g1_default { + function = "PWM11G1"; + groups = "PWM11G1"; + }; + + pinctrl_pwm12g1_default: pwm12g1_default { + function = "PWM12G1"; + groups = "PWM12G1"; + }; + + pinctrl_pwm13g1_default: pwm13g1_default { + function = "PWM13G1"; + groups = "PWM13G1"; + }; + + pinctrl_pwm14g1_default: pwm14g1_default { + function = "PWM14G1"; + groups = "PWM14G1"; + }; + + pinctrl_pwm15g1_default: pwm15g1_default { + function = "PWM15G1"; + groups = "PWM15G1"; + }; + + pinctrl_pwm8g0_default: pwm8g0_default { + function = "PWM8G0"; + groups = "PWM8G0"; + }; + + pinctrl_pwm9g0_default: pwm9g0_default { + function = "PWM9G0"; + groups = "PWM9G0"; + }; + + pinctrl_pwm10g0_default: pwm10g0_default { + function = "PWM10G0"; + groups = "PWM10G0"; + }; + + pinctrl_pwm11g0_default: pwm11g0_default { + function = "PWM11G0"; + groups = "PWM11G0"; + }; + + pinctrl_pwm12g0_default: pwm12g0_default { + function = "PWM12G0"; + groups = "PWM12G0"; + }; + + pinctrl_pwm13g0_default: pwm13g0_default { + function = "PWM13G0"; + groups = "PWM13G0"; + }; + + pinctrl_pwm14g0_default: pwm14g0_default { + function = "PWM14G0"; + groups = "PWM14G0"; + }; + + pinctrl_pwm15g0_default: pwm15g0_default { + function = "PWM15G0"; + groups = "PWM15G0"; + }; + pinctrl_rgmii1_default: rgmii1_default { function = "RGMII1"; groups = "RGMII1"; diff --git a/board/aspeed/ast2600_intel/intel.c b/board/aspeed/ast2600_intel/intel.c index 82df0ac6137e..f1136eec9ab9 100644 --- a/board/aspeed/ast2600_intel/intel.c +++ b/board/aspeed/ast2600_intel/intel.c @@ -8,6 +8,7 @@ #include #include #include +#include #define SYS_PWR_RESET_FLAG BIT(0) /* from scu_info.c */ #define WATCHDOG_RESET_BIT BIT(20) @@ -426,6 +427,53 @@ static void mailbox_init(void) } } +struct pwm_setting { + uint channel; + uint duty_pct; +}; + +static void pwm_init(void) +{ +#define NSEC_PER_SEC 1000000000L +#define PWM_TARGET_FREQ 25000 +#define PWM_TICK_NS (NSEC_PER_SEC / PWM_TARGET_FREQ) +#define PWM_TICK_1PCT_NS (PWM_TICK_NS / 100) + const struct pwm_setting settings[] = { + {0, 65}, + {1, 65}, + {2, 65}, + {3, 65}, + {4, 65}, + {5, 65}, + {12, 65}, + {13, 65}, + {14, 65}, + {15, 65}, + }; + struct udevice *dev; + int ret, setting_size, i; + + ret = uclass_first_device(UCLASS_PWM, &dev); + if (ret) { + debug("Can't find PWM controller: %d\n", ret); + return; + } + + setting_size = sizeof(settings) / sizeof(settings[0]); + + for (i = 0; i < setting_size; i++) { + ret = pwm_set_config(dev, settings[i].channel, PWM_TICK_NS, + settings[i].duty_pct * PWM_TICK_1PCT_NS); + if (!ret) { + ret = pwm_set_enable(dev, settings[i].channel, true); + if (ret) + debug("PWM enabling failed: %d\n", ret); + } else { + debug("PWM configure failed: %d\n", ret); + } + } +} + int board_early_init_f(void) { /* This is called before relocation; beware! */ @@ -576,6 +624,7 @@ int board_late_init(void) timer_callback, (void *)1); #endif + pwm_init(); espi_init(); /* Add reset reason to bootargs */ diff --git a/drivers/pinctrl/aspeed/pinctrl_ast2600.c b/drivers/pinctrl/aspeed/pinctrl_ast2600.c index 8a77a5d31556..980667f84e30 100644 --- a/drivers/pinctrl/aspeed/pinctrl_ast2600.c +++ b/drivers/pinctrl/aspeed/pinctrl_ast2600.c @@ -326,6 +326,110 @@ static struct aspeed_sig_desc pcie1rc_link[] = { { 0x500, BIT(24), 0 }, //dedicate rc reset }; +static struct aspeed_sig_desc pwm0[] = { + { 0x41C, BIT(16), 0 }, +}; + +static struct aspeed_sig_desc pwm1[] = { + { 0x41C, BIT(17), 0 }, +}; + +static struct aspeed_sig_desc pwm2[] = { + { 0x41C, BIT(18), 0 }, +}; + +static struct aspeed_sig_desc pwm3[] = { + { 0x41C, BIT(19), 0 }, +}; + +static struct aspeed_sig_desc pwm4[] = { + { 0x41C, BIT(20), 0 }, +}; + +static struct aspeed_sig_desc pwm5[] = { + { 0x41C, BIT(21), 0 }, +}; + +static struct aspeed_sig_desc pwm6[] = { + { 0x41C, BIT(22), 0 }, +}; + +static struct aspeed_sig_desc pwm7[] = { + { 0x41C, BIT(23), 0 }, +}; + +static struct aspeed_sig_desc pwm8g1[] = { + { 0x41C, BIT(24), 0 }, +}; + +static struct aspeed_sig_desc pwm9g1[] = { + { 0x41C, BIT(25), 0 }, +}; + +static struct aspeed_sig_desc pwm10g1[] = { + { 0x41C, BIT(26), 0 }, +}; + +static struct aspeed_sig_desc pwm11g1[] = { + { 0x41C, BIT(27), 0 }, +}; + +static struct aspeed_sig_desc pwm12g1[] = { + { 0x41C, BIT(28), 0 }, +}; + +static struct aspeed_sig_desc pwm13g1[] = { + { 0x41C, BIT(29), 0 }, +}; + +static struct aspeed_sig_desc pwm14g1[] = { + { 0x41C, BIT(30), 0 }, +}; + +static struct aspeed_sig_desc pwm15g1[] = { + { 0x41C, BIT(31), 0 }, +}; + +static struct aspeed_sig_desc pwm8g0[] = { + { 0x414, BIT(8), 1 }, + { 0x4B4, BIT(8), 0 }, +}; + +static struct aspeed_sig_desc pwm9g0[] = { + { 0x414, BIT(9), 1 }, + { 0x4B4, BIT(9), 0 }, +}; + +static struct aspeed_sig_desc pwm10g0[] = { + { 0x414, BIT(10), 1 }, + { 0x4B4, BIT(10), 0 }, +}; + +static struct aspeed_sig_desc pwm11g0[] = { + { 0x414, BIT(11), 1 }, + { 0x4B4, BIT(11), 0 }, +}; + +static struct aspeed_sig_desc pwm12g0[] = { + { 0x414, BIT(12), 1 }, + { 0x4B4, BIT(12), 0 }, +}; + +static struct aspeed_sig_desc pwm13g0[] = { + { 0x414, BIT(13), 1 }, + { 0x4B4, BIT(13), 0 }, +}; + +static struct aspeed_sig_desc pwm14g0[] = { + { 0x414, BIT(14), 1 }, + { 0x4B4, BIT(14), 0 }, +}; + +static struct aspeed_sig_desc pwm15g0[] = { + { 0x414, BIT(15), 1 }, + { 0x4B4, BIT(15), 0 }, +}; + static const struct aspeed_group_config ast2600_groups[] = { { "MAC1LINK", ARRAY_SIZE(mac1_link), mac1_link }, { "MAC2LINK", ARRAY_SIZE(mac2_link), mac2_link }, @@ -383,7 +487,31 @@ static const struct aspeed_group_config ast2600_groups[] = { { "USB2AH", ARRAY_SIZE(usb2ah_link), usb2ah_link }, { "USB2BH", ARRAY_SIZE(usb2bh_link), usb2bh_link }, { "PCIE0RC", ARRAY_SIZE(pcie0rc_link), pcie0rc_link }, - { "PCIE1RC", ARRAY_SIZE(pcie1rc_link), pcie1rc_link }, + { "PCIE1RC", ARRAY_SIZE(pcie1rc_link), pcie1rc_link }, + { "PWM0", ARRAY_SIZE(pwm0), pwm0 }, + { "PWM1", ARRAY_SIZE(pwm1), pwm1 }, + { "PWM2", ARRAY_SIZE(pwm2), pwm2 }, + { "PWM3", ARRAY_SIZE(pwm3), pwm3 }, + { "PWM4", ARRAY_SIZE(pwm4), pwm4 }, + { "PWM5", ARRAY_SIZE(pwm5), pwm5 }, + { "PWM6", ARRAY_SIZE(pwm6), pwm6 }, + { "PWM7", ARRAY_SIZE(pwm7), pwm7 }, + { "PWM8G1", ARRAY_SIZE(pwm8g1), pwm8g1 }, + { "PWM9G1", ARRAY_SIZE(pwm9g1), pwm9g1 }, + { "PWM10G1", ARRAY_SIZE(pwm10g1), pwm10g1 }, + { "PWM11G1", ARRAY_SIZE(pwm11g1), pwm11g1 }, + { "PWM12G1", ARRAY_SIZE(pwm12g1), pwm12g1 }, + { "PWM13G1", ARRAY_SIZE(pwm13g1), pwm13g1 }, + { "PWM14G1", ARRAY_SIZE(pwm14g1), pwm14g1 }, + { "PWM15G1", ARRAY_SIZE(pwm15g1), pwm15g1 }, + { "PWM8G0", ARRAY_SIZE(pwm8g0), pwm8g0 }, + { "PWM9G0", ARRAY_SIZE(pwm9g0), pwm9g0 }, + { "PWM10G0", ARRAY_SIZE(pwm10g0), pwm10g0 }, + { "PWM11G0", ARRAY_SIZE(pwm11g0), pwm11g0 }, + { "PWM12G0", ARRAY_SIZE(pwm12g0), pwm12g0 }, + { "PWM13G0", ARRAY_SIZE(pwm13g0), pwm13g0 }, + { "PWM14G0", ARRAY_SIZE(pwm14g0), pwm14g0 }, + { "PWM15G0", ARRAY_SIZE(pwm15g0), pwm15g0 }, }; static int ast2600_pinctrl_get_groups_count(struct udevice *dev) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 2984b7976637..95e82ee5ddf6 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -50,3 +50,11 @@ config PWM_SUNXI help This PWM is found on H3, A64 and other Allwinner SoCs. It supports a programmable period and duty cycle. A 16-bit counter is used. + +config PWM_ASPEED + bool "Enable support for the Aspeed AST2600 PWM" + depends on DM_PWM + depends on ASPEED_AST2600 + help + This PWM is found on Aspeed AST2600 SoC. It supports a programmable + period and duty cycle. A 16-bit counter is used. diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a837c35ed2e3..770b054c3f3b 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o +obj-$(CONFIG_PWM_ASPEED) += aspeed_pwm.o diff --git a/drivers/pwm/aspeed_pwm.c b/drivers/pwm/aspeed_pwm.c new file mode 100644 index 000000000000..bd9a911b4fe2 --- /dev/null +++ b/drivers/pwm/aspeed_pwm.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL +// Copyright (c) 2021 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NSEC_PER_SEC 1000000000L + +#define ASPEED_PWM_CTRL 0x00 /* PWM0 General Register */ +#define ASPEED_PWM_CTRL_CH(x) (((x) * 0x10) + ASPEED_PWM_CTRL) +#define PWM_LOAD_AS_WDT BIT(19) +#define PWM_DUTY_LOAD_AS_WDT_EN BIT(18) +#define PWM_DUTY_SYNC_DIS BIT(17) +#define PWM_CLK_ENABLE BIT(16) +#define PWM_LEVEL_OUTPUT BIT(15) +#define PWM_INVERSE BIT(14) +#define PWM_OPEN_DRAIN_EN BIT(13) +#define PWM_PIN_EN BIT(12) +#define PWM_CLK_DIV_H_MASK GENMASK(11, 8) +#define PWM_CLK_DIV_L_MASK GENMASK(7, 0) + +#define ASPEED_PWM_DUTY_CYCLE 0x04 /* PWM0 Duty Cycle Register */ +#define ASPEED_PWM_DUTY_CYCLE_CH(x) (((x) * 0x10) + ASPEED_PWM_DUTY_CYCLE) +#define PWM_PERIOD_MASK GENMASK(31, 24) +#define PWM_RISING_FALLING_AS_WDT_MASK GENMASK(23, 16) +#define PWM_RISING_POINT_MASK GENMASK(15, 8) +#define PWM_FALLING_POINT_MASK GENMASK(7, 0) + +#define PWM_PERIOD_MAX 255 + +struct aspeed_pwm_priv { + void __iomem *base; + ulong clk_freq; + u32 clk_tick_ns; +}; + +static int aspeed_pwm_set_config(struct udevice *dev, uint channel, + uint period_ns, uint duty_ns) +{ + struct aspeed_pwm_priv *priv = dev_get_priv(dev); + u8 div_h, div_l, period_value, falling_point, rising_point; + u32 ctrl_value, duty_value, tick_ns; + + /* + * We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1Hz is easily representable + * by 32bits. + */ + if (period_ns > NSEC_PER_SEC) + return -ERANGE; + + for (div_l = 0; div_l <= 0xff; div_l++) { + for (div_h = 0; div_h <= 0xf; div_h++) { + tick_ns = priv->clk_tick_ns * BIT(div_h) * (div_l + 1); + if (tick_ns * PWM_PERIOD_MAX >= period_ns) + break; + } + if (tick_ns * PWM_PERIOD_MAX >= period_ns) + break; + } + + if (period_ns / tick_ns > PWM_PERIOD_MAX) + return -ERANGE; + + ctrl_value = FIELD_PREP(PWM_CLK_DIV_H_MASK, div_h) | + FIELD_PREP(PWM_CLK_DIV_L_MASK, div_l); + period_value = period_ns / tick_ns; + falling_point = 0; + rising_point = duty_ns / tick_ns; + duty_value = FIELD_PREP(PWM_PERIOD_MASK, period_value) | + FIELD_PREP(PWM_RISING_POINT_MASK, rising_point) | + FIELD_PREP(PWM_FALLING_POINT_MASK, falling_point); + + clrsetbits_le32(priv->base + ASPEED_PWM_DUTY_CYCLE_CH(channel), + PWM_PERIOD_MASK | PWM_RISING_POINT_MASK | + PWM_FALLING_POINT_MASK, duty_value); + clrsetbits_le32(priv->base + ASPEED_PWM_CTRL_CH(channel), + PWM_CLK_DIV_H_MASK | PWM_CLK_DIV_L_MASK, ctrl_value); + + return 0; +} + +static int aspeed_pwm_set_enable(struct udevice *dev, uint channel, bool enable) +{ + struct aspeed_pwm_priv *priv = dev_get_priv(dev); + + debug("%s: Enable '%s' channel %u\n", __func__, dev->name, channel); + + clrsetbits_le32(priv->base + ASPEED_PWM_CTRL_CH(channel), + PWM_CLK_ENABLE | PWM_PIN_EN, + enable ? PWM_CLK_ENABLE | PWM_PIN_EN : 0); + + return 0; +} + +static int aspeed_pwm_ofdata_to_platdata(struct udevice *dev) +{ + struct aspeed_pwm_priv *priv = dev_get_priv(dev); + struct resource res_regs; + int ret; + + ret = dev_read_resource(dev, 0, &res_regs); + if (ret < 0) + return ret; + + priv->base = (void __iomem *)res_regs.start; + + return 0; +} + +static int aspeed_pwm_probe(struct udevice *dev) +{ + struct aspeed_pwm_priv *priv = dev_get_priv(dev); + struct reset_ctl reset_ctl; + struct clk hclk; + int ret; + + ret = clk_get_by_index(dev, 0, &hclk); + if (ret) { + pr_err("%s: could not get clock: %d\n", dev->name, ret); + return ret; + } + + priv->clk_freq = clk_get_rate(&hclk); + priv->clk_tick_ns = NSEC_PER_SEC / priv->clk_freq; + (void) clk_free(&hclk); + + ret = reset_get_by_index(dev, 0, &reset_ctl); + if (ret) { + pr_err("%s: Failed to get reset signal: %d\n", dev->name, ret); + return ret; + } + + ret = reset_assert(&reset_ctl); + if (!ret) { + mdelay(10); + ret = reset_deassert(&reset_ctl); + } + + return ret; +} + +static const struct pwm_ops aspeed_pwm_ops = { + .set_config = aspeed_pwm_set_config, + .set_enable = aspeed_pwm_set_enable, +}; + +static const struct udevice_id aspeed_pwm_ids[] = { + { .compatible = "aspeed,ast2600-pwm" }, + { } +}; + +U_BOOT_DRIVER(aspeed_pwm) = { + .name = "aspeed_pwm", + .id = UCLASS_PWM, + .of_match = aspeed_pwm_ids, + .ops = &aspeed_pwm_ops, + .ofdata_to_platdata = aspeed_pwm_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct aspeed_pwm_priv), + .probe = aspeed_pwm_probe, +}; + +MODULE_AUTHOR("Jae Hyun Yoo "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Aspeed AST2600 PWM Driver"); -- 2.17.1