Hi Sandeep.
I have wrote some driver for setup PWMs via dtsi for start PWMs:
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/pwm.h>
#include <linux/fsl_devices.h>
enum PwmDevs{DEVSTEP=0,DEVSERVO,DEVNUM};
struct tvh_pwm {
struct pwm_device *pwm;
bool enabled;
unsigned int period;
unsigned int irq;
};
struct tvh_pwm_device {
struct device *dev;
struct tvh_pwm *tvh[DEVNUM];
};
struct tvh_pwm_device *pb;
#if defined(CONFIG_OF)
static const struct of_device_id tvh_pwm_dt_ids[] = {
{ .compatible = "tvh,pwm"},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tvh_pwm_dt_ids);
#endif
/*
int tvh_pwm_freq(int dev, int freq)
{
unsigned int period_ns;
// printk("%s dev %d freq %d\n",__func__, dev, freq);
if(dev < 0 || dev >= DEVNUM) return -EINVAL;
if(!freq) return -EINVAL;
if(!pb) return -ENODEV;
if(!pb->tvh[dev]->pwm) return -ENODEV;
period_ns = 1000000000;
do_div(period_ns, freq);
pwm_config(pb->tvh[dev]->pwm, (period_ns >> 1), period_ns);
return 0;
}
EXPORT_SYMBOL(tvh_pwm_freq);
*/
static int tvh_pwm_probe(struct platform_device *pdev)
{
int ret = 0;
struct tvh_pwm *step;
struct tvh_pwm *servo;
// struct device_node *np = pdev->dev.of_node;
printk("%s\n",__func__);
step = kzalloc(sizeof(*step), GFP_KERNEL);
if (!step) {
dev_err(&pdev->dev, "no memory for state\n");
ret = -ENOMEM;
goto err_alloc;
}
servo = kzalloc(sizeof(*servo), GFP_KERNEL);
if (!servo) {
dev_err(&pdev->dev, "no memory for state\n");
ret = -ENOMEM;
goto err_alloc;
}
pb = kzalloc(sizeof(*pb), GFP_KERNEL);
if (!pb) {
dev_err(&pdev->dev, "no memory for state\n");
ret = -ENOMEM;
goto err_alloc;
}
pb->dev = &pdev->dev;
/*STEPPER*/
step->pwm = devm_pwm_get(&pdev->dev, "tvh-step");
if (IS_ERR(step->pwm)) {
dev_err(&pdev->dev, "unable to request PWM for stepper.\n");
ret = (PTR_ERR)(step->pwm);
kfree(step);
}
else
{
step->period = pwm_get_period(step->pwm);
printk("DEVSTEP period %d\n",step->period);
pwm_config(step->pwm, (step->period >> 1), step->period);
pwm_out_enable(step->pwm, 1);
pwm_enable(step->pwm);
step->enabled = true;
pb->tvh[DEVSTEP] = step;
}
/*SERVO*/
servo->pwm = devm_pwm_get(&pdev->dev, "tvh-servo");
if (IS_ERR(servo->pwm)) {
dev_err(&pdev->dev, "unable to request PWM for servo\n");
ret = (PTR_ERR)(servo->pwm);
kfree(servo);
}
else
{
servo->period = pwm_get_period(servo->pwm);
printk("DEVSERVO period %d\n",servo->period);
pwm_config(servo->pwm, (servo->period >> 1), servo->period);
pwm_out_enable(servo->pwm, 1);
pwm_enable(servo->pwm);
servo->enabled = true;
pb->tvh[DEVSERVO] = servo;
}
platform_set_drvdata(pdev, pb);
return 0;
//err_pwm:
kfree(pb);
err_alloc:
return ret;
}
static int tvh_pwm_remove(struct platform_device *pdev)
{
struct tvh_pwm_device *pb = platform_get_drvdata(pdev);
pwm_disable(pb->tvh[DEVSTEP]->pwm);
devm_pwm_put(&pdev->dev, pb->tvh[DEVSTEP]->pwm);
pwm_disable(pb->tvh[DEVSERVO]->pwm);
devm_pwm_put(&pdev->dev, pb->tvh[DEVSERVO]->pwm);
kfree(pb);
return 0;
}
static struct platform_driver tvh_pwm_driver = {
.probe = tvh_pwm_probe,
.remove = tvh_pwm_remove,
.driver = {
.name = "tvh_pwm",
.owner = THIS_MODULE,
.of_match_table = tvh_pwm_dt_ids,
},
};
module_platform_driver(tvh_pwm_driver)
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TvHelp, Ltd.");
MODULE_DESCRIPTION("Manage Servo and Stepper motors via PWM");
and add to my dtsi following lines:
...
pwm-tvh {
compatible = "tvh,pwm";
pwms = <&pwm1 0 5000000>, <&pwm2 0 1000000>;//period in nanosec
pwm-names = "tvh-servo", "tvh-step";
};
...
&pwm2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm2_2>;
status = "okay";
};
add to imx6qdl.dtsi:
pinctrl_pwm2_2: pwm2grp-2 {
fsl,pins = <
MX6QDL_PAD_GPIO_1__PWM2_OUT 0x1b0b1
>;
};
In application I call this:
#define MHZ 1000000
#define CLK 66*MHZ
#define PCLK 2*MHZ
#define PRESCALE (CLK)/(PCLK)
#define STEP_PWM2_IRQ 116
#define PWM1_BASE_ADDRESS 0xc09b0000
#define PWM2_BASE_ADDRESS 0xc09b8000
#define MX3_PWMCR 0x00 /* PWM Control Register */
#define MX3_PWMSR 0x04 /* PWM Status Register */
#define MX3_PWMIR 0x08 /* PWM Interrupt Register */
#define MX3_PWMSAR 0x0C /* PWM Sample Register */
#define MX3_PWMPR 0x10 /* PWM Period Register */
#define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4)
#define MX3_PWMCR_DOZEEN (1 << 24)
#define MX3_PWMCR_WAITEN (1 << 23)
#define MX3_PWMCR_DBGEN (1 << 22)
#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
#define MX3_PWMCR_CLKSRC_IPG (1 << 16)
#define MX3_PWMCR_SWR (1 << 3)
#define MX3_PWMCR_EN (1 << 0)
#define MX3_PWMIR_COMPAREIE (1<<2)
#define MX3_PWMIR_ROLLOVERIE (1<<1)
#define MX3_PWMIR_FIFOEMPTYIE (1<<0)
#define MX3_PWMSR_ROLLOVER_STATUS (1<<4)
irqreturn_t step_pwm_handler(int irq, void *handle)
{
//Clear rollover interrupt flag
writel(MX3_PWMSR_ROLLOVER_STATUS, (void volatile*)(PWM2_BASE_ADDRESS + MX3_PWMSR));
//set period
writel(pwm_per[period_cnt], (void volatile*)(PWM2_BASE_ADDRESS + MX3_PWMPR));
//set sample
writel(pwm_sam[period_cnt], (void volatile*)(PWM2_BASE_ADDRESS + MX3_PWMSAR));
//Do what you need
return IRQ_HANDLED;
}
/*
Init PWM2 of iMX6
Setup Control Register with prescale calculated above in defines
Request irq. Rollover interrupt enable set in drivers/pwm/pwm-imx.c
in imx_pwm_enable function:
...
imx->rei_enable(chip, true);
...
imx_pwm_enable calls in drivers/tvhelp/pwm.pwm.c probe function.
params:
cb - callback function which call in interrupt handler
*/
int step_pwm_init(void *cb)
{
int ret;
u32 cr;
if(opened) return EALREADY;
DBG("%s %s\n",TAG, __func__);
cr = MX3_PWMCR_PRESCALER(PRESCALE) |
MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH | MX3_PWMCR_EN;
writel(cr,(void volatile*)(PWM2_BASE_ADDRESS + MX3_PWMCR));
ret = request_irq(STEP_PWM2_IRQ, step_pwm_handler, IRQF_DISABLED, "step pwm irq", NULL);
DBG("%s request step pwm irq %d return %d\n", TAG, STEP_PWM2_IRQ, ret);
if(cb) callback = cb;
if(ret == 0)
opened = 1;
return ret;
}
/*
Free PWM2 irq.
*/
int step_pwm_exit(void)
{
if(!opened) return 0;
free_irq(STEP_PWM2_IRQ, NULL);
DBG("%s free step pwm irq %d\n", TAG, STEP_PWM2_IRQ);
opened = 0;
return 0;
}
Good luck)