kernel/0030-feat-add-fan-rtc-wdt-dw-spi-pwm.patch
2024-12-19 16:34:44 -05:00

1433 lines
39 KiB
Diff

From 703e01ff0938c136fcb67c5a09a460fcb0254268 Mon Sep 17 00:00:00 2001
From: xuxiang <xuxiang@eswincomputing.com>
Date: Fri, 24 May 2024 13:24:28 +0800
Subject: [PATCH 030/219] feat:add fan,rtc,wdt,dw-spi,pwm
Changelogs:
1. support pwm, fan control
2. support dw-spi
3. support rtc, dw-wdt
---
.../dts/eswin/eswin-win2030-die0-soc.dtsi | 6 +-
arch/riscv/configs/win2030_defconfig | 4 +-
drivers/hwmon/Kconfig | 9 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/eswin-fan-control.c | 522 ++++++++++++++++++
drivers/pwm/Kconfig | 9 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-eswin.c | 370 +++++++++++++
drivers/rtc/Kconfig | 8 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-eswin.c | 323 +++++++++++
drivers/spi/spi-dw-mmio.c | 9 +
12 files changed, 1261 insertions(+), 2 deletions(-)
create mode 100644 drivers/hwmon/eswin-fan-control.c
create mode 100644 drivers/pwm/pwm-eswin.c
create mode 100644 drivers/rtc/rtc-eswin.c
diff --git a/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi b/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi
index 70f8a9d9af3b..430c5410bdda 100644
--- a/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi
+++ b/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi
@@ -1292,6 +1292,8 @@ fan_control: fan_control@50b50000 {
pulses-per-revolution = <1>;
pwm-minimun-period = <1000>;
pwms = <&pwm0 0 100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fan_tach_default>;
status = "disabled";
};
@@ -1510,13 +1512,15 @@ portd: gpio-port@3 {
pwm0: pwm@0x50818000 {
compatible = "eswin,pwm-eswin";
+ #pwm-cells = <2>;
reg = <0x0 0x50818000 0x0 0x4000>;
clock-names = "pwm","pclk";
clocks = <&d0_clock WIN2030_CLK_LSP_TIMER_PCLK>;
clock-frequency = <200000000>;
resets = <&d0_reset TIMER_RST_CTRL SW_TIMER_RST_N>;
reset-names = "pwmrst";
- #pwm-cells = <2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm0_default>;
status = "disabled";
};
diff --git a/arch/riscv/configs/win2030_defconfig b/arch/riscv/configs/win2030_defconfig
index 0a03d049eff9..d0362cdd1b1e 100644
--- a/arch/riscv/configs/win2030_defconfig
+++ b/arch/riscv/configs/win2030_defconfig
@@ -247,7 +247,6 @@ CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_EDAC=y
CONFIG_EDAC_ESWIN=y
CONFIG_RTC_CLASS=y
-CONFIG_RTC_DRV_PCF8563=y
CONFIG_DMADEVICES=y
CONFIG_DW_AXI_DMAC=y
CONFIG_DMATEST=y
@@ -267,6 +266,9 @@ CONFIG_ARCH_ESWIN_EIC770X_SOC_FAMILY=y
CONFIG_EXTCON=y
CONFIG_MEMORY=y
CONFIG_PWM=y
+CONFIG_PWM_ESWIN=y
+CONFIG_SENSORS_ESWIN_FAN_CONTROL=y
+CONFIG_RTC_DRV_ESWIN=y
CONFIG_RESET_ESWIN_WIN2030=y
CONFIG_INTERCONNECT=y
CONFIG_EXT4_FS=y
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 14e8320b5c07..39185cb8e3df 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -312,6 +312,15 @@ config SENSORS_AXI_FAN_CONTROL
This driver can also be built as a module. If so, the module
will be called axi-fan-control
+config SENSORS_ESWIN_FAN_CONTROL
+ tristate "ESWIN FAN Control Core driver"
+ help
+ If you say yes here you get support for the Analog Devices
+ ESWIN FAN monitoring core.
+
+ This driver can also be built as a module. If so, the module
+ will be called eswin-fan-control
+
config SENSORS_K8TEMP
tristate "AMD Athlon64/FX or Opteron temperature sensor"
depends on X86 && PCI
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 6b5643ef316a..60f32f51eeb9 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -221,6 +221,7 @@ obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
obj-$(CONFIG_SENSORS_PAC1934) += pac193x.o
+obj-$(CONFIG_SENSORS_ESWIN_FAN_CONTROL) += eswin-fan-control.o
obj-$(CONFIG_SENSORS_OCC) += occ/
obj-$(CONFIG_SENSORS_PECI) += peci/
diff --git a/drivers/hwmon/eswin-fan-control.c b/drivers/hwmon/eswin-fan-control.c
new file mode 100644
index 000000000000..9c8ab39dee30
--- /dev/null
+++ b/drivers/hwmon/eswin-fan-control.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Fan Control CORE driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author: Han Min <hanmin@eswincomputing.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/wait.h>
+
+#define FAN_PWM_DUTY 0x0
+#define FAN_PWM_PERIOD 0x1
+#define FAN_PWM_FREE 0x2
+
+/* register map */
+#define REG_FAN_INT 0x0
+#define REG_FAN_RPM 0x4
+
+/* wait for 50 times pwm period to trigger read interrupt */
+#define TIMEOUT(period) nsecs_to_jiffies(50*(period))
+
+struct eswin_fan_control_data {
+ struct reset_control *fan_rst;
+ struct clk *clk;
+ void __iomem *base;
+ struct device *hdev;
+ unsigned long clk_rate;
+ int pwm_id;
+ struct pwm_device *pwm;
+ wait_queue_head_t wq;
+ bool wait_flag;
+ int irq;
+ /* pwm minimum period */
+ u32 min_period;
+ /* pulses per revolution */
+ u32 ppr;
+ /* revolutions per minute */
+ u32 rpm;
+};
+
+static inline void fan_iowrite(const u32 val, const u32 reg,
+ const struct eswin_fan_control_data *ctl)
+{
+ iowrite32(val, ctl->base + reg);
+}
+
+static inline u32 fan_ioread(const u32 reg,
+ const struct eswin_fan_control_data *ctl)
+{
+ return ioread32(ctl->base + reg);
+}
+
+static ssize_t eswin_fan_pwm_ctl_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct eswin_fan_control_data *ctl = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ long temp = 0;
+
+ if (FAN_PWM_DUTY == attr->index) {
+ temp = pwm_get_duty_cycle(ctl->pwm);
+ }
+ else if (FAN_PWM_PERIOD == attr->index) {
+ temp = pwm_get_period(ctl->pwm);
+ }
+ else {
+ dev_err(dev, "get error attr index 0x%x\n", attr->index);
+ }
+
+ return sprintf(buf, "%lu\n", temp);
+}
+
+static ssize_t eswin_fan_pwm_ctl_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct eswin_fan_control_data *ctl = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct pwm_state state;
+ int ret;
+
+ pwm_get_state(ctl->pwm, &state);
+
+ if (FAN_PWM_DUTY == attr->index) {
+ long val = 0;
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ state.duty_cycle = val;
+ }
+ else if (FAN_PWM_PERIOD == attr->index) {
+ long val = 0;
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+ if (val >= ctl->min_period)
+ state.period = val;
+ else
+ dev_err(dev, "invalid pwm period!\n");
+ }
+ else {
+ dev_err(dev, "get error attr index 0x%x\n", attr->index);
+ }
+
+ pwm_apply_state(ctl->pwm, &state);
+
+ return count;
+}
+
+static ssize_t eswin_fan_pwm_free_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct eswin_fan_control_data *ctl = dev_get_drvdata(dev);
+ long val;
+ int ret;
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val) {
+ pwm_put(ctl->pwm);
+ }
+
+ return count;
+}
+
+static long eswin_fan_control_get_pwm_duty(const struct eswin_fan_control_data *ctl)
+{
+ struct pwm_state state;
+ int duty;
+
+ pwm_get_state(ctl->pwm, &state);
+ duty = pwm_get_relative_duty_cycle(&state, 100);
+
+ return duty;
+}
+
+static long eswin_fan_control_get_fan_rpm(struct eswin_fan_control_data *ctl)
+{
+ unsigned int val;
+ long period, timeout;
+ int ret;
+
+ ctl->wait_flag = false;
+ period = pwm_get_period(ctl->pwm);
+ timeout = TIMEOUT(period);
+ if(!timeout)
+ timeout = TIMEOUT(ctl->min_period);
+
+ val = fan_ioread(REG_FAN_INT, ctl);
+ val = val | 0x1;
+ fan_iowrite(val, REG_FAN_INT, ctl);
+
+ /* wair read interrupt */
+ ret = wait_event_interruptible_timeout(ctl->wq,
+ ctl->wait_flag,
+ timeout);
+
+ if (!ret){
+ /* timeout, set rpm to 0 */
+ ctl->rpm = 0;
+ }
+
+ if(ctl->rpm)
+ ctl->rpm = DIV_ROUND_CLOSEST(60 * ctl->clk_rate, ctl->ppr * ctl->rpm);
+
+ return ret;
+}
+
+static int eswin_fan_control_read_fan(struct device *dev, u32 attr, long *val)
+{
+ struct eswin_fan_control_data *ctl = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_fan_input:
+ if(!eswin_fan_control_get_fan_rpm(ctl)){
+ dev_err(dev, "wait read interrupt timeout!\n");
+ }
+ *val = ctl->rpm;
+ return 0;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int eswin_fan_control_read_pwm(struct device *dev, u32 attr, long *val)
+{
+ struct eswin_fan_control_data *ctl = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ *val = eswin_fan_control_get_pwm_duty(ctl);
+ return 0;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int eswin_fan_control_set_pwm_duty(const long val, struct eswin_fan_control_data *ctl)
+{
+ struct pwm_state state;
+
+ pwm_get_state(ctl->pwm, &state);
+ pwm_set_relative_duty_cycle(&state, val, 100);
+ pwm_apply_state(ctl->pwm, &state);
+
+ return 0;
+}
+
+static int eswin_fan_control_write_pwm(struct device *dev, u32 attr, long val)
+{
+ struct eswin_fan_control_data *ctl = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ if((val < 0)||(val > 100))
+ return -EINVAL;
+ else
+ return eswin_fan_control_set_pwm_duty(val, ctl);
+ default:
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static int eswin_fan_control_read_labels(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ switch (type) {
+ case hwmon_fan:
+ *str = "FAN";
+ return 0;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int eswin_fan_control_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_fan:
+ return eswin_fan_control_read_fan(dev, attr, val);
+ case hwmon_pwm:
+ return eswin_fan_control_read_pwm(dev, attr, val);
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int eswin_fan_control_write(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_pwm:
+ return eswin_fan_control_write_pwm(dev, attr, val);
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static umode_t eswin_fan_control_fan_is_visible(const u32 attr)
+{
+ switch (attr) {
+ case hwmon_fan_input:
+ case hwmon_fan_label:
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static umode_t eswin_fan_control_pwm_is_visible(const u32 attr)
+{
+ switch (attr) {
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static umode_t eswin_fan_control_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_fan:
+ return eswin_fan_control_fan_is_visible(attr);
+ case hwmon_pwm:
+ return eswin_fan_control_pwm_is_visible(attr);
+ default:
+ return 0;
+ }
+}
+
+static irqreturn_t eswin_fan_control_irq_handler(int irq, void *data)
+{
+ struct eswin_fan_control_data *ctl = (struct eswin_fan_control_data *)data;
+ u32 status = 0;
+
+ status = fan_ioread(REG_FAN_INT, ctl);
+ if (0x3 == (status & 0x3)){
+ ctl->rpm = fan_ioread(REG_FAN_RPM, ctl);
+
+ /* clear interrupt */
+ fan_iowrite(0x5, REG_FAN_INT, ctl);
+
+ /* wake up fan_rpm read */
+ ctl->wait_flag = true;
+ wake_up_interruptible(&ctl->wq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int eswin_fan_control_init(struct eswin_fan_control_data *ctl,
+ const struct device_node *np)
+{
+ int ret;
+ /* get fan pulses per revolution */
+ ret = of_property_read_u32(np, "pulses-per-revolution", &ctl->ppr);
+ if (ret)
+ return ret;
+
+ /* 1, 2 and 4 are the typical and accepted values */
+ if (ctl->ppr != 1 && ctl->ppr != 2 && ctl->ppr != 4)
+ return -EINVAL;
+
+ /* get pwm minimum period */
+ ret = of_property_read_u32(np, "pwm-minimun-period", &ctl->min_period);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static void eswin_fan_control_remove(void *data)
+{
+ int ret;
+ struct eswin_fan_control_data *ctl = data;
+ pwm_put(ctl->pwm);
+ ret = reset_control_assert(ctl->fan_rst);
+ WARN_ON(0 != ret);
+ clk_disable_unprepare(ctl->clk);
+}
+
+static const struct hwmon_channel_info *eswin_fan_control_info[] = {
+ HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT),
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL),
+ NULL
+};
+
+static const struct hwmon_ops eswin_fan_control_hwmon_ops = {
+ .is_visible = eswin_fan_control_is_visible,
+ .read = eswin_fan_control_read,
+ .write = eswin_fan_control_write,
+ .read_string = eswin_fan_control_read_labels,
+};
+
+static const struct hwmon_chip_info eswin_chip_info = {
+ .ops = &eswin_fan_control_hwmon_ops,
+ .info = eswin_fan_control_info,
+};
+
+static SENSOR_DEVICE_ATTR_RW(fan_pwm_duty, eswin_fan_pwm_ctl, FAN_PWM_DUTY);
+static SENSOR_DEVICE_ATTR_RW(fan_pwm_period, eswin_fan_pwm_ctl, FAN_PWM_PERIOD);
+static SENSOR_DEVICE_ATTR_WO(fan_pwm_free, eswin_fan_pwm_free, FAN_PWM_FREE);
+
+static struct attribute *eswin_fan_control_attrs[] = {
+ &sensor_dev_attr_fan_pwm_duty.dev_attr.attr,
+ &sensor_dev_attr_fan_pwm_period.dev_attr.attr,
+ &sensor_dev_attr_fan_pwm_free.dev_attr.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(eswin_fan_control);
+
+static const struct of_device_id eswin_fan_control_of_match[] = {
+ { .compatible = "eswin-fan-control"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, eswin_fan_control_of_match);
+
+static int eswin_fan_control_probe(struct platform_device *pdev)
+{
+ struct eswin_fan_control_data *ctl;
+ struct clk *clk;
+ const struct of_device_id *id;
+ const char *name = "eswin_fan_control";
+ struct pwm_state state;
+ int ret;
+
+ id = of_match_node(eswin_fan_control_of_match, pdev->dev.of_node);
+ if (!id)
+ return -EINVAL;
+
+ ctl = devm_kzalloc(&pdev->dev, sizeof(*ctl), GFP_KERNEL);
+ if (!ctl)
+ return -ENOMEM;
+
+ ctl->base = devm_platform_ioremap_resource(pdev, 0);
+
+ if (IS_ERR(ctl->base))
+ return PTR_ERR(ctl->base);
+
+ ctl->clk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(ctl->clk)) {
+ dev_err(&pdev->dev, "Couldn't get the clock for fan-controller\n");
+ return -ENODEV;
+ }
+
+ ret = clk_prepare_enable(ctl->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable clock for fan-controller\n");
+ return ret;
+ }
+
+ ctl->clk_rate = clk_get_rate(ctl->clk);
+ if (!ctl->clk_rate)
+ return -EINVAL;
+
+ ctl->fan_rst = devm_reset_control_get_optional(&pdev->dev, "fan_rst");
+ if (IS_ERR_OR_NULL(ctl->fan_rst)) {
+ dev_err(&pdev->dev, "Failed to get fan_rst reset handle\n");
+ return -EFAULT;
+ }
+ ret = reset_control_reset(ctl->fan_rst);
+ WARN_ON(0 != ret);
+
+ init_waitqueue_head(&ctl->wq);
+
+ ctl->irq = platform_get_irq(pdev, 0);
+ if (ctl->irq < 0)
+ return ctl->irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, ctl->irq,
+ eswin_fan_control_irq_handler, NULL,
+ IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+ pdev->driver_override, ctl);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request an irq, %d", ret);
+ return ret;
+ }
+
+ ret = eswin_fan_control_init(ctl, pdev->dev.of_node);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialize device\n");
+ return ret;
+ }
+ ctl->pwm = pwm_get(&pdev->dev, NULL);
+ if (IS_ERR(ctl->pwm)) {
+ dev_dbg(&pdev->dev, "Unable to request PWM, trying legacy API\n");
+ }
+
+ if (IS_ERR(ctl->pwm)) {
+ ret = PTR_ERR(ctl->pwm);
+ dev_err(&pdev->dev, "Failed to request pwm device: %d\n", ret);
+ return ret;
+ }
+ pwm_enable(ctl->pwm);
+ pwm_init_state(ctl->pwm, &state);
+ state.duty_cycle = state.period/2;
+ ret = pwm_apply_state(ctl->pwm, &state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n",
+ ret);
+ }
+
+ ret = devm_add_action_or_reset(&pdev->dev, eswin_fan_control_remove, ctl);
+ if (ret)
+ return ret;
+
+ ctl->hdev = devm_hwmon_device_register_with_info(&pdev->dev,
+ name,
+ ctl,
+ &eswin_chip_info,
+ eswin_fan_control_groups);
+ dev_err(&pdev->dev, "eswin fan control init exit\n");
+ return PTR_ERR_OR_ZERO(ctl->hdev);
+}
+
+static struct platform_driver eswin_fan_control_driver = {
+ .driver = {
+ .name = "eswin_fan_control_driver",
+ .of_match_table = eswin_fan_control_of_match,
+ },
+ .probe = eswin_fan_control_probe,
+};
+module_platform_driver(eswin_fan_control_driver);
+
+MODULE_AUTHOR("Han Min <hanmin@eswincomputing.com>");
+MODULE_DESCRIPTION("ESWIN Fan Control CORE driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 8ebcddf91f7b..66e803d4b5a8 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -195,6 +195,15 @@ config PWM_DWC
To compile this driver as a module, choose M here: the module
will be called pwm-dwc.
+config PWM_ESWIN
+ tristate "DesignWare PWM Controller"
+ depends on PCI
+ help
+ PWM driver for Synopsys DWC PWM Controller.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-eswin.
+
config PWM_EP93XX
tristate "Cirrus Logic EP93xx PWM support"
depends on ARCH_EP93XX || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index c822389c2a24..4da24d757e1d 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
obj-$(CONFIG_PWM_CRC) += pwm-crc.o
obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o
obj-$(CONFIG_PWM_DWC) += pwm-dwc.o
+obj-$(CONFIG_PWM_ESWIN) += pwm-eswin.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
diff --git a/drivers/pwm/pwm-eswin.c b/drivers/pwm/pwm-eswin.c
new file mode 100644
index 000000000000..139474716da5
--- /dev/null
+++ b/drivers/pwm/pwm-eswin.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN pwm driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author: zhangchunyun@eswincomputing.com
+ */
+
+#include <linux/bitops.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/time.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+
+#define ESWIN_TIM_LD_CNT(n) ((n) * 0x14)
+#define ESWIN_TIM_LD_CNT2(n) (((n) * 4) + 0xb0)
+#define ESWIN_TIM_CUR_VAL(n) (((n) * 0x14) + 0x04)
+#define ESWIN_TIM_CTRL(n) (((n) * 0x14) + 0x08)
+#define ESWIN_TIM_EOI(n) (((n) * 0x14) + 0x0c)
+#define ESWIN_TIM_INT_STS(n) (((n) * 0x14) + 0x10)
+
+#define ESWIN_TIMERS_INT_STS 0xa0
+#define ESWIN_TIMERS_EOI 0xa4
+#define ESWIN_TIMERS_RAW_INT_STS 0xa8
+#define ESWIN_TIMERS_COMP_VERSION 0xac
+
+#define ESWIN_TIMERS_TOTAL 8
+#define NSEC_TO_SEC 1000000000
+
+/* Timer Control Register */
+#define ESWIN_TIM_CTRL_EN BIT(0)
+#define ESWIN_TIM_CTRL_MODE BIT(1)
+#define ESWIN_TIM_CTRL_MODE_FREE (0 << 1)
+#define ESWIN_TIM_CTRL_MODE_USER (1 << 1)
+#define ESWIN_TIM_CTRL_INT_MASK BIT(2)
+#define ESWIN_TIM_CTRL_PWM BIT(3)
+
+struct eswin_pwm_ctx {
+ u32 cnt;
+ u32 cnt2;
+ u32 ctrl;
+};
+
+struct eswin_pwm {
+ struct pwm_chip chip;
+ void __iomem *base;
+ struct clk *clk;
+ struct clk *pclk;
+ struct eswin_pwm_ctx ctx[ESWIN_TIMERS_TOTAL];
+ struct reset_control * pwm_rst;
+ u32 clk_period_ns;
+};
+
+#define to_eswin_pwm(p) (container_of((p), struct eswin_pwm, chip))
+
+static inline u32 eswin_pwm_readl(struct eswin_pwm *eswin, u32 offset)
+{
+ return readl(eswin->base + offset);
+}
+
+static inline void eswin_pwm_writel(struct eswin_pwm *eswin, u32 value, u32 offset)
+{
+ writel(value, eswin->base + offset);
+}
+
+static void __eswin_pwm_set_enable(struct eswin_pwm *eswin, int pwm, int enabled)
+{
+ u32 reg;
+
+ reg = eswin_pwm_readl(eswin, ESWIN_TIM_CTRL(pwm));
+
+ if (enabled)
+ reg |= ESWIN_TIM_CTRL_EN;
+ else
+ reg &= ~ESWIN_TIM_CTRL_EN;
+
+ eswin_pwm_writel(eswin, reg, ESWIN_TIM_CTRL(pwm));
+ reg = eswin_pwm_readl(eswin, ESWIN_TIM_CTRL(pwm));
+}
+
+static int __eswin_pwm_configure_timer(struct eswin_pwm *eswin,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ u64 tmp;
+ u32 ctrl;
+ u32 high;
+ u32 low;
+
+ /*
+ ¦* Calculate width of low and high period in terms of input clock
+ ¦* periods and check are the result within HW limits between 1 and
+ ¦* 2^32 periods.
+ ¦*/
+
+ tmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, eswin->clk_period_ns);
+ if (tmp < 1 || tmp > (1ULL << 32))
+ return -ERANGE;
+ high = tmp - 1;
+
+ tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle,
+ eswin->clk_period_ns);
+ if (tmp < 1 || tmp > (1ULL << 32))
+ return -ERANGE;
+ low = tmp - 1;
+ /*
+ ¦* Specification says timer usage flow is to disable timer, then
+ ¦* program it followed by enable. It also says Load Count is loaded
+ ¦* into timer after it is enabled - either after a disable or
+ ¦* a reset. Based on measurements it happens also without disable
+ ¦* whenever Load Count is updated. But follow the specification.
+ ¦*/
+ __eswin_pwm_set_enable(eswin, pwm->hwpwm, false);
+
+ /*
+ ¦* Write Load Count and Load Count 2 registers. Former defines the
+ ¦* width of low period and latter the width of high period in terms
+ ¦* multiple of input clock periods:
+ ¦* Width = ((Count + 1) * input clock period).
+ ¦*/
+ eswin_pwm_writel(eswin, low, ESWIN_TIM_LD_CNT(pwm->hwpwm));
+ eswin_pwm_writel(eswin, high, ESWIN_TIM_LD_CNT2(pwm->hwpwm));
+
+ /*
+ ¦* Set user-defined mode, timer reloads from Load Count registers
+ ¦* when it counts down to 0.
+ ¦* Set PWM mode, it makes output to toggle and width of low and high
+ ¦* periods are set by Load Count registers.
+ ¦*/
+ ctrl = ESWIN_TIM_CTRL_MODE_USER | ESWIN_TIM_CTRL_PWM;
+ eswin_pwm_writel(eswin, ctrl, ESWIN_TIM_CTRL(pwm->hwpwm));
+
+ /*
+ ¦* Enable timer. Output starts from low period.
+ ¦*/
+ __eswin_pwm_set_enable(eswin, pwm->hwpwm, state->enabled);
+
+ return 0;
+}
+
+static int eswin_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct eswin_pwm *eswin = to_eswin_pwm(chip);
+ struct pwm_state curstate;
+ int ret = 0;
+
+ ret = clk_enable(eswin->pclk);
+
+ ret = clk_enable(eswin->clk);
+
+ pwm_get_state(pwm, &curstate);
+
+ __eswin_pwm_configure_timer(eswin, pwm, state);
+
+ return 0;
+}
+
+static int eswin_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct eswin_pwm *eswin = to_eswin_pwm(chip);
+ u64 duty, period;
+
+ pm_runtime_get_sync(chip->dev);
+
+ state->enabled = !!(eswin_pwm_readl(eswin,
+ ESWIN_TIM_CTRL(pwm->hwpwm)) & ESWIN_TIM_CTRL_EN);
+
+ duty = eswin_pwm_readl(eswin, ESWIN_TIM_LD_CNT(pwm->hwpwm));
+ duty += 1;
+ duty *= eswin->clk_period_ns;
+ state->duty_cycle = duty;
+
+ period = eswin_pwm_readl(eswin, ESWIN_TIM_LD_CNT2(pwm->hwpwm));
+ period += 1;
+ period *= eswin->clk_period_ns;
+ period += duty;
+ state->period = period;
+
+ state->polarity = PWM_POLARITY_INVERSED;
+
+ pm_runtime_put_sync(chip->dev);
+
+ return 0;
+}
+
+
+static const struct pwm_ops eswin_pwm_ops = {
+ .apply = eswin_pwm_apply,
+ .get_state = eswin_pwm_get_state,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id eswin_pwm_dt_ids[] = {
+ { .compatible = "eswin,pwm-eswin", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, eswin_pwm_dt_ids);
+
+static int eswin_pwm_probe(struct platform_device *pdev)
+{
+ struct eswin_pwm *pc;
+ int ret, count;
+ struct resource *res;
+ int clk_rate;
+/* unsigned long *conf, *conf1, *conf2;
+ unsigned int val, val1, val2;
+
+ ret = of_property_read_u32_index(pdev->dev.of_node, "pinctrl-pwm", 0, &val);
+ if(ret){
+ dev_err(&pdev->dev, "Can't get pwm pin0\n");
+ return -1;
+ }
+
+ ret = of_property_read_u32_index(pdev->dev.of_node, "pinctrl-pwm", 1, &val1);
+ if(ret){
+ dev_err(&pdev->dev, "Can't get pwm pin1\n");
+ return -1;
+ }
+
+ ret = of_property_read_u32_index(pdev->dev.of_node, "pinctrl-pwm", 2, &val2);
+ if(ret){
+ dev_err(&pdev->dev, "Can't get pwm pin2\n");
+ return -1;
+ }
+ conf = (unsigned long *)(&val);
+ conf1 = (unsigned long *)(&val1);
+ conf2 = (unsigned long *)(&val2);
+
+ eswin_pinconf_cfg_set(NULL,147, conf,32);
+ eswin_pinconf_cfg_set(NULL,116, conf1,32);
+ eswin_pinconf_cfg_set(NULL,117, conf2,32);
+*/
+ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ pc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pc->base))
+ return PTR_ERR(pc->base);
+
+ pc->clk = devm_clk_get(&pdev->dev, "pwm");
+ if (IS_ERR(pc->clk)) {
+ pc->clk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(pc->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
+ "Can't get PWM clk\n");
+ }
+
+ count = of_count_phandle_with_args(pdev->dev.of_node,
+ "clocks", "#clock-cells");
+ if (count == 2)
+ pc->pclk = devm_clk_get(&pdev->dev, "pclk");
+ else
+ pc->pclk = pc->clk;
+
+ if (IS_ERR(pc->pclk)) {
+ ret = PTR_ERR(pc->pclk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Can't get APB clk: %d\n", ret);
+ return ret;
+ }
+
+ clk_rate = clk_get_rate(pc->pclk);
+ pc->clk_period_ns = DIV_ROUND_CLOSEST_ULL(NSEC_TO_SEC, clk_rate);
+ /* pwm reset init */
+ pc->pwm_rst = devm_reset_control_get_optional(&pdev->dev, "pwmrst");
+ if(IS_ERR_OR_NULL(pc->pwm_rst)) {
+ dev_err(&pdev->dev, "Failed to get pwmrst reset handle\n");
+ return -EFAULT;
+ }
+
+ ret = clk_prepare_enable(pc->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't prepare enable PWM clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(pc->pclk);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't prepare enable APB clk: %d\n", ret);
+ goto err_clk;
+ }
+
+ /* reset pwm */
+ ret = reset_control_assert(pc->pwm_rst);
+ WARN_ON(0 != ret);
+ ret = reset_control_deassert(pc->pwm_rst);
+ WARN_ON(0 != ret);
+
+ platform_set_drvdata(pdev, pc);
+
+ pc->chip.dev = &pdev->dev;
+ pc->chip.ops = &eswin_pwm_ops;
+ pc->chip.npwm = 3;
+
+ ret = pwmchip_add(&pc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ goto err_pclk;
+ }
+ dev_err(&pdev->dev, "eswin pwm init success \n");
+
+ return 0;
+
+err_pclk:
+ clk_disable_unprepare(pc->pclk);
+err_clk:
+ clk_disable_unprepare(pc->clk);
+
+ return ret;
+}
+
+static int eswin_pwm_remove(struct platform_device *pdev)
+{
+ struct eswin_pwm *pc = platform_get_drvdata(pdev);
+
+ pwmchip_remove(&pc->chip);
+
+ clk_disable_unprepare(pc->pclk);
+ clk_disable_unprepare(pc->clk);
+
+ return 0;
+}
+
+static struct platform_driver eswin_pwm_driver = {
+ .driver = {
+ .name = "eswin-pwm",
+ .of_match_table = eswin_pwm_dt_ids,
+ },
+ .probe = eswin_pwm_probe,
+ .remove = eswin_pwm_remove,
+};
+module_platform_driver(eswin_pwm_driver);
+
+MODULE_DESCRIPTION("eswin SoC PWM driver");
+MODULE_AUTHOR("zhangchunyun@eswincomputing.com");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 92f46a6312c2..e6a5d8083520 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1985,4 +1985,12 @@ config RTC_DRV_POLARFIRE_SOC
This driver can also be built as a module, if so, the module
will be called "rtc-mpfs".
+config RTC_DRV_ESWIN
+ tristate "eswin win2030 RTC"
+ help
+ If you say yes here you get support for the eswin win2030 real time
+ clock.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-eswin".
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index fd209883ee2e..feff57496fb9 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -184,3 +184,4 @@ obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o
obj-$(CONFIG_RTC_DRV_ZYNQMP) += rtc-zynqmp.o
+obj-$(CONFIG_RTC_DRV_ESWIN) += rtc-eswin.o
diff --git a/drivers/rtc/rtc-eswin.c b/drivers/rtc/rtc-eswin.c
new file mode 100644
index 000000000000..b4ca046d8ff8
--- /dev/null
+++ b/drivers/rtc/rtc-eswin.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN rtc driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author: zhangpengcheng@eswincomputing.com
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define RTC_INT_TO_U84 0xffff9fff
+/* RTC CSR Registers */
+#define RTC_CCVR 0x00
+#define RTC_CMR 0x04
+#define RTC_CLR 0x08
+#define RTC_CCR 0x0C
+#define RTC_CCR_IE BIT(0)
+#define RTC_CCR_MASK BIT(1)
+#define RTC_CCR_EN BIT(2)
+#define RTC_CCR_WEN BIT(3)
+#define RTC_CCR_PEN BIT(4)
+#define RTC_STAT 0x10
+#define RTC_STAT_BIT BIT(0)
+#define RTC_RSTAT 0x14
+#define RTC_EOI 0x18
+#define RTC_VER 0x1C
+#define RTC_CPSR 0x20
+#define RTC_CPCVR 0x24
+
+struct eswin_rtc_dev {
+ struct rtc_device *rtc;
+ struct device *dev;
+ unsigned long alarm_time;
+ void __iomem *csr_base;
+ struct clk *clk;
+ unsigned int irq_wake;
+ struct reset_control *rst_rtc;
+};
+
+static int eswin_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct eswin_rtc_dev *pdata = dev_get_drvdata(dev);
+ rtc_time64_to_tm(readl(pdata->csr_base + RTC_CCVR), tm);
+ return rtc_valid_tm(tm);
+}
+
+static int eswin_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct eswin_rtc_dev *pdata = dev_get_drvdata(dev);
+ unsigned long tr;
+
+ tr = rtc_tm_to_time64(tm);
+ writel(tr, pdata->csr_base + RTC_CLR);
+ readl(pdata->csr_base + RTC_CLR); /* Force a barrier */
+
+ return 0;
+}
+
+static int eswin_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct eswin_rtc_dev *pdata = dev_get_drvdata(dev);
+ rtc_time64_to_tm(pdata->alarm_time, &alrm->time);
+ alrm->enabled = readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE;
+
+ return 0;
+}
+
+static int eswin_rtc_alarm_irq_enable(struct device *dev, u32 enabled)
+{
+ struct eswin_rtc_dev *pdata = dev_get_drvdata(dev);
+ u32 ccr;
+
+ ccr = readl(pdata->csr_base + RTC_CCR);
+ if (enabled) {
+ ccr &= ~RTC_CCR_MASK;
+ ccr |= RTC_CCR_IE;
+ } else {
+ ccr &= ~RTC_CCR_IE;
+ ccr |= RTC_CCR_MASK;
+ }
+ writel(ccr, pdata->csr_base + RTC_CCR);
+
+ return 0;
+}
+
+static int eswin_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct eswin_rtc_dev *pdata = dev_get_drvdata(dev);
+ unsigned long rtc_time;
+ unsigned long alarm_time;
+ rtc_time = readl(pdata->csr_base + RTC_CCVR);
+ alarm_time = rtc_tm_to_time64(&alrm->time);
+
+ pdata->alarm_time = alarm_time;
+ writel((u32) pdata->alarm_time, pdata->csr_base + RTC_CMR);
+
+ eswin_rtc_alarm_irq_enable(dev, alrm->enabled);
+
+ return 0;
+}
+
+static const struct rtc_class_ops eswin_rtc_ops = {
+ .read_time = eswin_rtc_read_time,
+ .set_time = eswin_rtc_set_time,
+ .read_alarm = eswin_rtc_read_alarm,
+ .set_alarm = eswin_rtc_set_alarm,
+ .alarm_irq_enable = eswin_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t eswin_rtc_interrupt(int irq, void *id)
+{
+ struct eswin_rtc_dev *pdata = (struct eswin_rtc_dev *) id;
+ /* Check if interrupt asserted */
+ if (!(readl(pdata->csr_base + RTC_STAT) & RTC_STAT_BIT))
+ return IRQ_NONE;
+
+ /* Clear interrupt */
+ readl(pdata->csr_base + RTC_EOI);
+
+ rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static int eswin_rtc_probe(struct platform_device *pdev)
+{
+ struct eswin_rtc_dev *pdata;
+ struct resource *res;
+ int ret;
+ int irq;
+ unsigned int reg_val;
+ unsigned int int_off;
+ unsigned int clk_freq;
+ struct regmap *regmap;
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, pdata);
+ pdata->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pdata->csr_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pdata->csr_base))
+ return PTR_ERR(pdata->csr_base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ return irq;
+ }
+ ret = devm_request_irq(&pdev->dev, irq, eswin_rtc_interrupt, 0,
+ dev_name(&pdev->dev), pdata);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request IRQ\n");
+ return ret;
+ }
+
+ /* update RTC interrupt to u84 */
+ regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "eswin,syscfg");
+ if (IS_ERR(regmap)) {
+ dev_err(&pdev->dev, "No syscfg phandle specified\n");
+ return PTR_ERR(regmap);
+ }
+
+ ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscfg", 1, &int_off);
+ if (ret) {
+ dev_err(&pdev->dev, "No rtc interrupt offset found\n");
+ return -1;
+ }
+ regmap_read(regmap, int_off, &reg_val);
+ reg_val &= (RTC_INT_TO_U84);
+ regmap_write(regmap, int_off, reg_val);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &clk_freq);
+ if (ret) {
+ dev_err(&pdev->dev, "No rtc clock-frequency found\n");
+ }
+ /* rtc reset init*/
+ pdata->rst_rtc = devm_reset_control_get_optional(&pdev->dev, "rtcrst");
+ if (IS_ERR_OR_NULL(pdata->rst_rtc)) {
+ dev_err(&pdev->dev, "Failed to get rtcrst reset handle\n");
+ return -EFAULT;
+ }
+
+ /* get RTC clock */
+ pdata->clk = devm_clk_get(&pdev->dev, "rtcclk");
+ if (IS_ERR(pdata->clk)) {
+ dev_err(&pdev->dev, "Couldn't get the clock for RTC\n");
+ return -ENODEV;
+ }
+ /* Enable the clock */
+ clk_prepare_enable(pdata->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable RTC clock: %d\n", ret);
+ return -ENODEV;
+ }
+ /* reset rtc */
+ ret = reset_control_assert(pdata->rst_rtc);
+ WARN_ON(0 != ret);
+ ret = reset_control_deassert(pdata->rst_rtc);
+ WARN_ON(0 != ret);
+
+ /* Turn on the clock and the crystal */
+ reg_val = readl(pdata->csr_base + RTC_CCR);
+ writel(RTC_CCR_EN | reg_val, pdata->csr_base + RTC_CCR);
+
+ /* Turn on the prescaler and set the value */
+ writel(clk_freq, pdata->csr_base + RTC_CPSR);
+ reg_val = readl(pdata->csr_base + RTC_CCR);
+ writel(RTC_CCR_PEN | reg_val, pdata->csr_base + RTC_CCR);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &eswin_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pdata->rtc)) {
+ clk_disable_unprepare(pdata->clk);
+ return PTR_ERR(pdata->rtc);
+ }
+
+ return 0;
+}
+
+static int eswin_rtc_remove(struct platform_device *pdev)
+{
+ struct eswin_rtc_dev *pdata = platform_get_drvdata(pdev);
+
+ eswin_rtc_alarm_irq_enable(&pdev->dev, 0);
+ device_init_wakeup(&pdev->dev, 0);
+ clk_disable_unprepare(pdata->clk);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int eswin_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct eswin_rtc_dev *pdata = platform_get_drvdata(pdev);
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (device_may_wakeup(&pdev->dev)) {
+ if (!enable_irq_wake(irq))
+ pdata->irq_wake = 1;
+ } else {
+ eswin_rtc_alarm_irq_enable(dev, 0);
+ clk_disable(pdata->clk);
+ }
+
+ return 0;
+}
+
+static int eswin_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct eswin_rtc_dev *pdata = platform_get_drvdata(pdev);
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (device_may_wakeup(&pdev->dev)) {
+ if (pdata->irq_wake) {
+ disable_irq_wake(irq);
+ pdata->irq_wake = 0;
+ }
+ } else {
+ clk_enable(pdata->clk);
+ eswin_rtc_alarm_irq_enable(dev, 1);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(eswin_rtc_pm_ops, eswin_rtc_suspend, eswin_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id eswin_rtc_of_match[] = {
+ {.compatible = "eswin,win2030-rtc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, eswin_rtc_of_match);
+#endif
+
+static struct platform_driver eswin_rtc_driver = {
+ .probe = eswin_rtc_probe,
+ .remove = eswin_rtc_remove,
+ .driver = {
+ .name = "eswin-rtc",
+ .pm = &eswin_rtc_pm_ops,
+ .of_match_table = of_match_ptr(eswin_rtc_of_match),
+ },
+};
+
+module_platform_driver(eswin_rtc_driver);
+
+MODULE_DESCRIPTION("eswin win2030 RTC driver");
+MODULE_AUTHOR("zhangpengcheng@eswin.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index 805264c9c65c..39c1ae316ac0 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -210,6 +210,14 @@ static int dw_spi_alpine_init(struct platform_device *pdev,
return 0;
}
+static int dw_spi_eswin_init(struct platform_device *pdev,
+ struct dw_spi_mmio *dwsmmio)
+{
+ dw_spi_dma_setup_generic(&dwsmmio->dws);
+
+ return 0;
+}
+
static int dw_spi_pssi_init(struct platform_device *pdev,
struct dw_spi_mmio *dwsmmio)
{
@@ -432,6 +440,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = {
{ .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init},
{ .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init},
{ .compatible = "amd,pensando-elba-spi", .data = dw_spi_elba_init},
+ { .compatible = "snps,eic770x-spi", .data = dw_spi_eswin_init},
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
--
2.47.0