9
0
Fork 0

drivers/pwm: add PXA pulse width modulator controller

Add PXA embedded pulse width modulator support. The PWM can
generate signals from 49.6kHz to 1.625MHz.
The driver is for pxa2xx family. The pxa3xx was not handled yet.

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Robert Jarzmik 2012-02-16 19:23:47 +01:00 committed by Sascha Hauer
parent 6fa3e3119f
commit bf841a598e
9 changed files with 197 additions and 1 deletions

View File

@ -1,6 +1,7 @@
obj-y += clocksource.o
obj-y += common.o
obj-y += gpio.o
obj-y += devices.o
obj-$(CONFIG_ARCH_PXA2XX) += mfp-pxa2xx.o
obj-$(CONFIG_ARCH_PXA27X) += speed-pxa27x.o

View File

@ -47,3 +47,8 @@ struct device_d *pxa_add_mmc(void *base, int id, void *pdata)
{
return pxa_add_device("pxa-mmc", id, base, 0x1000, pdata);
}
struct device_d *pxa_add_pwm(void *base, int id)
{
return pxa_add_device("pxa_pwm", id, base, 0x10, NULL);
}

View File

@ -14,5 +14,6 @@
unsigned long pxa_get_uartclk(void);
unsigned long pxa_get_mmcclk(void);
unsigned long pxa_get_lcdclk(void);
unsigned long pxa_get_pwmclk(void);
#endif /* !__MACH_CLOCK_H */

View File

@ -23,4 +23,4 @@ struct device_d *pxa_add_i2c(void *base, int id,
struct device_d *pxa_add_uart(void *base, int id);
struct device_d *pxa_add_fb(void *base, struct pxafb_platform_data *pdata);
struct device_d *pxa_add_mmc(void *base, int id, void *pdata);
struct device_d *pxa_add_pwm(void *base, int id);

View File

@ -0,0 +1,20 @@
/*
*
* 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.
*/
#ifndef __ASM_MACH_REGS_PWM_H
#define __ASM_MACH_REGS_PWM_H
#include <mach/hardware.h>
/*
* Pulse modulator registers
*/
#define PWM0 0x40B00000
#define PWM1 0x40C00000
#define PWM0slave 0x40B00010
#define PWM1slave 0x40C00010
#endif

View File

@ -47,3 +47,8 @@ unsigned long pxa_get_lcdclk(void)
{
return pxa_get_lcdclk_10khz() * 10000;
}
unsigned long pxa_get_pwmclk(void)
{
return BASE_CLK;
}

View File

@ -8,5 +8,11 @@ menuconfig PWM
If unsure, say N.
if PWM
config PWM_PXA
bool "PXA PWM Support"
default y if ARCH_PXA2XX
help
This enables PWM support for Intel/Marvell PXA chips, such
as the PXA25x, PXA27x.
endif

View File

@ -1 +1,2 @@
obj-$(CONFIG_PWM) += core.o
obj-$(CONFIG_PWM_PXA) += pxa_pwm.o

157
drivers/pwm/pxa_pwm.c Normal file
View File

@ -0,0 +1,157 @@
/*
* simple driver for PWM (Pulse Width Modulator) controller
*
* 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.
*
* 2008-02-13 initial version eric miao <eric.miao@marvell.com>
* 2012 Robert Jarzmik <robert.jarzmik@free.fr>
*/
#include <common.h>
#include <init.h>
#include <errno.h>
#include <io.h>
#include <pwm.h>
#include <mach/hardware.h>
#include <mach/clock.h>
#include <mach/pxa-regs.h>
#include <mach/regs-pwm.h>
#include <asm-generic/div64.h>
#include <linux/compiler.h>
/* PWM registers and bits definitions */
#define PWMCR (0x00)
#define PWMDCR (0x04)
#define PWMPCR (0x08)
#define PWMCR_SD (1 << 6)
#define PWMDCR_FD (1 << 10)
struct pxa_pwm_chip {
struct pwm_chip chip;
void __iomem *iobase;
int id;
int duty_ns;
int period_ns;
};
static struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip)
{
return container_of(chip, struct pxa_pwm_chip, chip);
}
/*
* period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
* duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
* PWM_CLK_RATE = 13 MHz
*/
static int pxa_pwm_config(struct pwm_chip *chip, int duty_ns, int period_ns)
{
unsigned long long c;
unsigned long period_cycles, prescale, pv, dc;
struct pxa_pwm_chip *pxa_pwm = to_pxa_pwm_chip(chip);
c = pxa_get_pwmclk();
c = c * period_ns;
do_div(c, 1000000000);
period_cycles = c;
if (period_cycles < 1)
period_cycles = 1;
prescale = (period_cycles - 1) / 1024;
pv = period_cycles / (prescale + 1) - 1;
if (prescale > 63)
return -EINVAL;
if (duty_ns == period_ns)
dc = PWMDCR_FD;
else
dc = (pv + 1) * duty_ns / period_ns;
pxa_pwm->duty_ns = duty_ns;
pxa_pwm->period_ns = period_ns;
/* NOTE: the clock to PWM has to be enabled first
* before writing to the registers
*/
__raw_writel(prescale, pxa_pwm->iobase + PWMCR);
__raw_writel(dc, pxa_pwm->iobase + PWMDCR);
__raw_writel(pv, pxa_pwm->iobase + PWMPCR);
return 0;
}
static int pxa_pwm_enable(struct pwm_chip *chip)
{
struct pxa_pwm_chip *pxa_pwm = to_pxa_pwm_chip(chip);
switch (pxa_pwm->id) {
case 0:
case 2:
CKEN |= CKEN_PWM0;
break;
case 1:
case 3:
CKEN |= CKEN_PWM1;
break;
default:
return -EINVAL;
}
return 0;
}
static void pxa_pwm_disable(struct pwm_chip *chip)
{
struct pxa_pwm_chip *pxa_pwm = to_pxa_pwm_chip(chip);
switch (pxa_pwm->id) {
case 0:
case 2:
CKEN &= ~CKEN_PWM0;
break;
case 1:
case 3:
CKEN &= ~CKEN_PWM1;
break;
default:
break;
}
}
static struct pwm_ops pxa_pwm_ops = {
.config = pxa_pwm_config,
.enable = pxa_pwm_enable,
.disable = pxa_pwm_disable,
};
static int pxa_pwm_probe(struct device_d *dev)
{
struct pxa_pwm_chip *chip;
chip = xzalloc(sizeof(*chip));
chip->chip.devname = asprintf("%s", dev_name(dev));
chip->chip.ops = &pxa_pwm_ops;
chip->iobase = dev_request_mem_region(dev, 0);
chip->id = dev->id;
dev->priv = chip;
return pwmchip_add(&chip->chip, dev);
}
static struct driver_d pxa_pwm_driver = {
.name = "pxa_pwm",
.probe = pxa_pwm_probe,
};
static int __init pxa_pwm_init_driver(void)
{
CKEN &= ~CKEN_PWM0 & ~CKEN_PWM1;
register_driver(&pxa_pwm_driver);
return 0;
}
device_initcall(pxa_pwm_init_driver);