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
7a1fb9bc
Commit
7a1fb9bc
authored
Oct 12, 2012
by
Alessandro Rubini
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'fmc-gpio'
parents
7a10b8bc
cd6a8dcb
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
254 additions
and
23 deletions
+254
-23
fmc-bus.in
doc/fmc-bus.in
+82
-1
fmc-trivial.c
kernel/fmc-trivial.c
+15
-0
fmc.h
kernel/include/linux/fmc.h
+30
-1
spec-fmc.c
kernel/spec-fmc.c
+113
-21
spec-pci.c
kernel/spec-pci.c
+4
-0
wr-nic-eth.c
kernel/wr-nic-eth.c
+10
-0
No files found.
doc/fmc-bus.in
View file @
7a1fb9bc
...
...
@@ -199,6 +199,8 @@ struct fmc_operations {
char *name, int flags);
void (*irq
_
ack)(struct fmc
_
device *fmc);
int (*irq
_
free)(struct fmc
_
device *fmc);
int (*gpio
_
config)(struct fmc
_
device *fmc, struct fmc
_
gpio *gpio,
int ngpio);
int (*read
_
ee)(struct fmc
_
device *fmc, int pos, void *d, int l);
int (*write
_
ee)(struct fmc
_
device *fmc, int pos, const void *d, int l);
}
;
...
...
@@ -254,6 +256,12 @@ The individual methods perform the following tasks:
The handler will receive the @i
{
fmc
}
pointer as @i
{
dev
_
id
}
; the
@i
{
flags
}
argument is still to be defined.
@item gpio
_
config
The method allows to configure a GPIO pin in the carrier, and
read its current value if it is configured as input. See
@ref
{
The GPIO Abstraction
}
for details.
@item read
_
ee
@itemx write
_
ee
...
...
@@ -270,7 +278,80 @@ The individual methods perform the following tasks:
@end table
@c ##########################################################################
@node The GPIO Abstraction
@chapter The GPIO Abstraction
Support for GPIO pins in the @i
{
fmc-bus
}
environment is a little
heavy, and deserves special discussion.
While the general idea of a carrier-independent driver seems to fly,
configuration of specific signals within the carrier needs at least
some knowledge of the carrier itself. For this reason, the specific
driver can request to configure carrier-specific GPIO pins, numbered
from 0 to at most 4095. Configuration is performed by passing
a pointer to an array of @t
{
struct fmc
_
gpio
}
items, as well as
the number of those items:
@example
struct fmc
_
gpio
{
char *carrier
_
name;
int gpio;
int
_
gpio; /* internal use by the carrier */
int mode; /* GPIOF
_
DIR
_
OUT etc, from <linux/gpio.h> */
int irqmode; /* IRQF
_
TRIGGER
_
LOW and so on */
}
;
@end example
By specifying a @i
{
carrier
_
name
}
for each pin, the driver may access
different pins in different carriers. The @i
{
gpio
_
config
}
method
returns the number of pins successfully configured, and each carrier
just ignores requests for other carriers. So, for example, a driver
that has been developed and tested on both the SPEC and the SVEC may
request configuration of two different GPIO pins, and expect one such
configuration to succeed -- if none succeeds it most likely means that
the current carrier is a still-unknown one. (FIXME: the return value
is not actually used this way in current code).
If, however, your GPIO pin has a specific known role, you can
pass a special number in the @t
{
gpio
}
field. The header defines
the following macros:
@example
#define FMC
_
GPIO
_
RAW(x) (x) /* 4096 of them */
#define FMC
_
GPIO
_
IRQ(x) ((x) + 0x1000) /* 256 of them */
#define FMC
_
GPIO
_
LED(x) ((x) + 0x1100) /* 256 of them */
#define FMC
_
GPIO
_
KEY(x) ((x) + 0x1200) /* 256 of them */
#define FMC
_
GPIO
_
TP(x) ((x) + 0x1300) /* 256 of them */
#define FMC
_
GPIO
_
USER(x) ((x) + 0x1400) /* 256 of them */
@end example
Use of virtual GPIO numbers (anything but @t
{
FMC
_
GPIO
_
RAW
}
) is allowed
provided the @i
{
carrier
_
name
}
field is left unspecified (NULL). Each
carrier is responsible for providing a mapping between virtual and
physical GPIO numbers (and possibly cache the raw number in the
@t
{_
gpio
}
field). All carriers must map their I/O lines
to the sets above starting from zero. The SPEC, for example, maps
interrupt 0 and 1, and test points 0 through 3.
If, for example, a driver requires a free led and a test point (for a
scope probe to be plugged at some point during development) it may ask
for @t
{
FMC
_
GPIO
_
LED(0)
}
and @t
{
FMC
_
GPIO
_
TP(0)
}
. Each carrier will
provide suitable GPIO pins. Clearly, the person running the drivers
will know the order used by the specific carrier driver in assigning
leds and testpoints, so to make a carrier-dependent use of the diagnostic tools.
In theory, some form of autodetection should be possible: a driver
like the @i
{
wr-nic
}
(which uses IRQ(1) on the SPEC card) should
configure IRQ(0), make a test with software-generated interrupts and
configure IRQ(1) if the test fails -- the @i
{
wr-nic
}
gateware is
known to use IRQ1 on the SPEC, but the driver should be
carrier-independent if possible and thus use IRQ(0) as a first bet.
If a pin is configured as input, the @i
{
gpio
_
config
}
method returns 0
or 1, to report its current value. Invalid GPIO numbers will cause
@code
{
-ENODEV
}
to be returned for physical numbers and @code
{
-ENOENT
}
for virtual mappings.
@bye
@c LocalWords: gnudd titlepage iftex texinfo CERN documentlanguage settitle
...
...
kernel/fmc-trivial.c
View file @
7a1fb9bc
...
...
@@ -2,6 +2,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/fmc.h>
#include "spec.h"
...
...
@@ -16,6 +17,18 @@ irqreturn_t t_handler(int irq, void *dev_id)
return
IRQ_HANDLED
;
}
struct
fmc_gpio
t_gpio
[]
=
{
{
.
gpio
=
FMC_GPIO_IRQ
(
0
),
.
mode
=
GPIOF_DIR_IN
,
.
irqmode
=
IRQF_TRIGGER_RISING
,
},
{
.
gpio
=
FMC_GPIO_IRQ
(
1
),
.
mode
=
GPIOF_DIR_IN
,
.
irqmode
=
IRQF_TRIGGER_RISING
,
}
};
int
t_probe
(
struct
fmc_device
*
fmc
)
{
int
ret
;
...
...
@@ -28,6 +41,8 @@ int t_probe(struct fmc_device *fmc)
ret
=
fmc
->
op
->
irq_request
(
fmc
,
t_handler
,
"fmc-trivial"
,
IRQF_SHARED
);
if
(
ret
<
0
)
return
ret
;
/* ignore error code of call below, we really don't care */
fmc
->
op
->
gpio_config
(
fmc
,
t_gpio
,
ARRAY_SIZE
(
t_gpio
));
/* Reprogram, if asked to. ESRCH == no filename specified */
ret
=
fmc
->
op
->
reprogram
(
fmc
,
&
t_drv
,
""
);
...
...
kernel/include/linux/fmc.h
View file @
7a1fb9bc
...
...
@@ -57,7 +57,34 @@ struct fmc_driver {
#define FMC_PARAM_GATEWARE(_d) \
module_param_array_named(gateware, _d.gw_val, charp, &_d.gw_n, 0444)
/* To be carrier-independent, we need to abstract hardware access */
/*
* Drivers may need to configure gpio pins in the carrier. To read input
* (a very uncommon opeation, and definitely not in the hot paths), just
* configure one gpio only and get 0 or 1 as retval of the config method
*/
struct
fmc_gpio
{
char
*
carrier_name
;
/* name or NULL for virtual pins */
int
gpio
;
int
_gpio
;
/* internal use by the carrier */
int
mode
;
/* GPIOF_DIR_OUT etc, from <linux/gpio.h> */
int
irqmode
;
/* IRQF_TRIGGER_LOW and so on */
};
/* The numbering of gpio pins allows access to raw pins or virtual roles */
#define FMC_GPIO_RAW(x) (x)
/* 4096 of them */
#define __FMC_GPIO_IS_RAW(x) ((x) < 0x1000)
#define FMC_GPIO_IRQ(x) ((x) + 0x1000)
/* 256 of them */
#define FMC_GPIO_LED(x) ((x) + 0x1100)
/* 256 of them */
#define FMC_GPIO_KEY(x) ((x) + 0x1200)
/* 256 of them */
#define FMC_GPIO_TP(x) ((x) + 0x1300)
/* 256 of them */
#define FMC_GPIO_USER(x) ((x) + 0x1400)
/* 256 of them */
/* We may add SCL and SDA, or other roles if the need arises */
/*
* The operations are offered by each carrier and should make driver
* design completely independent of th carrier. Named GPIO pins may be
* the exception.
*/
struct
fmc_operations
{
uint32_t
(
*
readl
)(
struct
fmc_device
*
fmc
,
int
offset
);
void
(
*
writel
)(
struct
fmc_device
*
fmc
,
uint32_t
value
,
int
offset
);
...
...
@@ -67,6 +94,8 @@ struct fmc_operations {
char
*
name
,
int
flags
);
void
(
*
irq_ack
)(
struct
fmc_device
*
fmc
);
int
(
*
irq_free
)(
struct
fmc_device
*
fmc
);
int
(
*
gpio_config
)(
struct
fmc_device
*
fmc
,
struct
fmc_gpio
*
gpio
,
int
ngpio
);
int
(
*
read_ee
)(
struct
fmc_device
*
fmc
,
int
pos
,
void
*
d
,
int
l
);
int
(
*
write_ee
)(
struct
fmc_device
*
fmc
,
int
pos
,
const
void
*
d
,
int
l
);
};
...
...
kernel/spec-fmc.c
View file @
7a1fb9bc
...
...
@@ -11,6 +11,7 @@
#include <linux/fmc.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/gpio.h>
#include <linux/fmc-sdb.h>
#include "spec.h"
...
...
@@ -112,27 +113,7 @@ static int spec_irq_request(struct fmc_device *fmc, irq_handler_t handler,
gennum_writel
(
spec
,
value
,
GNPPCI_MSI_CONTROL
);
}
/* Enable gpio interrupts:
* gpio6: tp8: output low
* gpio7: tp7: input (was: interrupt, raising edge)
* gpio8: IRQ1 from FPGA: interrupt, raising edge
* gpio9: IRQ0 from FPGA: interrupt, raising edge
* gpio10: tp6: output low
* gpio11: tp5: input (was: interrupt, raising edge)
*/
/* bypass = alternate function */
gennum_mask_val
(
spec
,
0xfc0
,
0x00
,
GNGPIO_BYPASS_MODE
);
/* direction 0 = output */
gennum_mask_val
(
spec
,
0x440
,
0x000
,
GNGPIO_DIRECTION_MODE
);
gennum_mask_val
(
spec
,
0xb80
,
0xb80
,
GNGPIO_DIRECTION_MODE
);
gennum_mask_val
(
spec
,
0x440
,
0x440
,
GNGPIO_OUTPUT_ENABLE
);
gennum_mask_val
(
spec
,
0x300
,
0x000
,
GNGPIO_INT_TYPE
);
/* 0 = edge */
gennum_mask_val
(
spec
,
0x300
,
0x300
,
GNGPIO_INT_VALUE
);
/* 1 = raising */
gennum_mask_val
(
spec
,
0x300
,
0x000
,
GNGPIO_INT_ON_ANY
);
gennum_writel
(
spec
,
0x300
,
GNGPIO_INT_MASK_CLR
);
/* enable */
/* Interrupts are enabled by the driver, with gpio_config() */
return
0
;
}
...
...
@@ -157,6 +138,116 @@ static int spec_irq_free(struct fmc_device *fmc)
return
0
;
}
/* This is the mapping from virtual GPIO pin numbers to raw gpio numbers */
struct
{
int
virtual
;
int
raw
;
}
spec_gpio_map
[]
=
{
/* 0: TCK */
/* 1: TMS */
/* 2: TDO */
/* 3: TDI */
/* 4: SDA */
/* 5: SCL */
/* 6: TP8 */
{
FMC_GPIO_TP
(
3
),
FMC_GPIO_RAW
(
6
)},
/* 7: TP7 */
{
FMC_GPIO_TP
(
2
),
FMC_GPIO_RAW
(
7
)},
/* 8: IRQ */
{
FMC_GPIO_IRQ
(
0
),
FMC_GPIO_RAW
(
8
)},
/* 9: IRQ */
{
FMC_GPIO_IRQ
(
1
),
FMC_GPIO_RAW
(
9
)},
/* 10: TP6 */
{
FMC_GPIO_TP
(
1
),
FMC_GPIO_RAW
(
10
)},
/* 11: TP5 */
{
FMC_GPIO_TP
(
0
),
FMC_GPIO_RAW
(
11
)},
/* 12: flash_cs, 13: spri_din, 14: bootsel1, 15: bootsel0 */
};
static
int
spec_map_pin
(
int
virtual
)
{
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
spec_gpio_map
);
i
++
)
if
(
spec_gpio_map
[
i
].
virtual
==
virtual
)
return
spec_gpio_map
[
i
].
raw
;
return
-
ENOENT
;
}
static
int
spec_cfg_pin
(
struct
fmc_device
*
fmc
,
int
pin
,
int
mode
,
int
imode
)
{
struct
spec_dev
*
spec
=
fmc
->
carrier_data
;
int
ret
=
0
;
int
bit
=
(
1
<<
pin
);
if
(
pin
<
0
||
pin
>
15
)
return
-
ENODEV
;
if
(
mode
&
(
GPIOF_OPEN_DRAIN
|
GPIOF_OPEN_SOURCE
))
return
-
EINVAL
;
if
(
mode
&
GPIOF_DIR_IN
)
{
/* 1 = input */
gennum_mask_val
(
spec
,
bit
,
bit
,
GNGPIO_DIRECTION_MODE
);
gennum_mask_val
(
spec
,
bit
,
bit
,
GNGPIO_OUTPUT_ENABLE
);
ret
=
!!
(
gennum_readl
(
spec
,
GNGPIO_INPUT_VALUE
)
&
bit
);
}
else
{
if
(
mode
&
GPIOF_INIT_HIGH
)
gennum_mask_val
(
spec
,
bit
,
bit
,
GNGPIO_OUTPUT_VALUE
);
else
gennum_mask_val
(
spec
,
bit
,
0
,
GNGPIO_OUTPUT_VALUE
);
gennum_mask_val
(
spec
,
bit
,
0
,
GNGPIO_DIRECTION_MODE
);
}
/* Then, interrupt configuration, if needed */
if
(
!
(
imode
&
IRQF_TRIGGER_MASK
))
{
gennum_writel
(
spec
,
bit
,
GNGPIO_INT_MASK_SET
);
/* disable */
return
ret
;
}
if
(
imode
&
(
IRQF_TRIGGER_HIGH
|
IRQF_TRIGGER_RISING
))
gennum_mask_val
(
spec
,
bit
,
bit
,
GNGPIO_INT_VALUE
);
else
gennum_mask_val
(
spec
,
bit
,
0
,
GNGPIO_INT_VALUE
);
if
(
imode
&
(
IRQF_TRIGGER_HIGH
|
IRQF_TRIGGER_LOW
))
gennum_mask_val
(
spec
,
bit
,
bit
,
GNGPIO_INT_TYPE
);
else
gennum_mask_val
(
spec
,
bit
,
0
,
GNGPIO_INT_TYPE
);
gennum_mask_val
(
spec
,
bit
,
0
,
GNGPIO_INT_ON_ANY
);
/* me lazy */
gennum_writel
(
spec
,
bit
,
GNGPIO_INT_MASK_CLR
);
/* enable */
return
ret
;
}
static
int
spec_gpio_config
(
struct
fmc_device
*
fmc
,
struct
fmc_gpio
*
gpio
,
int
ngpio
)
{
int
i
,
done
=
0
,
retval
=
0
;
for
(
;
ngpio
;
gpio
++
,
ngpio
--
)
{
if
(
gpio
->
carrier_name
&&
strcmp
(
gpio
->
carrier_name
,
"SPEC"
))
{
/* The array may setup raw pins for various carriers */
continue
;
}
if
(
gpio
->
carrier_name
)
{
/* so, it's ours */
gpio
->
_gpio
=
gpio
->
gpio
;
}
else
if
(
!
gpio
->
_gpio
)
{
/* virtual but not mapped (or poor gpio0) */
i
=
spec_map_pin
(
gpio
->
gpio
);
if
(
i
<
0
)
return
i
;
gpio
->
_gpio
=
i
;
}
i
=
spec_cfg_pin
(
fmc
,
gpio
->
_gpio
,
gpio
->
mode
,
gpio
->
irqmode
);
if
(
i
<
0
)
return
i
;
retval
+=
i
;
/* may be the input value */
done
++
;
}
if
(
!
done
)
return
-
ENODEV
;
return
retval
;
}
/* The engines for this live in spec-i2c.c, we only shape arguments */
static
int
spec_read_ee
(
struct
fmc_device
*
fmc
,
int
pos
,
void
*
data
,
int
len
)
{
...
...
@@ -180,6 +271,7 @@ static struct fmc_operations spec_fmc_operations = {
.
irq_request
=
spec_irq_request
,
.
irq_ack
=
spec_irq_ack
,
.
irq_free
=
spec_irq_free
,
.
gpio_config
=
spec_gpio_config
,
.
read_ee
=
spec_read_ee
,
.
write_ee
=
spec_write_ee
,
};
...
...
kernel/spec-pci.c
View file @
7a1fb9bc
...
...
@@ -127,6 +127,10 @@ static int __devinit spec_probe(struct pci_dev *pdev,
if
(
ret
)
goto
out_unmap
;
/* Put our 6 pins to a sane state (4 test points, 2 from FPGA) */
gennum_mask_val
(
spec
,
0xfc0
,
0x000
,
GNGPIO_BYPASS_MODE
);
/* no AF */
gennum_mask_val
(
spec
,
0xfc0
,
0xfc0
,
GNGPIO_DIRECTION_MODE
);
/* input */
gennum_writel
(
spec
,
0xffff
,
GNGPIO_INT_MASK_SET
);
/* disable */
/* Load the golden FPGA binary to read the eeprom */
ret
=
spec_load_fpga_file
(
spec
,
spec_fw_name
);
...
...
kernel/wr-nic-eth.c
View file @
7a1fb9bc
...
...
@@ -161,6 +161,14 @@ static struct wrn_core wrn_cores2[] = {
}
};
struct
fmc_gpio
wrn_gpio_cfg
[]
=
{
{
.
gpio
=
FMC_GPIO_IRQ
(
1
),
.
mode
=
GPIOF_DIR_IN
,
.
irqmode
=
IRQF_TRIGGER_RISING
,
}
};
int
wrn_eth_init
(
struct
fmc_device
*
fmc
)
{
struct
device
*
dev
=
fmc
->
hwdev
;
...
...
@@ -179,6 +187,8 @@ int wrn_eth_init(struct fmc_device *fmc)
dev_err
(
dev
,
"Can't request interrupt
\n
"
);
return
ret
;
}
/* FIXME: we should request irq0, self-test and then move to irq1 */
fmc
->
op
->
gpio_config
(
fmc
,
wrn_gpio_cfg
,
ARRAY_SIZE
(
wrn_gpio_cfg
));
/* Make a copy of the platform device and register it */
ret
=
-
ENOMEM
;
...
...
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