Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
S
Simple PCIe FMC carrier SPEC - Software
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
3
Issues
3
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
image/svg+xml
Discourse
Discourse
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Projects
Simple PCIe FMC carrier SPEC - Software
Commits
a6b133ef
Commit
a6b133ef
authored
Jan 15, 2014
by
Tomasz Wlostowski
Committed by
Alessandro Rubini
Feb 28, 2014
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
kernel: built-in support for the VIC interrupt controller
parent
eb9b9d03
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
394 additions
and
11 deletions
+394
-11
Makefile
kernel/Makefile
+1
-0
vic_regs.h
kernel/hw/vic_regs.h
+87
-0
spec-fmc.c
kernel/spec-fmc.c
+97
-11
spec-vic.c
kernel/spec-vic.c
+200
-0
spec.h
kernel/spec.h
+9
-0
No files found.
kernel/Makefile
View file @
a6b133ef
...
...
@@ -24,6 +24,7 @@ obj-$(CONFIG_WR_NIC) += wr-nic.o
spec-y
=
spec-pci.o
spec-y
+=
spec-fmc.o
spec-y
+=
spec-i2c.o
spec-y
+=
spec-vic.o
spec-y
+=
loader-ll.o
spec-y
+=
spec-gpio-no.o
spec-$(CONFIG_GPIOLIB)
+=
spec-gpio.o
...
...
kernel/hw/vic_regs.h
0 → 100644
View file @
a6b133ef
/*
Register definitions for slave core: Vectored Interrupt Controller (VIC)
* File : vic_regs.h
* Author : auto-generated by wbgen2 from wb_slave_vic.wb
* Created : Thu Oct 25 16:47:27 2012
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE wb_slave_vic.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_WB_SLAVE_VIC_WB
#define __WBGEN2_REGDEFS_WB_SLAVE_VIC_WB
#include <linux/types.h>
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: VIC Control Register */
/* definitions for field: VIC Enable in reg: VIC Control Register */
#define VIC_CTL_ENABLE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: VIC output polarity in reg: VIC Control Register */
#define VIC_CTL_POL WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Emulate Edge sensitive output in reg: VIC Control Register */
#define VIC_CTL_EMU_EDGE WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Emulated Edge pulse timer in reg: VIC Control Register */
#define VIC_CTL_EMU_LEN_MASK WBGEN2_GEN_MASK(3, 16)
#define VIC_CTL_EMU_LEN_SHIFT 3
#define VIC_CTL_EMU_LEN_W(value) WBGEN2_GEN_WRITE(value, 3, 16)
#define VIC_CTL_EMU_LEN_R(reg) WBGEN2_GEN_READ(reg, 3, 16)
/* definitions for register: Raw Interrupt Status Register */
/* definitions for register: Interrupt Enable Register */
/* definitions for register: Interrupt Disable Register */
/* definitions for register: Interrupt Mask Register */
/* definitions for register: Vector Address Register */
/* definitions for register: Software Interrupt Register */
/* definitions for register: End Of Interrupt Acknowledge Register */
/* definitions for RAM: Interrupt Vector Table */
#define VIC_IVT_RAM_BYTES 0x00000080
/* size in bytes */
#define VIC_IVT_RAM_WORDS 0x00000020
/* size in 32-bit words, 32-bit aligned */
#define VIC_IVT_RAM_BASE 0x00000080
/* [0x0]: REG VIC Control Register */
#define VIC_REG_CTL 0x00000000
/* [0x4]: REG Raw Interrupt Status Register */
#define VIC_REG_RISR 0x00000004
/* [0x8]: REG Interrupt Enable Register */
#define VIC_REG_IER 0x00000008
/* [0xc]: REG Interrupt Disable Register */
#define VIC_REG_IDR 0x0000000c
/* [0x10]: REG Interrupt Mask Register */
#define VIC_REG_IMR 0x00000010
/* [0x14]: REG Vector Address Register */
#define VIC_REG_VAR 0x00000014
/* [0x18]: REG Software Interrupt Register */
#define VIC_REG_SWIR 0x00000018
/* [0x1c]: REG End Of Interrupt Acknowledge Register */
#define VIC_REG_EOIR 0x0000001c
#endif
kernel/spec-fmc.c
View file @
a6b133ef
...
...
@@ -93,14 +93,15 @@ out:
return
ret
;
}
static
int
spec_irq_request
(
struct
fmc_device
*
fmc
,
irq_handler_t
handler
,
char
*
name
,
int
flags
)
/* Low-level IRQ request function: set up handler, with or without the VIC */
static
int
spec_shared_irq_request
(
struct
fmc_device
*
fmc
,
irq_handler_t
handler
,
char
*
name
,
int
flags
)
{
struct
spec_dev
*
spec
=
fmc
->
carrier_data
;
int
ret
;
u32
value
;
ret
=
request_irq
(
fmc
->
irq
,
handler
,
flags
,
name
,
fmc
);
ret
=
request_irq
(
spec
->
pdev
->
irq
,
handler
,
flags
,
name
,
fmc
);
if
(
ret
)
return
ret
;
...
...
@@ -118,7 +119,67 @@ static int spec_irq_request(struct fmc_device *fmc, irq_handler_t handler,
return
0
;
}
static
void
spec_irq_ack
(
struct
fmc_device
*
fmc
)
static
void
spec_shared_irq_ack
(
struct
fmc_device
*
fmc
);
static
irqreturn_t
spec_vic_irq_handler
(
int
id
,
void
*
data
)
{
struct
fmc_device
*
fmc
=
(
struct
fmc_device
*
)
data
;
irqreturn_t
rv
;
rv
=
spec_vic_irq_dispatch
((
struct
spec_dev
*
)
fmc
->
carrier_data
);
spec_shared_irq_ack
(
fmc
);
return
IRQ_HANDLED
;
}
static
struct
fmc_gpio
spec_vic_gpio_cfg
[]
=
{
{
.
gpio
=
FMC_GPIO_IRQ
(
1
),
.
mode
=
GPIOF_DIR_IN
,
.
irqmode
=
IRQF_TRIGGER_RISING
,
}
};
static
int
spec_irq_request
(
struct
fmc_device
*
fmc
,
irq_handler_t
handler
,
char
*
name
,
int
flags
)
{
struct
spec_dev
*
spec
=
fmc
->
carrier_data
;
int
rv
;
/* VIC mode interrupt */
if
(
!
(
flags
&
IRQF_SHARED
))
{
int
first_time
=
!
spec
->
vic
;
/* configure the VIC */
rv
=
spec_vic_irq_request
(
spec
,
fmc
,
fmc
->
irq
,
handler
);
if
(
rv
)
return
rv
;
/* on first IRQ, configure VIC "master" handler and GPIO too */
if
(
first_time
)
{
rv
=
spec_shared_irq_request
(
fmc
,
spec_vic_irq_handler
,
"spec-vic"
,
IRQF_SHARED
);
if
(
rv
)
return
rv
;
fmc
->
op
->
gpio_config
(
fmc
,
spec_vic_gpio_cfg
,
ARRAY_SIZE
(
spec_vic_gpio_cfg
));
}
}
else
{
rv
=
spec_shared_irq_request
(
fmc
,
handler
,
name
,
flags
);
printk
(
"Requesting irq '%s' in shared mode (rv %d)
\n
"
,
name
,
rv
);
}
if
(
!
rv
)
spec
->
flags
|=
SPEC_FLAG_IRQS_REQUESTED
;
return
rv
;
}
static
void
spec_shared_irq_ack
(
struct
fmc_device
*
fmc
)
{
struct
spec_dev
*
spec
=
fmc
->
carrier_data
;
...
...
@@ -130,15 +191,35 @@ static void spec_irq_ack(struct fmc_device *fmc)
gennum_readl
(
spec
,
GNGPIO_INT_STATUS
);
}
static
int
spec_irq_free
(
struct
fmc_device
*
fmc
)
static
void
spec_irq_ack
(
struct
fmc_device
*
fmc
)
{
struct
spec_dev
*
spec
=
fmc
->
carrier_data
;
gennum_writel
(
spec
,
0xffff
,
GNGPIO_INT_MASK_SET
);
/* disable */
free_irq
(
fmc
->
irq
,
fmc
);
if
(
!
spec
->
vic
)
spec_shared_irq_ack
(
fmc
);
/* Nothing for VIC here, all irqs are acked by master VIC handler */
}
static
int
spec_shared_irq_free
(
struct
fmc_device
*
fmc
)
{
struct
spec_dev
*
spec
=
fmc
->
carrier_data
;
gennum_writel
(
spec
,
0xffff
,
GNGPIO_INT_MASK_SET
);
/* disable */
free_irq
(
spec
->
pdev
->
irq
,
fmc
);
return
0
;
}
static
int
spec_irq_free
(
struct
fmc_device
*
fmc
)
{
struct
spec_dev
*
spec
=
fmc
->
carrier_data
;
if
(
spec
->
vic
)
return
spec_vic_irq_free
(
spec
,
spec
->
pdev
->
irq
);
else
return
spec_shared_irq_free
(
fmc
);
}
/* This is the mapping from virtual GPIO pin numbers to raw gpio numbers */
struct
{
int
virtual
;
int
raw
;
...
...
@@ -230,8 +311,7 @@ static int spec_gpio_config(struct fmc_device *fmc, struct fmc_gpio *gpio,
if
(
gpio
->
carrier_name
)
{
/* so, it's ours */
gpio
->
_gpio
=
gpio
->
gpio
;
}
else
if
(
!
gpio
->
_gpio
)
{
}
else
if
(
!
gpio
->
_gpio
)
{
/* virtual but not mapped (or poor gpio0) */
i
=
spec_map_pin
(
gpio
->
gpio
);
if
(
i
<
0
)
...
...
@@ -360,6 +440,13 @@ static void spec_irq_exit(struct fmc_device *fmc)
for
(
i
=
0
;
i
<
7
;
i
++
)
gennum_writel
(
spec
,
0
,
GNINT_CFG
(
i
));
fmc
->
op
->
irq_ack
(
fmc
);
/* just to be safe */
/* VIC mode: release VIC resources and disable VIC master IRQ line */
if
(
spec
->
vic
)
{
spec_vic_cleanup
(
spec
);
gennum_writel
(
spec
,
0xffff
,
GNGPIO_INT_MASK_SET
);
/* disable */
free_irq
(
spec
->
pdev
->
irq
,
fmc
);
}
}
static
int
check_golden
(
struct
fmc_device
*
fmc
)
...
...
@@ -392,7 +479,7 @@ static int check_golden(struct fmc_device *fmc)
int
spec_fmc_create
(
struct
spec_dev
*
spec
)
{
struct
fmc_device
*
fmc
;
struct
pci_dev
*
pdev
;
struct
pci_dev
*
pdev
;
int
ret
;
fmc
=
kzalloc
(
sizeof
(
*
fmc
),
GFP_KERNEL
);
...
...
@@ -408,7 +495,6 @@ int spec_fmc_create(struct spec_dev *spec)
fmc
->
fpga_base
=
spec
->
remap
[
0
];
fmc
->
memlen
=
1
<<
20
;
fmc
->
irq
=
spec
->
pdev
->
irq
;
fmc
->
op
=
&
spec_fmc_operations
;
fmc
->
hwdev
=
&
spec
->
pdev
->
dev
;
/* for messages */
spec
->
fmc
=
fmc
;
...
...
kernel/spec-vic.c
0 → 100644
View file @
a6b133ef
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version
*
* Driver for SPEC (Simple PCI Express FMC carrier) board.
* VIC (Vectored Interrupt Controller) support code.
*/
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "spec.h"
#include "hw/vic_regs.h"
#define VIC_MAX_VECTORS 32
#define VIC_SDB_VENDOR 0xce42
#define VIC_SDB_DEVICE 0x0013
/* A Vectored Interrupt Controller object */
struct
vic_irq_controller
{
/* already-initialized flag */
int
initialized
;
/* Base address (FPGA-relative) */
uint32_t
base
;
/* Mapped base address of the VIC */
void
*
kernel_va
;
/* Vector table */
struct
vector
{
/* Saved ID of the vector (for autodetection purposes) */
int
saved_id
;
/* Pointer to the assigned handler */
irq_handler_t
handler
;
/* FMC device that owns the interrupt */
struct
fmc_device
*
requestor
;
}
vectors
[
VIC_MAX_VECTORS
];
};
static
inline
void
vic_writel
(
struct
vic_irq_controller
*
vic
,
uint32_t
value
,
uint32_t
offset
)
{
writel
(
value
,
vic
->
kernel_va
+
offset
);
}
static
inline
uint32_t
vic_readl
(
struct
vic_irq_controller
*
vic
,
uint32_t
offset
)
{
return
readl
(
vic
->
kernel_va
+
offset
);
}
static
int
spec_vic_init
(
struct
spec_dev
*
spec
,
struct
fmc_device
*
fmc
)
{
int
i
;
signed
long
vic_base
;
struct
vic_irq_controller
*
vic
;
/*
* Try to look up the VIC in the SDB tree - note that IRQs
* shall be requested after the FMC driver has scanned the SDB tree
*/
vic_base
=
fmc_find_sdb_device
(
fmc
->
sdb
,
VIC_SDB_VENDOR
,
VIC_SDB_DEVICE
,
NULL
);
if
(
vic_base
<
0
)
{
dev_err
(
&
spec
->
pdev
->
dev
,
"VIC controller not found, but a VIC interrupt requested. Wrong gateware?
\n
"
);
return
-
ENODEV
;
}
dev_info
(
&
spec
->
pdev
->
dev
,
"Found VIC @ 0x%lx
\n
"
,
vic_base
);
vic
=
kzalloc
(
sizeof
(
struct
vic_irq_controller
),
GFP_KERNEL
);
if
(
!
vic
)
return
-
ENOMEM
;
vic
->
kernel_va
=
spec
->
remap
[
0
]
+
vic_base
;
vic
->
base
=
(
uint32_t
)
vic_base
;
/* disable all IRQs, copy the vector table with pre-defined IRQ ids */
vic_writel
(
vic
,
0xffffffff
,
VIC_REG_IDR
);
for
(
i
=
0
;
i
<
VIC_MAX_VECTORS
;
i
++
)
vic
->
vectors
[
i
].
saved_id
=
vic_readl
(
vic
,
VIC_IVT_RAM_BASE
+
4
*
i
);
/* config the VIC output: active high, edge, width = 256 tick (4 us) */
vic_writel
(
vic
,
VIC_CTL_ENABLE
|
VIC_CTL_POL
|
VIC_CTL_EMU_EDGE
|
VIC_CTL_EMU_LEN_W
(
250
),
VIC_REG_CTL
);
vic
->
initialized
=
1
;
spec
->
vic
=
vic
;
return
0
;
}
void
spec_vic_cleanup
(
struct
spec_dev
*
spec
)
{
if
(
!
spec
->
vic
)
return
;
/* Disable all irq lines and the VIC in general */
vic_writel
(
spec
->
vic
,
0xffffffff
,
VIC_REG_IDR
);
vic_writel
(
spec
->
vic
,
0
,
VIC_REG_CTL
);
kfree
(
spec
->
vic
);
spec
->
vic
=
NULL
;
}
irqreturn_t
spec_vic_irq_dispatch
(
struct
spec_dev
*
spec
)
{
struct
vic_irq_controller
*
vic
=
spec
->
vic
;
int
index
,
rv
;
struct
vector
*
vec
;
/*
* Our parent IRQ handler: read the index value
* from the Vector Address Register, and find matching handler
*/
index
=
vic_readl
(
vic
,
VIC_REG_VAR
)
&
0xff
;
if
(
index
>=
VIC_MAX_VECTORS
)
goto
fail
;
vec
=
&
vic
->
vectors
[
index
];
if
(
!
vec
->
handler
)
goto
fail
;
rv
=
vec
->
handler
(
vec
->
saved_id
,
vec
->
requestor
);
vic_writel
(
vic
,
0
,
VIC_REG_EOIR
);
/* ack the irq */
return
rv
;
fail:
return
0
;
}
int
spec_vic_irq_request
(
struct
spec_dev
*
spec
,
struct
fmc_device
*
fmc
,
unsigned
long
id
,
irq_handler_t
handler
)
{
struct
vic_irq_controller
*
vic
;
int
rv
=
0
,
i
;
/* First interrupt to be requested? Look up and init the VIC */
if
(
!
spec
->
vic
)
{
rv
=
spec_vic_init
(
spec
,
fmc
);
if
(
rv
)
return
rv
;
}
vic
=
spec
->
vic
;
for
(
i
=
0
;
i
<
VIC_MAX_VECTORS
;
i
++
)
{
/* find vector in stored table, assign handle, enable */
if
(
vic
->
vectors
[
i
].
saved_id
==
id
)
{
spin_lock
(
&
spec
->
irq_lock
);
vic_writel
(
vic
,
i
,
VIC_IVT_RAM_BASE
+
4
*
i
);
vic
->
vectors
[
i
].
requestor
=
fmc
;
vic
->
vectors
[
i
].
handler
=
handler
;
vic_writel
(
vic
,
(
1
<<
i
),
VIC_REG_IER
);
spin_unlock
(
&
spec
->
irq_lock
);
return
0
;
}
}
return
-
EINVAL
;
}
int
spec_vic_irq_free
(
struct
spec_dev
*
spec
,
unsigned
long
id
)
{
int
i
;
for
(
i
=
0
;
i
<
VIC_MAX_VECTORS
;
i
++
)
{
uint32_t
vec
=
spec
->
vic
->
vectors
[
i
].
saved_id
;
if
(
vec
==
id
)
{
spin_lock
(
&
spec
->
irq_lock
);
vic_writel
(
spec
->
vic
,
1
<<
i
,
VIC_REG_IDR
);
vic_writel
(
spec
->
vic
,
vec
,
VIC_IVT_RAM_BASE
+
4
*
i
);
spec
->
vic
->
vectors
[
i
].
handler
=
NULL
;
spin_unlock
(
&
spec
->
irq_lock
);
}
}
return
0
;
}
void
spec_vic_irq_ack
(
struct
spec_dev
*
spec
,
unsigned
long
id
)
{
/* fixme: do we need anything special here? */
}
kernel/spec.h
View file @
a6b133ef
...
...
@@ -33,9 +33,12 @@ struct spec_dev {
int
irq_count
;
/* for mezzanine use too */
struct
completion
compl
;
struct
gpio_chip
*
gpio
;
struct
vic_irq_controller
*
vic
;
spinlock_t
irq_lock
;
};
#define SPEC_FLAG_FAKE_EEPROM 0x00000001
#define SPEC_FLAG_IRQS_REQUESTED 0x00000002
/* Registers for GN4124 access */
enum
{
...
...
@@ -141,5 +144,11 @@ extern int spec_eeprom_write(struct fmc_device *fmc, uint32_t offset,
extern
int
spec_gpio_init
(
struct
fmc_device
*
fmc
);
extern
void
spec_gpio_exit
(
struct
fmc_device
*
fmc
);
int
spec_vic_irq_request
(
struct
spec_dev
*
spec
,
struct
fmc_device
*
fmc
,
unsigned
long
id
,
irq_handler_t
handler
);
int
spec_vic_irq_free
(
struct
spec_dev
*
spec
,
unsigned
long
id
);
irqreturn_t
spec_vic_irq_dispatch
(
struct
spec_dev
*
spec
);
void
spec_vic_cleanup
(
struct
spec_dev
*
spec
);
#endif
/* __SPEC_H__ */
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