Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
W
wr-switch-sw
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
image/svg+xml
Discourse
Discourse
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
white-rabbit
wr-switch-sw
Commits
3b279e05
Commit
3b279e05
authored
Jul 27, 2012
by
Tomasz Wlostowski
Committed by
Alessandro Rubini
Jul 29, 2012
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
kernel: added AT91 software PWM emulation driver
parent
5409bf4a
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
433 additions
and
0 deletions
+433
-0
Makefile
kernel/at91_softpwm/Makefile
+16
-0
at91_softpwm.h
kernel/at91_softpwm/at91_softpwm.h
+10
-0
fiq-asm.S
kernel/at91_softpwm/fiq-asm.S
+51
-0
fiq-at91sam92.c
kernel/at91_softpwm/fiq-at91sam92.c
+58
-0
fiq-at91sam92.h
kernel/at91_softpwm/fiq-at91sam92.h
+80
-0
fiq-engine.h
kernel/at91_softpwm/fiq-engine.h
+37
-0
fiq-module.c
kernel/at91_softpwm/fiq-module.c
+32
-0
softpwm_core.c
kernel/at91_softpwm/softpwm_core.c
+149
-0
No files found.
kernel/at91_softpwm/Makefile
0 → 100644
View file @
3b279e05
# Trivial makefile for 2.6 modules
#LINUX ?= /lib/modules/$(shell uname -r)/build
obj-m
=
at91_softpwm.o
at91_softpwm-objs
:=
fiq-asm.o fiq-at91sam92.o fiq-module.o softpwm_core.o
all
:
$(MAKE)
-C
$(LINUX)
M
=
$(
shell
/bin/pwd
)
modules
clean
:
$(MAKE)
-C
$(LINUX)
clean
M
=
$(
shell
/bin/pwd
)
rm
-f
Module.symvers modules.order
*
~
# $(MAKE) -C doc terse
kernel/at91_softpwm/at91_softpwm.h
0 → 100644
View file @
3b279e05
#ifndef __AT91_SOFTPWM_H
#define __AT91_SOFTPWM_H
#define __AT91_SOFTPWM_IOC_MAGIC '5'
#define AT91_SOFTPWM_ENABLE _IO(__AT91_SOFTPWM_IOC_MAGIC, 4)
#define AT91_SOFTPWM_DISABLE _IO(__AT91_SOFTPWM_IOC_MAGIC, 5)
#define AT91_SOFTPWM_SETPOINT _IO(__AT91_SOFTPWM_IOC_MAGIC, 6)
#endif
/*__AT91_SOFTPWM_H*/
kernel/at91_softpwm/fiq-asm.S
0 → 100644
View file @
3b279e05
/*
* Alessandro Rubini, 2007, GPL 2 or later
*
* This file implements entry/exit from the fiq handler.
* It must save r0..r8 (since R8 is used to preserve LR in the few lines
* I added in kernel code, before calling this with "bl").
*/
.globl fiq_entry
.type fiq_entry, #function
/* here, we can use r10..r13 to our pleasure */
fiq_entry:
ldr r11, fiq_handler
movs r11, r11
moveq pc, lr /* nobody registered, return */
/* count the fiq: it doesn't cost too much cpu time */
ldr r10, fiqcount
add r10, r10, #1
str r10, fiqcount
/* save all registers, build sp and branch to C */
adr r9, regpool
stmia r9, {r0 - r8, lr}
adr sp, fiq_sp
ldr sp, [sp]
mov lr, pc
mov pc, r11
adr r9, regpool
ldmia r9, {r0 - r8, pc}
/* data words*/
.globl fiqcount
fiqcount: .long 0
fiq_sp: .long fiq_stack+1020
/* The pointer */
.globl fiq_handler
fiq_handler:
.long 0
regpool:
.space 40 /* 10 registers */
.pool
.align 5
fiq_stack:
.space 1024
kernel/at91_softpwm/fiq-at91sam92.c
0 → 100644
View file @
3b279e05
/* cpu-specific code for Atmel 92xx timers (actually, only 9263 as it is) */
#include <linux/module.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <mach/at91_aic.h>
#include <mach/at91_pmc.h>
#include <mach/at91_tc.h>
#include "fiq-engine.h"
#include "fiq-at91sam92.h"
static
int
fiq_irqnr
;
/* The following are called from the cpu-independent source file */
int
__fiq_register
(
void
(
*
handler
)(
void
),
int
irq
)
{
int
ret
;
if
(
irq
<
FIQ_IRQ_MIN
||
irq
>
FIQ_IRQ_MAX
)
{
printk
(
KERN_WARNING
"at91-fiq: irq %i not in valid range (%i-%i)
\n
"
,
irq
,
FIQ_IRQ_MIN
,
FIQ_IRQ_MAX
);
return
-
EINVAL
;
}
fiq_irqnr
=
irq
;
/* save it for ack and so on */
/* clock the peripheral */
at91_sys_write
(
AT91_PMC_PCER
,
1
<<
fiq_irqnr
);
fiq_handler
=
handler
;
/* used both from fiq and non-fiq */
/* request the irq in any case, only turn it to fiq as needed */
ret
=
request_irq
(
irq
,
fake_fiq_handler
,
IRQF_DISABLED
,
"fake-fiq"
,
&
fiq_handler
);
if
(
ret
)
{
printk
(
KERN_WARNING
"at91-fiq: can't request irq %i
\n
"
,
irq
);
return
ret
;
}
if
(
fiq_use_fiq
)
{
at91_sys_write
(
AT91_AIC_FFER
,
1
<<
fiq_irqnr
);
}
return
0
;
}
void
__fiq_unregister
(
void
(
*
handler
)(
void
),
int
irq
)
{
if
(
fiq_use_fiq
)
{
at91_sys_write
(
AT91_AIC_FFDR
,
1
<<
fiq_irqnr
);
}
free_irq
(
irq
,
&
fiq_handler
);
fiq_handler
=
NULL
;
}
kernel/at91_softpwm/fiq-at91sam92.h
0 → 100644
View file @
3b279e05
#ifndef __FIQ_AT91SAM92_H__
#define __FIQ_AT91SAM92_H__
/* defines for atmel 926x */
#include <mach/gpio.h>
#include <mach/at91_tc.h>
/*
* We have different setups for each of the three processors.
* This is mainly because of different names, but also because the 9263 differs
*/
#ifdef CONFIG_ARCH_AT91SAM9G45
/* one interrupt for tc block; use TC0 */
/* Limits we support */
# define FIQ_IRQ_MIN AT91SAM9G45_ID_TCB
# define FIQ_IRQ_MAX AT91SAM9G45_ID_TCB
/* Choices we make */
# define FIQ_IRQNR AT91SAM9G45_ID_TCB
# define FIQ_BASE AT91SAM9G45_BASE_TC0
#endif
#ifdef CONFIG_ARCH_AT91SAM9263
/* one interrupt for tc block; use TC0 */
/* Limits we support */
# define FIQ_IRQ_MIN AT91SAM9263_ID_TCB
# define FIQ_IRQ_MAX AT91SAM9263_ID_TCB
/* Choices we make */
# define FIQ_IRQNR AT91SAM9263_ID_TCB
# define FIQ_BASE AT91SAM9263_BASE_TC0
# define FIQ_BITNR AT91_PIN_PB20
#endif
#ifdef CONFIG_ARCH_AT91SAM9260
/* Use TC0 but any of TC0-TC2 can work */
/* Limits we support */
# define FIQ_IRQ_MIN AT91SAM9260_ID_TC0
# define FIQ_IRQ_MAX AT91SAM9260_ID_TC2
/* Choices we make */
# define FIQ_IRQNR AT91SAM9260_ID_TC0
# define FIQ_BASE AT91SAM9260_BASE_TC0
# define FIQ_BITNR AT91_PIN_PB20
#endif
#ifdef CONFIG_ARCH_AT91SAM9261
/* Use TC0 but any of TC0-TC2 can work */
/* Limits we support */
# define FIQ_IRQ_MIN AT91SAM9261_ID_TC0
# define FIQ_IRQ_MAX AT91SAM9261_ID_TC2
/* Choices we make */
# define FIQ_IRQNR AT91SAM9261_ID_TC0
# define FIQ_BASE AT91SAM9261_BASE_TC0
# define FIQ_BITNR AT91_PIN_PA20
/* very few pins are available */
#endif
#define FIQ_MHZ 3
/* timer clock runs at 99.328MHz/32 = 3.104MHz */
#define FIQ_TASK_CONVERT 1000, 3104
/* sysctl-stamp, used in fiq-task.c */
extern
void
*
__fiq_timer_base
;
/* for register access to the timer */
/* how to get a time stamp (counts, not usec). This cpu counts up */
#define __GETSTAMP() __raw_readl(__fiq_timer_base + AT91_TC_CV)
static
inline
void
__fiq_ack
(
void
)
{
/* read status register */
static
int
foo
;
foo
=
__raw_readl
(
__fiq_timer_base
+
AT91_TC_SR
);
}
static
inline
void
__fiq_sched_next_irq
(
int
usec
)
{
__raw_writel
(
usec
*
FIQ_MHZ
,
__fiq_timer_base
+
AT91_TC_RC
);
}
/* This is not needed for the fiq engine, but our example task wants it */
#define __fiq_set_gpio_value(a,b) at91_set_gpio_value(a, b);
#endif
/* __FIQ_AT91SAM92_H__ */
kernel/at91_softpwm/fiq-engine.h
0 → 100644
View file @
3b279e05
#ifndef __FIQ_ENGINE_H__
#define __FIQ_ENGINE_H__
#include <linux/irqreturn.h>
/* include cpu-specific defines to customize use */
#if defined(CONFIG_ARCH_AT91)
# include "fiq-at91sam92.h"
#elif defined(CONFIG_ARCH_PXA)
# include "fiq-pxa.h"
#elif defined(CONFIG_ARCH_OMAP3)
# include "fiq-omap3.h"
#else
# error "This package has no support for your architecture"
#endif
/* exported by the kernel (our patch) */
extern
void
(
*
fiq_userptr
)(
void
);
/* declared in fiq-asm.S */
extern
void
fiq_entry
(
void
);
extern
unsigned
long
fiqcount
;
extern
void
(
*
fiq_handler
)(
void
);
/* declared in fiq-module.c */
extern
irqreturn_t
fake_fiq_handler
(
int
irq
,
void
*
dev
);
extern
int
fiq_verbose
;
extern
int
fiq_use_fiq
;
/* false by default */
extern
int
fiq_register
(
void
(
*
handler
)(
void
),
int
irq
);
extern
int
fiq_unregister
(
void
(
*
handler
)(
void
),
int
irq
);
/* declared in the cpu-specific source file */
extern
int
__fiq_register
(
void
(
*
handler
)(
void
),
int
irq
);
extern
void
__fiq_unregister
(
void
(
*
handler
)(
void
),
int
irq
);
#endif
/* __FIQ_ENGINE_H__ */
kernel/at91_softpwm/fiq-module.c
0 → 100644
View file @
3b279e05
/*
* Fast-interrupt infrastructure for a real-time task.
* Alessandro Rubini, 2007,2008 GPL 2 or later.
* This code is generic, you must supplement it with cpu-specific code
* in another source file.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include "fiq-engine.h"
int
fiq_use_fiq
;
EXPORT_SYMBOL
(
fiq_use_fiq
);
int
fiq_verbose
;
/*
* High-level interface (cpu-independent)
*/
/* An irq handler, faking fiq, to test with fiq=0 */
irqreturn_t
fake_fiq_handler
(
int
irq
,
void
*
dev
)
{
(
*
fiq_handler
)();
return
IRQ_HANDLED
;
}
kernel/at91_softpwm/softpwm_core.c
0 → 100644
View file @
3b279e05
/* Software PWM emulation for AT91SAM9xxx MCUs. Relies on Alessandro's fiq-engine.
Licensed under GPL v2 (c) T.W. 2012
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <mach/gpio.h>
#include <mach/at91_tc.h>
#include <mach/at91sam9g45.h>
#include "fiq-engine.h"
#include "at91_softpwm.h"
struct
at91_softpwm_dev
{
int
in_use
;
int
pin
;
int
setpoint
;
int
state
;
void
*
timer_base
;
};
#define PERIOD 3000
static
struct
at91_softpwm_dev
dev
;
static
void
at91_softpwm_fiq_handler
(
void
)
{
static
int
status
;
status
=
__raw_readl
(
dev
.
timer_base
+
AT91_TC_SR
);
if
(
!
dev
.
in_use
)
return
;
if
(
dev
.
state
)
{
__raw_writel
(
dev
.
setpoint
,
dev
.
timer_base
+
AT91_TC_RC
);
/* FIQ Rate = 1 kHz */
at91_set_gpio_value
(
dev
.
pin
,
0
);
dev
.
state
=
0
;
}
else
{
__raw_writel
(
PERIOD
-
dev
.
setpoint
,
dev
.
timer_base
+
AT91_TC_RC
);
/* FIQ Rate = 1 kHz */
at91_set_gpio_value
(
dev
.
pin
,
1
);
dev
.
state
=
1
;
}
}
static
long
at91_softpwm_ioctl
(
struct
file
*
f
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
/* Check cmd type */
if
(
_IOC_TYPE
(
cmd
)
!=
__AT91_SOFTPWM_IOC_MAGIC
)
return
-
ENOIOCTLCMD
;
switch
(
cmd
)
{
case
AT91_SOFTPWM_ENABLE
:
dev
.
pin
=
arg
;
dev
.
in_use
=
1
;
dev
.
state
=
0
;
return
0
;
case
AT91_SOFTPWM_DISABLE
:
dev
.
in_use
=
0
;
dev
.
state
=
0
;
return
0
;
case
AT91_SOFTPWM_SETPOINT
:
dev
.
setpoint
=
PERIOD
-
(
arg
*
PERIOD
/
1000
);
return
0
;
default:
return
-
ENOIOCTLCMD
;
}
}
static
struct
file_operations
at91_softpwm_fops
=
{
.
owner
=
THIS_MODULE
,
.
unlocked_ioctl
=
at91_softpwm_ioctl
};
// TODO check available minor numbers
static
struct
miscdevice
at91_softpwm_misc
=
{
.
minor
=
78
,
.
name
=
"at91_softpwm"
,
.
fops
=
&
at91_softpwm_fops
};
int
__init
at91_softpwm_init
(
void
)
{
int
err
;
printk
(
"at91_softpwm: initializing
\n
"
);
// register misc device
err
=
misc_register
(
&
at91_softpwm_misc
);
if
(
err
<
0
)
{
printk
(
KERN_ERR
"%s: Can't register misc device
\n
"
,
KBUILD_MODNAME
);
return
err
;
}
dev
.
in_use
=
0
;
dev
.
setpoint
=
0
;
dev
.
timer_base
=
ioremap_nocache
(
AT91SAM9G45_BASE_TC2
,
0x40
);
__fiq_register
(
at91_softpwm_fiq_handler
,
AT91SAM9G45_ID_TCB
);
__raw_writel
(
AT91_TC_TIMER_CLOCK3
|
AT91_TC_WAVE
|
AT91_TC_WAVESEL_UP_AUTO
,
dev
.
timer_base
+
AT91_TC_CMR
);
/* Clock = 3 MHz */
__raw_writel
(
PERIOD
,
dev
.
timer_base
+
AT91_TC_RC
);
/* FIQ Rate = 1 kHz */
__raw_writel
((
1
<<
4
)
/* rc/rb compare */
,
dev
.
timer_base
+
AT91_TC_IER
);
__raw_writel
(
AT91_TC_CLKEN
|
AT91_TC_SWTRG
,
dev
.
timer_base
+
AT91_TC_CCR
);
return
0
;
}
void
__exit
at91_softpwm_exit
(
void
)
{
__fiq_unregister
(
at91_softpwm_fiq_handler
,
AT91SAM9G45_ID_TCB
);
__raw_writel
((
1
<<
4
)
/* rc compare */
,
dev
.
timer_base
+
AT91_TC_IDR
);
__raw_writel
(
AT91_TC_CLKDIS
,
dev
.
timer_base
+
AT91_TC_CCR
);
if
(
dev
.
in_use
)
at91_set_gpio_value
(
dev
.
pin
,
0
);
dev
.
in_use
=
0
;
misc_deregister
(
&
at91_softpwm_misc
);
iounmap
(
dev
.
timer_base
);
}
module_init
(
at91_softpwm_init
);
module_exit
(
at91_softpwm_exit
);
MODULE_DESCRIPTION
(
"Atmel AT91SAM9G45 software PWM"
);
MODULE_VERSION
(
"0.1"
);
MODULE_LICENSE
(
"GPL"
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment