From 65a1527ae85b93a1e60038635c0708b511ec1c2b Mon Sep 17 00:00:00 2001 From: xuxiang Date: Fri, 24 May 2024 13:24:28 +0800 Subject: [PATCH 030/222] 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 . + * + * Author: Han Min + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 . + * + * Author: zhangchunyun@eswincomputing.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + * + * Author: zhangpengcheng@eswincomputing.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ®_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