+// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (C) 2018 Starfive, Inc
+ * Copyright (C) 2018 StarFive, 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.
+ * under the terms of the GNU General Public License version 2, as published
+ * by the Free Software Foundation.
*/
#include <dt-bindings/pwm/pwm.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/clk.h>
+#include <linux/reset.h>
#include <linux/io.h>
/* max channel of pwm */
#define MAX_PWM 8
/* PTC Register offsets */
-#define REG_RPTC_CNTR 0x0
+#define REG_RPTC_CNTR 0x0
#define REG_RPTC_HRC 0x4
#define REG_RPTC_LRC 0x8
#define REG_RPTC_CTRL 0xC
#define PTC_DEBUG 0
-/* Access PTC register (cntr hrc lrc and ctrl) ,need to replace PWM_BASE_ADDR */
-#define REG_PTC_BASE_ADDR_SUB(base, N) ((base) + ((N>3)?((N%4)*0x10+(1<<15)):(N*0x10)))
-#define REG_PTC_RPTC_CNTR(base,N) (REG_PTC_BASE_ADDR_SUB(base,N))
-#define REG_PTC_RPTC_HRC(base,N) (REG_PTC_BASE_ADDR_SUB(base,N) + 0x4)
-#define REG_PTC_RPTC_LRC(base,N) (REG_PTC_BASE_ADDR_SUB(base,N) + 0x8)
-#define REG_PTC_RPTC_CTRL(base,N) (REG_PTC_BASE_ADDR_SUB(base,N) + 0xC)
-
-///PTC_RPTC_CTRL
-#define PTC_EN (1<<0)
-#define PTC_ECLK (1<<1) /* 1:ptc_ecgt signal increment RPTC_CNTR. 0:system clock increment RPTC_CNTR. */
-#define PTC_NEC (1<<2) /* gate:system clock or ptc_ecgt input signal to increment RPTC_CNTR. If gate function is enabled, PWM periods can be automatically adjusted with the capture input. */
-#define PTC_OE (1<<3) /* enbale PWM output */
-#define PTC_SIGNLE (1<<4) /* 1:single operation; 0:continue operation */
-#define PTC_INTE (1<<5) /* Timer/Counter interrput enable */
-#define PTC_INT (1<<6) /* interrupt status, write 1 to clear */
-#define PTC_CNTRRST (1<<7) /* 0:clear reset */
-#define PTC_CAPTE (1<<8) /* ptc_capt to increment RPTC_CNTR.*/
-
+/*
+ * Access PTC register (cntr hrc lrc and ctrl),
+ * need to replace PWM_BASE_ADDR
+ */
+#define REG_PTC_BASE_ADDR_SUB(base, N) \
+((base) + (((N) > 3) ? (((N) % 4) * 0x10 + (1 << 15)) : ((N) * 0x10)))
+#define REG_PTC_RPTC_CNTR(base, N) (REG_PTC_BASE_ADDR_SUB(base, N))
+#define REG_PTC_RPTC_HRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x4)
+#define REG_PTC_RPTC_LRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x8)
+#define REG_PTC_RPTC_CTRL(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0xC)
+
+/* PTC_RPTC_CTRL */
+#define PTC_EN BIT(0)
+#define PTC_ECLK BIT(1)
+#define PTC_NEC BIT(2)
+#define PTC_OE BIT(3)
+#define PTC_SIGNLE BIT(4)
+#define PTC_INTE BIT(5)
+#define PTC_INT BIT(6)
+#define PTC_CNTRRST BIT(7)
+#define PTC_CAPTE BIT(8)
/* pwm ptc device */
struct starfive_pwm_ptc_device {
struct pwm_chip chip;
struct clk *clk;
+ struct reset_control *rst;
void __iomem *regs;
- int irq;
- /* apb clock frequency , from dts */
+ int irq;
+ /* apb clock frequency, from dts */
unsigned int approx_period;
};
-static inline struct starfive_pwm_ptc_device *chip_to_starfive_ptc(struct pwm_chip *c)
+static inline struct starfive_pwm_ptc_device *
+chip_to_starfive_ptc(struct pwm_chip *c)
{
return container_of(c, struct starfive_pwm_ptc_device, chip);
}
-
-static void starfive_pwm_ptc_get_state(struct pwm_chip *chip, struct pwm_device *dev, struct pwm_state *state)
+static void starfive_pwm_ptc_get_state(struct pwm_chip *chip,
+ struct pwm_device *dev,
+ struct pwm_state *state)
{
struct starfive_pwm_ptc_device *pwm = chip_to_starfive_ptc(chip);
- uint32_t data_lrc;
- uint32_t data_hrc;
- uint32_t period;
- uint32_t pwm_clk_ns = 0;
+ u32 data_lrc;
+ u32 data_hrc;
+ u32 period;
+ u32 pwm_clk_ns = 0;
/* get lrc and hrc data from registe*/
- data_lrc = ioread32(REG_PTC_RPTC_LRC(pwm->regs,dev->hwpwm));
- data_hrc = ioread32(REG_PTC_RPTC_HRC(pwm->regs,dev->hwpwm));
- //period = data_lrc + data_hrc;
+ data_lrc = ioread32(REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm));
+ data_hrc = ioread32(REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm));
/* how many ns does apb clock elapse */
pwm_clk_ns = NS_1 / pwm->approx_period;
/* pwm period(ns) */
- state->period = data_lrc*pwm_clk_ns;
+ state->period = data_lrc * pwm_clk_ns;
- /* duty cycle(ns) ,means high level eclapse ns if it is normal polarity */
- state->duty_cycle = data_hrc*pwm_clk_ns;
+ /* duty cycle(ns), means high level eclapse ns if it is normal polarity */
+ state->duty_cycle = data_hrc * pwm_clk_ns;
- /* polarity,we don't use it now because it is not in dts */
- state->polarity = PWM_POLARITY_NORMAL;
+ /* polarity, we don't use it now because it is not in dts */
+ state->polarity = PWM_POLARITY_NORMAL;
/* enabled or not */
- state->enabled = 1;
-#if PTC_DEBUG
- printk("starfive_pwm_ptc_get_state in,no:%d....\r\n",dev->hwpwm);
- printk("data_hrc:0x%x 0x%x \n", data_hrc, data_lrc);
- printk("period:%d\r\n",state->period);
- printk("duty_cycle:%d\r\n",state->duty_cycle);
- printk("polarity:%d\r\n",state->polarity);
- printk("enabled:%d\r\n",state->enabled);
+ state->enabled = 1;
+#if PTC_DEBUG
+ dev_info(chip->dev, "%s in,no:%d....\r\n", __func__, dev->hwpwm);
+ dev_info(chip->dev, "data_hrc:0x%x 0x%x\n", data_hrc, data_lrc);
+ dev_info(chip->dev, "period:%d\r\n", state->period);
+ dev_info(chip->dev, "duty_cycle:%d\r\n", state->duty_cycle);
+ dev_info(chip->dev, "polarity:%d\r\n", state->polarity);
+ dev_info(chip->dev, "enabled:%d\r\n", state->enabled);
#endif
-
}
-
-static int starfive_pwm_ptc_apply(struct pwm_chip *chip, struct pwm_device *dev, struct pwm_state *state)
+static int starfive_pwm_ptc_apply(struct pwm_chip *chip,
+ struct pwm_device *dev,
+ struct pwm_state *state)
{
struct starfive_pwm_ptc_device *pwm = chip_to_starfive_ptc(chip);
- uint32_t pwm_clk_ns = 0;
- uint32_t data_hrc = 0;
- uint32_t data_lrc = 0;
- uint32_t period_data = 0;
- uint32_t duty_data = 0;
- void __iomem* reg_addr;
-
-#if PTC_DEBUG
- printk("starfive_pwm_ptc_apply in,no:%d....\r\n",dev->hwpwm);
- printk("set parameter......\r\n");
- printk("period:%d\r\n",state->period);
- printk("duty_cycle:%d\r\n",state->duty_cycle);
- printk("polarity:%d\r\n",state->polarity);
- printk("enabled:%d\r\n",state->enabled);
+ u32 pwm_clk_ns = 0;
+ u32 data_hrc = 0;
+ u32 data_lrc = 0;
+ u32 period_data = 0;
+ u32 duty_data = 0;
+ void __iomem *reg_addr;
+
+#if PTC_DEBUG
+ dev_info(chip->dev, "%s in,no:%d....\r\n", __func__, dev->hwpwm);
+ dev_info(chip->dev, "set parameter......\r\n");
+ dev_info(chip->dev, "period:%d\r\n", state->period);
+ dev_info(chip->dev, "duty_cycle:%d\r\n", state->duty_cycle);
+ dev_info(chip->dev, "polarity:%d\r\n", state->polarity);
+ dev_info(chip->dev, "enabled:%d\r\n", state->enabled);
#endif
/* duty_cycle should be less or equal than period */
- if(state->duty_cycle > state->period)
- {
+ if (state->duty_cycle > state->period)
state->duty_cycle = state->period;
- }
/* calculate pwm real period (ns) */
pwm_clk_ns = NS_1 / pwm->approx_period;
-
+
#if PTC_DEBUG
- printk("approx_period,:%d,pwm_clk_ns:%d\r\n",pwm->approx_period,pwm_clk_ns);
+ dev_info(chip->dev, "approx_period,:%d,pwm_clk_ns:%d\r\n",
+ pwm->approx_period, pwm_clk_ns);
#endif
/* calculate period count */
period_data = state->period / pwm_clk_ns;
- if (!state->enabled)
- {
- /* if is unenable,just set duty_dat to 0 , means low level always */
+ if (!state->enabled) {
+ /* if is unenable, just set duty_dat to 0, means low level always */
duty_data = 0;
- }
- else
- {
+ } else {
/* calculate duty count*/
duty_data = state->duty_cycle / pwm_clk_ns;
}
#if PTC_DEBUG
- printk("period_data:%d,duty_data:%d\r\n",period_data,duty_data);
+ dev_info(chip->dev, "period_data:%d,duty_data:%d\r\n",
+ period_data, duty_data);
#endif
- if(state->polarity == PWM_POLARITY_NORMAL)
- {
- /* calculate data_hrc */
- data_hrc = period_data - duty_data;
- }
- else
- {
+ if (state->polarity == PWM_POLARITY_NORMAL) {
+ /* calculate data_hrc */
+ data_hrc = period_data - duty_data;
+ } else {
/* calculate data_hrc */
- data_hrc = duty_data;
+ data_hrc = duty_data;
}
data_lrc = period_data;
/* set hrc */
- reg_addr = REG_PTC_RPTC_HRC(pwm->regs,dev->hwpwm);
+ reg_addr = REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm);
#if PTC_DEBUG
- printk("[starfive_pwm_ptc_config]reg_addr:0x%lx,HRC data:0x%x....\n",reg_addr,data_hrc);
+ dev_info(chip->dev, "[pwm_reg]reg_addr:0x%lx,HRC data:0x%x....\n",
+ reg_addr, data_hrc);
#endif
iowrite32(data_hrc, reg_addr);
-
/* set lrc */
- reg_addr = REG_PTC_RPTC_LRC(pwm->regs,dev->hwpwm);
+ reg_addr = REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm);
#if PTC_DEBUG
- printk("[starfive_pwm_ptc_config]reg_addr:0x%lx,LRC data:0x%x....\n",reg_addr,data_lrc);
-#endif
- iowrite32(data_lrc, reg_addr);
+ dev_info(chip->dev, "[pwm_reg]reg_addr:0x%lx,LRC data:0x%x....\n",
+ reg_addr, data_lrc);
+#endif
+ iowrite32(data_lrc, reg_addr);
/* set REG_RPTC_CNTR*/
reg_addr = REG_PTC_RPTC_CNTR(pwm->regs, dev->hwpwm);
iowrite32(0, reg_addr);
-
+
/* set REG_PTC_RPTC_CTRL*/
reg_addr = REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm);
- iowrite32(PTC_EN|PTC_OE, reg_addr);
-
+ iowrite32(PTC_EN | PTC_OE, reg_addr);
+
return 0;
}
-
-
static const struct pwm_ops starfive_pwm_ptc_ops = {
.get_state = starfive_pwm_ptc_get_state,
- .apply = (void*)starfive_pwm_ptc_apply,
+ .apply = (void *)starfive_pwm_ptc_apply,
.owner = THIS_MODULE,
};
-
-
-
static int starfive_pwm_ptc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct starfive_pwm_ptc_device *pwm;
struct pwm_chip *chip;
struct resource *res;
+ unsigned long clk_tree_freq;
int ret;
-
+
#if PTC_DEBUG
- printk("starfive_pwm_ptc_probe in....\r\n");
+ dev_info(dev, "%s in....\r\n", __func__);
#endif
pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
if (!pwm) {
chip->dev = dev;
chip->ops = &starfive_pwm_ptc_ops;
- /* how many parameters can be transfered to ptc,need to fix */
+ /* how many parameters can be transferred to ptc, need to fix */
chip->of_pwm_n_cells = 3;
chip->base = -1;
/* get pwm channels count, max value is 8 */
ret = of_property_read_u32(node, "starfive,npwm", &chip->npwm);
- if (ret < 0 || chip->npwm > MAX_PWM)
- {
+ if (ret < 0 || chip->npwm > MAX_PWM)
chip->npwm = MAX_PWM;
- }
-#if PTC_DEBUG
- printk("[starfive_pwm_ptc_probe] npwm:0x%lx....\r\n",chip->npwm);
-#endif
- /* get apb clock frequency */
- ret = of_property_read_u32(node, "starfive,approx-period", &pwm->approx_period);
-
#if PTC_DEBUG
- printk("[starfive_pwm_ptc_probe] approx_period:%d....\r\n",pwm->approx_period);
+ dev_info(dev, "[%s] npwm:0x%lx....\r\n", __func__, chip->npwm);
#endif
+
/* get IO base address*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
#if PTC_DEBUG
- printk("[starfive_pwm_ptc_probe] res start:0x%lx,end:0x%lx....\r\n",res->start,res->end);
-#endif
+ dev_info(dev, "[%s] res start:0x%lx,end:0x%lx....\r\n",
+ __func__, res->start, res->end);
+#endif
pwm->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(pwm->regs))
- {
+ if (IS_ERR(pwm->regs)) {
dev_err(dev, "Unable to map IO resources\n");
return PTR_ERR(pwm->regs);
}
-
#if PTC_DEBUG
- printk("[starfive_pwm_ptc_probe] regs:0x%lx....\r\n",pwm->regs);
+ dev_info(dev, "[%s] regs:0x%lx....\r\n", __func__, pwm->regs);
#endif
+ /* get and enable clocks/resets */
pwm->clk = devm_clk_get(dev, NULL);
if (IS_ERR(pwm->clk)) {
- dev_err(dev, "Unable to find controller clock\n");
+ dev_err(dev, "Unable to get pwm clock\n");
return PTR_ERR(pwm->clk);
}
+ pwm->rst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(pwm->rst)) {
+ dev_err(dev, "Unable to get pwm reset\n");
+ return PTR_ERR(pwm->rst);
+ }
- /* after add,it will display as /sys/class/pwm/pwmchip0,0 is chip->base
- * after execute echo 0 > export in , pwm0 can be seen */
+ ret = clk_prepare_enable(pwm->clk);
+ if (ret) {
+ dev_err(dev,
+ "Failed to enable pwm clock, %d\n", ret);
+ return ret;
+ }
+ reset_control_deassert(pwm->rst);
+
+ /* get apb clock frequency */
+ ret = of_property_read_u32(node, "starfive,approx-period",
+ &pwm->approx_period);
+ if (ret)
+ pwm->approx_period = 2000000;
+#if PTC_DEBUG
+ dev_info(dev, "[%s] approx_period:%d....\r\n",
+ __func__, pwm->approx_period);
+#endif
+
+#ifndef HWBOARD_FPGA
+ clk_tree_freq = clk_get_rate(pwm->clk);
+ if (!clk_tree_freq)
+ dev_warn(dev,
+ "get pwm apb clock rate failed.\n");
+ else
+ pwm->approx_period = (unsigned int)clk_tree_freq;
+#endif
+
+ /*
+ * after add, /sys/class/pwm/pwmchip0/ will appear,'0' is chip->base
+ * if execute 'echo 0 > export' in this directory, pwm0/ can be seen
+ */
ret = pwmchip_add(chip);
if (ret < 0) {
dev_err(dev, "cannot register PTC: %d\n", ret);
}
platform_set_drvdata(pdev, pwm);
-
+
#if PTC_DEBUG
- printk("starfive PWM PTC chip registered %d PWMs\n", chip->npwm);
+ dev_info(dev, "starfive PWM PTC chip registered %d PWMs\n",
+ chip->npwm);
#endif
return 0;
struct starfive_pwm_ptc_device *pwm = platform_get_drvdata(dev);
struct pwm_chip *chip = &pwm->chip;
+ clk_disable_unprepare(pwm->clk);
pwmchip_remove(chip);
return 0;
};
module_platform_driver(starfive_pwm_ptc_driver);
-MODULE_DESCRIPTION("starfive PWM PTC driver");
+MODULE_DESCRIPTION("StarFive PWM PTC driver");
MODULE_LICENSE("GPL v2");
-