Compare commits

..

37 Commits

Author SHA1 Message Date
Shaun Ruffell
1ab5723b85 wcte43x: Remove 'dcxo' debug attribute.
Removes another bit of debugging instrumentation that I let slip through.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-20 09:39:12 -06:00
Shaun Ruffell
cbad5a7ae1 wcte43x: Do not grab reglock in handle_transmit/handle_receive.
If the driver is loaded with vpmsupport=0, then it was possible to create a
deadlock situation since the call into __dahdi_ec_chunk might then try to grab
the channel lock while already holding the reglock.

The purpose of grabbing reglock in the DMA routines was to protect the channel
array, which can be changed when linemode is changing. So instead, we'll
completely mask off that interrupt line from all CPUs when potentially changing
the channel array.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-20 09:33:07 -06:00
Shaun RuffellL
b3c6320374 wcaxx: Remove some left over debugging trace statements.
Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-17 02:36:29 -06:00
Shaun Ruffell
69a716af1a wcxb: Update the firmware meta block during flash update.
The meta block contains specific version and checksum information and allows the
test tools to validate the image in the flash.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-16 16:03:19 -06:00
Shaun Ruffell
eed2e4e00b wcaxx: Update A4B firmware to version 0b0017
Resolves some sporadic issues with invalid underruns.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-16 16:02:00 -06:00
Wendell Thompson
466acea7e0 wcte43x: Add driver for TE435/TE235 digital cards.
From: Wendell Thompson <wthompson@digium.com>

These new cards are based on a common architecture with the TE133/TE134 as well
as the new analog cards, A4A/A4B/A8A/A8B.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Russ Meyerriecks
3134b36d7e wcte13xp: Improve maintenance functions and error counters
From: Rus Meyerriecks <rmeyerriecks@digium.com>

* Removed work queue for maint interface as it is not needed in this driver
* Error counters are now accessible through the maint interface
* Consolidated and revised the big maint switch case
* Added loopup/loopdown code transmit logic to E1
* Now supports error/defect insersion

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Russ Meyerriecks
4707e19654 wcte13xp: Hold framer in reset to stop xmit on modprobe -r
From: Russ Meyerriecks <rmeyerriecks@digium.com>

The framer appears to continue transmitting a signal after modprobe -r. This
patch will leave the physical framer reset pin asserted to force the chip to
stop transmitting a signal when there is no driver attached.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Russ Meyerriecks
e42548f2c5 wcte13xp: Migrate to wcxb library
From: Russ Meyerriecks <rmeyerriecks@digium.com>

Removed all the custom logic and replaced with the common platform wcxb stuff.

There are also changes in here to standardize the function prefix in this
driver to t13x_.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Wendell Thompson
abe35059bc wcte13xp: Use interrupts for Falc alarms and signaling
From: Wendell Thompson <wthompson@digium.com>

Now uses framer interrupt vector polling (in the FPGA interrupt) for
alarm, loopcode, and signaling events eliminating continuous polling.
Now uses timer function for alarm and loopcode debouce, which only
executes during exception conditions.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Shaun Ruffell
348f6ab030 wcaxx: New driver for A4A/A4B/A8A/A8B analog cards.
This is a driver for the new line of analog cards that shares a common interface
with the TE133/TE134 and the TE435.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Shaun Ruffell
26efcf6902 wct4xxp, wcte13xp: Move the octasic DSP code into separate module.
The octasic library is relatively large and is currently separately linked into
both the wcte13xp and wct4xxp libraries. This change moves it out into a
separate loadable module.

The bigest change from the drivers perspectives is that they must provide a
table of callbacks instead of using statically linked Oct6100UserXxxxx functions
to allow the library to communicate with actual parts on the cards.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Shaun Ruffell
a648cef0f6 wct4xxp: If linemode changed via sysfs, reset the complete part.
This works around some issues with older versions of the firmware not
working properly after the linemode is changed via sysfs. Basically the
intent is to simulate redoing a complete driver reload with a new
linemode.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Shaun Ruffell
a5a52dbe94 wct4xxp: VPM companding switch print is now debug only.
This print happened when the linemode was changed via sysfs, but it didn't
really add much extra information that a user could act on.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Shaun Ruffell
43ad3eeb65 wct4xxp: Fix bipolar error insertion test mode.
This change wouldn't affect most people since it's only used by the error
injection code.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Shaun Ruffell
706dc43483 wct4xxp: Print warning in dmesg if span priority is not set correctly.
dahdi_genconf currently does not take into account the number of spans
configured for a particular board. Therefore if you have two dual span cards
installed in the system, it could be possible to have something like this in
/etc/dahdi/system.conf:

  spans=1,1,...
  spans=2,2,...
  spans=3,3,...
  spans=4,4,...

When what you really want is something like this:

  spans=1,1,...
  spans=2,2,...
  spans=3,1,...
  spans=4,2,...

Otherwise, spans 3 and 4 will be configured to not recover the clock from the
span which is most likely NOT what you want.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Shaun Ruffell
92f1e46b8f dahdi: Backport try_wait_for_completion() and list_first_entry()
This allows drivers to use the newer interface but build against older
kernels.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Shaun Ruffell
abb09a012d dahdi: Work around missing KBUILD_MODNAME
Some older versions of Kbuild will not pass KBUILD_MODNAME to a
compilation unit that is linked to multiple drivers. This change works
around this issue by globally modifing dev_dbg in this case.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:19 -06:00
Shaun Ruffell
7405dd6038 dahdi: Fix placement of '/' in output of /proc/dahdi/x
Fixes strings that look like:
  Span 1: TE2/0/1 "T2XXP (PCI) Card 0 Span 1" CCSHDB3//CRC4 RED

To look like:
  Span 1: TE2/0/1 "T2XXP (PCI) Card 0 Span 1" CCS/HDB3/CRC4 RED

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:18 -06:00
Shaun Ruffell
496f817773 dahdi: Clear DAHDI_ALARM_NOTOPEN when spans are re-initialized.
This eliminates the need for board drivers to always re-report their alarm
states when spans are unassigned and then reassigned.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:18 -06:00
Shaun Ruffell
4a6ecbbdeb dahdi_config: Remove unused NO_DCDC definition.
There are not any drivers in the tree that make reference to this definition
anymore, so it can be removed.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-11-11 14:39:18 -06:00
Tzafrir Cohen
bd0366287e xpp: Fail loading if no module on first slot
The driver assumes that the first slot is not empty. If this is not the
case, synchronization will not work.

Fail loading if this assertion does not hold.

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-11-10 16:39:36 +02:00
Tzafrir Cohen
f287c0af7f xpp: mark an AB as failed if it gives bad desc
If we fail at handling the device descriptor from the Astribank, mark it
as in state "failed".

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-11-10 16:39:36 +02:00
Tzafrir Cohen
7adc74512e xpp: USB_FW.202.hex: provide as a symlink
Provide USB_FW.202.hex as a (install-time) symlink to USB_FW.201.hex.

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-11-07 17:08:45 +02:00
Tzafrir Cohen
b993d25c5a xpp: Firmware for Astribanks 2.02: Makefile
Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-11-06 16:57:48 +02:00
Tzafrir Cohen
41ae90a8a3 xpp: Firmware for Astribanks 2.02
Astribank 2.02: a newer model: slight variation of 2.01 (both use E-Main
4). Uses the same USB firmware, has to use a different FPGA firmware as
newer devices cannot be made to work with older 2.01 firmware.

FPGA rev. 11307.

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-10-21 16:51:30 +03:00
Oron Peled
d4c8eb47ac add a 'location' attribute to sysfs (dahdi_device):
* Our user-space previously used the implicit location from the sysfs
  device path of the "dahdi_device".
* However:
  - Sysfs paths are very limited in length.
  - Low-level driver need more control on the location field.
  - For example, it need to be persistant for span_assignments.
* We now export explicitly the 'location' field which is managed by
  low-level drivers.
* We will use this field as the location in span_assignments.

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-10-01 23:42:21 +03:00
Oron Peled
2bd36a3307 xpp: ring/mwi settings: add to FXS init script
init_card_1_30 (FXS modules init script) can now use the new sysfs
interface to set ring settings.

Currently it only overwrite a single register

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-10-01 15:25:42 +03:00
Oron Peled
600dd03e30 xpp: FXS: ring/mwi settings: a sysfs interface
A new SysFS interface for FXS modules (XPDs):
* In /sys/bus/xpds/devices/<ll>:<m>:<n>/fxs_ring_registers
* Reading show current register values for NEON/TRAPEZ/NORMAL
* Modify the table via writing: "<column> <reg> <byte> [<byte>]"
* Example:
	echo "NEON 0x33 0x12" > \
		'/sys/bus/xpds/devices/00:0:0/fxs_ring_registers'

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-10-01 15:23:56 +03:00
Oron Peled
3d1d87e526 xpp: refactor FXS ring settings
xpp: refactor the FXS module ring settings into separate data structure:

* Update High VBAT initialization in init_card_1_30:
  - Take the value used in set_vm_led_mode() (0x34)
  - Now we don't need to set it over and over again in set_vm_led_mode()
* Create a unified ring_parameters[] array for all ring registers:
  - Columns for 3 ring types (NEON, TRAPEZ, NORMAL)
  - Used by new send_ring_parameters() function
* Now the set_vm_led_mode() simply calls send_ring_parameters()
* This cleanup would allow us to change ring parameters at runtime (by
  updating the values in these tables).

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-10-01 15:22:32 +03:00
Oron Peled
745118acb9 xpp: Serialize dahdi registration
xpp: Serialize dahdi registration: Cause races under highly-parallel
workloads (with dahdi_autoreg=0).

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-09-29 19:13:26 +03:00
Russ Meyerriecks
ce3f1f2650 wcte13xp: Workaround rare nmi on modprobe on select systems
With certain pci controllers that support pci hotplug, during the reset
sequence of the fpga, the host controller can get confused about the state of
the card and throw an nmi.

Known affected systems:
 HP proliant DL160 & DL360p
 Dell poweredge R520
 Super Micro X7SPA-HF

This patch attempts to work around that by removing the fpga reset sequence
from the driver startup, then setting various control registers to known
starting state values.

This patch removes the field upgradeable firmware logic temporarily for the
te133 card to prevent fpga reset.

Signed-off-by: Russ Meyerriecks <rmeyerriecks@digium.com>
2013-08-16 15:28:24 -05:00
Oron Peled
2ad4dc555f xpp: fix waitfor_xpds race at startup
* If userspace would run
    modprobe xpp_usb && cat /sys/bus/astribanks/devices/xbus-00/waitfor_xpds
  it would return immediately rather than wait for the Astribank to
  initialize.
* We sometimes managed to read before getting the reply from the
  astribank.
* Now we don't trust unit count at such an early stage.
* Instread we wait for the Astribank to reach a stable state (READY or FAIL)
  before finish waiting for this astribank.

Internal-Issue-ID: Xorcom-1502
Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-07-30 15:04:00 +03:00
Shaun Ruffell
0469efb733 build_tools/make_version: Fix typo in build_tools/make_version.
The make_version script would test for a .version file in the proper location,
but then would not properly read it. Since make_version is always reading the
.version script correctly now, the Makefile can be slightly simpilfied and
always defer to it.

This has no user impact since build_tools/make_version was never used to read
the .version file previously but will allow external scripts to use the
make_version script.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
Signed-off-by: Russ Meyerriecks <rmeyerriecks@digium.com>
2013-06-26 14:32:15 -05:00
Oron Peled
8ea23535dc sysfs: bugfix: shorten too long file names
* In old kernels (e.g: 2.6.18) sysfs file names must be shorter
  then KOBJ_NAME_LEN.

* Longer names are truncated and this leads to duplicate names
  which fails device_register()
  Examples: "dahdi!channels!10!10" is truncated to "dahdi!channels!10!1"

* The fix shorten the names and make them fixed length. Example:
            "dahdi!chan!010!010"

* It seems no current userspace tools rely on that name or on
  /dev/dahdi/channels .

Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
2013-06-23 22:12:33 +03:00
Russ Meyerriecks
431571b5fc wcte13xp: Start the span in unconfigured, instead of red state
This was causing inconsistancies with dahdi_scan showing an "UNCONFIGURED" span
in the "RED" state.

Signed-off-by: Russ Meyerriecks <rmeyerriecks@digium.com>
2013-06-14 14:55:37 -05:00
Shaun Ruffell
ab927f796b dahdi: Do not set rxbufpolicy when opening dahdi net device.
This fixes a regression introduced in 2.7.0 in commit
(da8b96d725 "dahdi: Remove unused 'rxbufpolicy'
and 'rxdisable' from dahdi_chan.") when CONFIG_DAHDI_NET is defined in
include/dahdi/dahdi_config.h.

rxbufpolicy was always hardcoded to immediate policy and was removed from the
channel structure. There is no longer any need to set it in dahdi_net_open.

Reported-by: Dave Fullerton <dfullertasterisk@shorelinecontainer.com>
Signed-off-by: Shaun Ruffell <sruffell@digium.com>
Signed-off-by: Russ Meyerriecks <rmeyerriecks@digium.com>
2013-06-11 10:25:10 -05:00
34 changed files with 32282 additions and 1658 deletions

View File

@@ -64,11 +64,7 @@ ASCIIDOC_CMD:=$(ASCIIDOC) -n -a toc -a toclevels=4
GENERATED_DOCS:=README.html
ifneq ($(wildcard .version),)
DAHDIVERSION:=$(shell cat .version)
else
DAHDIVERSION:=$(shell build_tools/make_version . dahdi/linux)
endif
DAHDIVERSION:=$(shell build_tools/make_version . dahdi/linux)
all: modules
@@ -86,7 +82,7 @@ include/dahdi/version.h: FORCE
fi
@rm -f $@.tmp
prereq: include/dahdi/version.h firmware-loaders oct612x-lib
prereq: include/dahdi/version.h firmware-loaders
stackcheck: $(CHECKSTACK) modules
objdump -d drivers/dahdi/*.ko drivers/dahdi/*/*.ko | $(CHECKSTACK)
@@ -122,13 +118,6 @@ uninstall-firmware:
firmware-loaders:
$(MAKE) -C drivers/dahdi/firmware firmware-loaders
oct612x-lib:
ifeq (no,$(HAS_KSRC))
@echo "You do not appear to have the sources for the $(KVERS) kernel installed."
@exit 1
endif
$(MAKE) -C $(KSRC) M='$(PWD)/drivers/dahdi/oct612x'
install-include:
for hdr in $(INST_HEADERS); do \
install -D -m 644 include/dahdi/$$hdr $(DESTDIR)/usr/include/dahdi/$$hdr; \

6
README
View File

@@ -646,8 +646,10 @@ or dynamically through the udev system.
* /dev/dahdi/ctl (196:0) - a general device file for various information and
control operations on the DAHDI channels.
* /dev/dahdi/channels/N/M - A device file for channel M in span N
(M is chanpos - numbering relative to the current span).
* /dev/dahdi/chan/N/M - A device file for channel M in span N
- Both N and M are zero padded 3 digit numbers
- Both N and M start at 001
- M is chanpos - numbering relative to the current span.
* /dev/dahdi/NNN (196:NNN) - for NNN in the range 1-249. A device file for
DAHDI channel NNN. It can be used to read data from the channel
and write data to the channel. It is not generated by default but may

View File

@@ -1,7 +1,7 @@
#!/bin/sh
if [ -f ${1}/.version ]; then
cat ${1}.version
cat ${1}/.version
elif [ -f ${1}/.svnrevision ]; then
echo SVN-`cat ${1}/.svnbranch`-r`cat ${1}/.svnrevision`
elif [ -d ${1}/.svn ]; then

View File

@@ -7,18 +7,35 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_ETHMF) += dahdi_dynamic_ethmf.o
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_TRANSCODE) += dahdi_transcode.o
ifdef CONFIG_PCI
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OCT612X) += oct612x/
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCT4XXP) += wct4xxp/
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTC4XXP) += wctc4xxp/
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP) += wctdm24xxp/
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE12XP) += wcte12xp/
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE13XP) += wcte13xp.o
wcte13xp-objs := wcte13xp-base.o oct612x/lib.a
CFLAGS_wcte13xp-base.o += -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
wcte13xp-objs := wcte13xp-base.o wcxb_spi.o wcxb.o wcxb_flash.o
CFLAGS_wcte13xp-base.o += -I$(src)/oct612x -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
ifeq ($(HOTPLUG_FIRMWARE),yes)
CFLAGS_wcte13xp-base.o += -DHOTPLUG_FIRMWARE
endif
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE43X) += wcte43x.o
wcte43x-objs := wcte43x-base.o wcxb_spi.o wcxb.o wcxb_flash.o
CFLAGS_wcte43x-base.o += -I$(src)/oct612x -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
ifeq ($(HOTPLUG_FIRMWARE),yes)
CFLAGS_wcte43x-base.o += -DHOTPLUG_FIRMWARE
endif
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCAXX) += wcaxx.o
wcaxx-objs := wcaxx-base.o wcxb_spi.o wcxb.o wcxb_flash.o
CFLAGS_wcaxx-base.o += -I$(src)/oct612x/ -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
ifeq ($(HOTPLUG_FIRMWARE),yes)
CFLAGS_wcaxx-base.o += -DHOTPLUG_FIRMWARE
endif
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM) += wctdm.o
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_VOICEBUS) += voicebus/
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCB4XXP) += wcb4xxp/

View File

@@ -787,6 +787,7 @@ ssize_t lineconfig_str(int lineconfig, char buf[], size_t size)
int crc4_bit = 0;
int len = 0;
int bit;
bool written = false;
for (bit = 4; bit <= 12; bit++) {
int mask = (1 << bit);
@@ -798,8 +799,10 @@ ssize_t lineconfig_str(int lineconfig, char buf[], size_t size)
case DAHDI_CONFIG_AMI:
case DAHDI_CONFIG_HDB3:
framing_bit = bit;
len += snprintf(buf + len, size, "%s/",
len += snprintf(buf + len, size, "%s%s",
(written) ? "/" : "",
dahdi_lineconfig_bit_name(bit));
written = true;
}
}
if (!coding_bit) {
@@ -808,14 +811,18 @@ ssize_t lineconfig_str(int lineconfig, char buf[], size_t size)
case DAHDI_CONFIG_D4:
case DAHDI_CONFIG_CCS:
coding_bit = bit;
len += snprintf(buf + len, size, "%s",
len += snprintf(buf + len, size, "%s%s",
(written) ? "/" : "",
dahdi_lineconfig_bit_name(bit));
written = true;
}
}
if (!crc4_bit && mask == DAHDI_CONFIG_CRC4) {
crc4_bit = bit;
len += snprintf(buf + len, size, "/%s",
len += snprintf(buf + len, size, "%s%s",
(written) ? "/" : "",
dahdi_lineconfig_bit_name(bit));
written = true;
}
}
return len;
@@ -1964,7 +1971,6 @@ static int dahdi_net_open(struct net_device *dev)
return -EINVAL;
}
ms->txbufpolicy = DAHDI_POLICY_IMMEDIATE;
ms->rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
res = dahdi_reallocbufs(ms, DAHDI_DEFAULT_MTU_MRU, DAHDI_DEFAULT_NUM_BUFS);
if (res)
@@ -7154,6 +7160,13 @@ static void __dahdi_init_span(struct dahdi_span *span)
spin_lock_init(&span->lock);
clear_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags);
/* DAHDI_ALARM_NOTOPEN can be set when a span is disabled, i.e. via
* sysfs, so when the span is being initialized again before
* reassignment we should make sure it's cleared. This eliminates the
* need for board drivers to re-report their alarm states on span
* reassignment. */
span->alarms &= ~DAHDI_ALARM_NOTOPEN;
if (!span->deflaw) {
module_printk(KERN_NOTICE, "Span %s didn't specify default "
"law. Assuming mulaw, please fix driver!\n",

View File

@@ -247,7 +247,7 @@ int chan_sysfs_create(struct dahdi_chan *chan)
/*
* FIXME: the name cannot be longer than KOBJ_NAME_LEN
*/
dev_set_name(dev, "dahdi!channels!%d!%d", span->spanno, chan->chanpos);
dev_set_name(dev, "dahdi!chan!%03d!%03d", span->spanno, chan->chanpos);
dev_set_drvdata(dev, chan);
dev->release = chan_release;
res = device_register(dev);

View File

@@ -402,6 +402,16 @@ dahdi_device_hardware_id_show(struct device *dev,
(ddev->hardware_id) ? ddev->hardware_id : "");
}
static ssize_t
dahdi_device_location_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dahdi_device *ddev = to_ddev(dev);
return sprintf(buf, "%s\n",
(ddev->location) ? ddev->location : "");
}
static ssize_t
dahdi_device_auto_assign(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
@@ -549,6 +559,7 @@ static struct device_attribute dahdi_device_attrs[] = {
__ATTR(type, S_IRUGO, dahdi_device_type_show, NULL),
__ATTR(span_count, S_IRUGO, dahdi_device_span_count_show, NULL),
__ATTR(hardware_id, S_IRUGO, dahdi_device_hardware_id_show, NULL),
__ATTR(location, S_IRUGO, dahdi_device_location_show, NULL),
__ATTR(auto_assign, S_IWUSR, NULL, dahdi_device_auto_assign),
__ATTR(assign_span, S_IWUSR, NULL, dahdi_device_assign_span),
__ATTR(unassign_span, S_IWUSR, NULL, dahdi_device_unassign_span),

View File

@@ -3,7 +3,7 @@
#
# Makefile for firmware downloading/installation
#
# Copyright (C) 2007-2010, Digium, Inc.
# Copyright (C) 2007-2013, Digium, Inc.
#
# Joshua Colp <jcolp@digium.com>
#
@@ -33,10 +33,17 @@ VPMOCT032_VERSION:=1.12.0
WCT820_VERSION:=1.76
TE133_VERSION:=6f0017
TE134_VERSION:=6f0017
TE435_VERSION:=d0017
A8A_VERSION:=1d0017
A8B_VERSION:=1d0017
A4A_VERSION:=a0017
A4B_VERSION:=b0017
FIRMWARE_URL:=http://downloads.digium.com/pub/telephony/firmware/releases
ALL_FIRMWARE=FIRMWARE-OCT6114-032 FIRMWARE-OCT6114-064 FIRMWARE-OCT6114-128 FIRMWARE-OCT6114-256 FIRMWARE-TC400M FIRMWARE-HX8 FIRMWARE-VPMOCT032 FIRMWARE-TE820 FIRMWARE-TE133 FIRMWARE-TE134
ALL_FIRMWARE=FIRMWARE-OCT6114-032 FIRMWARE-OCT6114-064 FIRMWARE-OCT6114-128 FIRMWARE-OCT6114-256
ALL_FIRMWARE+=FIRMWARE-TC400M FIRMWARE-HX8 FIRMWARE-VPMOCT032 FIRMWARE-TE820 FIRMWARE-TE133 FIRMWARE-TE134
ALL_FIRMWARE+=FIRMWARE-A8A FIRMWARE-A8B FIRMWARE-A4A FIRMWARE-A4B FIRMWARE-TE435
# Firmware files should use the naming convention: dahdi-fw-<base name>-<sub name>-<version> or dahdi-fw-<base name>-<version>
# First example: dahdi-fw-oct6114-064-1.05.01
@@ -55,6 +62,11 @@ FIRMWARE:=$(FIRMWARE:FIRMWARE-VPMOCT032=dahdi-fw-vpmoct032-$(VPMOCT032_VERSION).
FIRMWARE:=$(FIRMWARE:FIRMWARE-TE820=dahdi-fw-te820-$(WCT820_VERSION).tar.gz)
FIRMWARE:=$(FIRMWARE:FIRMWARE-TE133=dahdi-fw-te133-$(TE133_VERSION).tar.gz)
FIRMWARE:=$(FIRMWARE:FIRMWARE-TE134=dahdi-fw-te134-$(TE134_VERSION).tar.gz)
FIRMWARE:=$(FIRMWARE:FIRMWARE-TE435=dahdi-fw-te435-$(TE435_VERSION).tar.gz)
FIRMWARE:=$(FIRMWARE:FIRMWARE-A8A=dahdi-fw-a8b-$(A8B_VERSION).tar.gz)
FIRMWARE:=$(FIRMWARE:FIRMWARE-A8B=dahdi-fw-a8a-$(A8A_VERSION).tar.gz)
FIRMWARE:=$(FIRMWARE:FIRMWARE-A4A=dahdi-fw-a4b-$(A4B_VERSION).tar.gz)
FIRMWARE:=$(FIRMWARE:FIRMWARE-A4B=dahdi-fw-a4a-$(A4A_VERSION).tar.gz)
FWLOADERS:=dahdi-fwload-vpmadt032-$(VPMADT032_VERSION).tar.gz
@@ -225,6 +237,61 @@ ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te134-$(TE
else
@echo "Firmware dahdi-fw-te134.bin is already installed with required version $(TE134_VERSION)"
endif
ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te435-$(TE435_VERSION) ] ) && ( [ -f $(DESTDIR)/lib/firmware/.dahdi-fw-te435-$(TE435_VERSION) ] ); then echo "no"; else echo "yes"; fi),yes)
@echo "Installing dahdi-fw-te435.bin to hotplug firmware directories"
@install -m 644 dahdi-fw-te435.bin $(DESTDIR)/usr/lib/hotplug/firmware
@rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te435-*
@touch $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te435-$(TE435_VERSION)
@install -m 644 dahdi-fw-te435.bin $(DESTDIR)/lib/firmware
@rm -rf $(DESTDIR)/lib/firmware/.dahdi-fw-te435-*
@touch $(DESTDIR)/lib/firmware/.dahdi-fw-te435-$(TE435_VERSION)
else
@echo "Firmware dahdi-fw-te435.bin is already installed with required version $(TE435_VERSION)"
endif
ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a8a-$(A8A_VERSION) ] ) && ( [ -f $(DESTDIR)/lib/firmware/.dahdi-fw-a8a-$(A8A_VERSION) ] ); then echo "no"; else echo "yes"; fi),yes)
@echo "Installing dahdi-fw-a8a.bin to hotplug firmware directories"
@install -m 644 dahdi-fw-a8a.bin $(DESTDIR)/usr/lib/hotplug/firmware
@rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a8a-*
@touch $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a8a-$(A8A_VERSION)
@install -m 644 dahdi-fw-a8a.bin $(DESTDIR)/lib/firmware
@rm -rf $(DESTDIR)/lib/firmware/.dahdi-fw-a8a-*
@touch $(DESTDIR)/lib/firmware/.dahdi-fw-a8a-$(A8A_VERSION)
else
@echo "Firmware dahdi-fw-a8a.bin is already installed with required version $(A8A_VERSION)"
endif
ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a8b-$(A8B_VERSION) ] ) && ( [ -f $(DESTDIR)/lib/firmware/.dahdi-fw-a8b-$(A8B_VERSION) ] ); then echo "no"; else echo "yes"; fi),yes)
@echo "Installing dahdi-fw-a8b.bin to hotplug firmware directories"
@install -m 644 dahdi-fw-a8b.bin $(DESTDIR)/usr/lib/hotplug/firmware
@rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a8b-*
@touch $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a8b-$(A8B_VERSION)
@install -m 644 dahdi-fw-a8b.bin $(DESTDIR)/lib/firmware
@rm -rf $(DESTDIR)/lib/firmware/.dahdi-fw-a8b-*
@touch $(DESTDIR)/lib/firmware/.dahdi-fw-a8b-$(A8B_VERSION)
else
@echo "Firmware dahdi-fw-a8b.bin is already installed with required version $(A8B_VERSION)"
endif
ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a4a-$(A4A_VERSION) ] ) && ( [ -f $(DESTDIR)/lib/firmware/.dahdi-fw-a4a-$(A4A_VERSION) ] ); then echo "no"; else echo "yes"; fi),yes)
@echo "Installing dahdi-fw-a4a.bin to hotplug firmware directories"
@install -m 644 dahdi-fw-a4a.bin $(DESTDIR)/usr/lib/hotplug/firmware
@rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a4a-*
@touch $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a4a-$(A4A_VERSION)
@install -m 644 dahdi-fw-a4a.bin $(DESTDIR)/lib/firmware
@rm -rf $(DESTDIR)/lib/firmware/.dahdi-fw-a4a-*
@touch $(DESTDIR)/lib/firmware/.dahdi-fw-a4a-$(A4A_VERSION)
else
@echo "Firmware dahdi-fw-a4a.bin is already installed with required version $(A4A_VERSION)"
endif
ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a4b-$(A4B_VERSION) ] ) && ( [ -f $(DESTDIR)/lib/firmware/.dahdi-fw-a4b-$(A4B_VERSION) ] ); then echo "no"; else echo "yes"; fi),yes)
@echo "Installing dahdi-fw-a4b.bin to hotplug firmware directories"
@install -m 644 dahdi-fw-a4b.bin $(DESTDIR)/usr/lib/hotplug/firmware
@rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a4b-*
@touch $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-a4b-$(A4B_VERSION)
@install -m 645 dahdi-fw-a4b.bin $(DESTDIR)/lib/firmware
@rm -rf $(DESTDIR)/lib/firmware/.dahdi-fw-a4b-*
@touch $(DESTDIR)/lib/firmware/.dahdi-fw-a4b-$(A4B_VERSION)
else
@echo "Firmware dahdi-fw-a4b.bin is already installed with required version $(A4B_VERSION)"
endif
# Uninstall any installed dahdi firmware images from hotplug firmware directories
hotplug-uninstall:

View File

@@ -0,0 +1,32 @@
#
# Produces the oct612x library
#
octapi_files = octdeviceapi/oct6100api/oct6100_api/oct6100_adpcm_chan.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_channel.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_chip_open.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_chip_stats.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_conf_bridge.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_debug.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_events.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_interrupts.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_memory.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_miscellaneous.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_mixer.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_phasing_tsst.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_playout_buf.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_remote_debug.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_tlv.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_tone_detection.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_tsi_cnct.o \
octdeviceapi/oct6100api/oct6100_api/oct6100_tsst.o \
apilib/bt/octapi_bt0.o \
apilib/largmath/octapi_largmath.o \
apilib/llman/octapi_llman.o \
oct612x-user.o
# TODO: ccflags was added in 2.6.24 in commit f77bf01425b11947eeb3b5b54. This
# should be changed to a conditional compilation based on the Kernel Version.
# ccflags-y := -I$(src)/.. -Wno-undef -I$(src)/include -I$(src)/octdeviceapi -I$(src)/octdeviceapi/oct6100api
EXTRA_CFLAGS = -I$(src)/.. -Wno-undef -I$(src)/include -I$(src)/octdeviceapi -I$(src)/octdeviceapi/oct6100api
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OCT612X) := oct612x.o
oct612x-objs := $(octapi_files)

View File

@@ -0,0 +1,181 @@
/*
* Octasic OCT6100 Interface
*
* Copyright (C) 2013 Digium, Inc.
*
* All rights reserved.
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include "oct612x.h"
UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime)
{
/* Why couldn't they just take a timeval like everyone else? */
struct timeval tv;
unsigned long long total_usecs;
unsigned int mask = ~0;
do_gettimeofday(&tv);
total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) +
(((unsigned long long)(tv.tv_usec)));
f_pTime->aulWallTimeUs[0] = (total_usecs & mask);
f_pTime->aulWallTimeUs[1] = (total_usecs >> 32);
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern,
UINT32 f_ulLength)
{
memset(f_pAddress, f_ulPattern, f_ulLength);
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource,
UINT32 f_ulLength)
{
memcpy(f_pDestination, f_pSource, f_ulLength);
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserCreateSerializeObject(
tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate)
{
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDestroySerializeObject(
tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy)
{
#ifdef OCTASIC_DEBUG
pr_debug("I should never be called! (destroy serialize object)\n");
#endif
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserSeizeSerializeObject(
tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize)
{
/* Not needed */
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserReleaseSerializeObject(
tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease)
{
/* Not needed */
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams)
{
struct oct612x_context *context = f_pWriteParams->pProcessContext;
#ifdef OCTASIC_DEBUG
if (!context || !context->ops || !context->ops->write) {
pr_debug("Invalid call to %s\n", __func__);
return cOCT6100_ERR_BASE;
}
#endif
context->ops->write(context, f_pWriteParams->ulWriteAddress,
f_pWriteParams->usWriteData);
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams)
{
struct oct612x_context *context = f_pSmearParams->pProcessContext;
#ifdef OCTASIC_DEBUG
if (!context || !context->ops || !context->ops->write_smear) {
pr_debug("Invalid call to %s\n", __func__);
return cOCT6100_ERR_BASE;
}
#endif
context->ops->write_smear(context, f_pSmearParams->ulWriteAddress,
f_pSmearParams->usWriteData,
f_pSmearParams->ulWriteLength);
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverWriteBurstApi(
tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams)
{
struct oct612x_context *context = f_pBurstParams->pProcessContext;
#ifdef OCTASIC_DEBUG
if (!context || !context->ops || !context->ops->write_burst) {
pr_debug("Invalid call to %s\n", __func__);
return cOCT6100_ERR_BASE;
}
#endif
context->ops->write_burst(context, f_pBurstParams->ulWriteAddress,
f_pBurstParams->pusWriteData,
f_pBurstParams->ulWriteLength);
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams)
{
struct oct612x_context *context = f_pReadParams->pProcessContext;
#ifdef OCTASIC_DEBUG
if (!context || !context->ops || !context->ops->read) {
pr_debug("Invalid call to %s\n", __func__);
return cOCT6100_ERR_BASE;
}
#endif
context->ops->read(context, f_pReadParams->ulReadAddress,
f_pReadParams->pusReadData);
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams)
{
struct oct612x_context *context = f_pBurstParams->pProcessContext;
#ifdef OCTASIC_DEBUG
if (!context || !context->ops || !context->ops->read_burst) {
pr_debug("Invalid call to %s\n", __func__);
return cOCT6100_ERR_BASE;
}
#endif
context->ops->read_burst(context, f_pBurstParams->ulReadAddress,
f_pBurstParams->pusReadData,
f_pBurstParams->ulReadLength);
return cOCT6100_ERR_OK;
}
EXPORT_SYMBOL(Oct6100ChipOpen);
EXPORT_SYMBOL(Oct6100ChipClose);
EXPORT_SYMBOL(Oct6100ChipCloseDef);
EXPORT_SYMBOL(Oct6100GetInstanceSize);
EXPORT_SYMBOL(Oct6100GetInstanceSizeDef);
EXPORT_SYMBOL(Oct6100ChipOpenDef);
EXPORT_SYMBOL(Oct6100ChannelModify);
EXPORT_SYMBOL(Oct6100ToneDetectionEnableDef);
EXPORT_SYMBOL(Oct6100InterruptServiceRoutine);
EXPORT_SYMBOL(Oct6100InterruptServiceRoutineDef);
EXPORT_SYMBOL(Oct6100ApiGetCapacityPins);
EXPORT_SYMBOL(Oct6100ToneDetectionEnable);
EXPORT_SYMBOL(Oct6100EventGetToneDef);
EXPORT_SYMBOL(Oct6100EventGetTone);
EXPORT_SYMBOL(Oct6100ApiGetCapacityPinsDef);
EXPORT_SYMBOL(Oct6100ChannelOpen);
EXPORT_SYMBOL(Oct6100ChannelOpenDef);
EXPORT_SYMBOL(Oct6100ChannelModifyDef);
MODULE_AUTHOR("Digium Incorporated <support@digium.com>");
MODULE_DESCRIPTION("Octasic OCT6100 Hardware Echocan Library");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,49 @@
/*
* Octasic OCT6100 Interface
*
* Copyright (C) 2013 Digium, Inc.
*
* All rights reserved.
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#ifndef __OCT612X_H__
#define __OCT612X_H__
#include <oct6100api/oct6100_api.h>
struct oct612x_context;
/**
* struct oct612x_ops - Callbacks used by oct612x library to talk to part.
*
*/
struct oct612x_ops {
int (*write)(struct oct612x_context *context, u32 address, u16 value);
int (*read)(struct oct612x_context *context, u32 address, u16 *value);
int (*write_smear)(struct oct612x_context *context, u32 address,
u16 value, size_t count);
int (*write_burst)(struct oct612x_context *context, u32 address,
const u16 *value, size_t count);
int (*read_burst)(struct oct612x_context *context, u32 address,
u16 *value, size_t count);
};
struct oct612x_context {
const struct oct612x_ops *ops;
struct device *dev;
};
#endif /* __OCT612X_H__ */

4540
drivers/dahdi/wcaxx-base.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCT4XXP) += wct4xxp.o
FIRM_DIR := ../firmware
EXTRA_CFLAGS += -I$(src)/.. $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef
EXTRA_CFLAGS += -I$(src)/.. -I$(src)/../oct612x/ $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef
# The OCT612X source files are from a vendor drop and we do not want to edit
# them to make this warning go away. Therefore, turn off the
@@ -14,7 +14,7 @@ ifeq ($(HOTPLUG_FIRMWARE),yes)
EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE
endif
wct4xxp-objs := base.o vpm450m.o ../oct612x/lib.a
wct4xxp-objs := base.o vpm450m.o
ifneq ($(HOTPLUG_FIRMWARE),yes)
wct4xxp-objs += $(FIRM_DIR)/dahdi-fw-oct6114-064.o $(FIRM_DIR)/dahdi-fw-oct6114-128.o $(FIRM_DIR)/dahdi-fw-oct6114-256.o

View File

@@ -183,7 +183,7 @@ static inline int t4_queue_work(struct workqueue_struct *wq, struct work_struct
"CONFIG_NOEXTENDED_RESET."
#endif
static int debug=0;
int debug = 0;
static int timingcable = 0;
static int t1e1override = -1; /* deprecated */
static char *default_linemode = "auto";
@@ -299,6 +299,7 @@ struct t4_span {
unsigned long alarmcheck_time;
int spanflags;
int syncpos;
#ifdef SUPPORT_GEN1
int e1check; /* E1 check */
#endif
@@ -345,6 +346,7 @@ struct t4 {
int irq; /* IRQ used by device */
int order; /* Order */
const struct devtype *devtype;
unsigned int reset_required:1; /* If reset needed in serial_setup */
unsigned int falc31:1; /* are we falc v3.1 (atomic not necessary) */
unsigned int t1e1:8; /* T1 / E1 select pins */
int ledreg; /* LED Register */
@@ -505,7 +507,7 @@ static void t4_check_sigbits(struct t4 *wc, int span);
#define FMR5_EIBR (1 << 6) /* Internal Bit Robbing Access */
#define DEC_T 0x60 /* Diable Error Counter */
#define IERR_T 0x1B /* Single Bit Defect Insertion Register */
#define IBV 0 /* Bipolar violation */
#define IBV (1 << 0) /* Bipolar violation */
#define IPE (1 << 1) /* PRBS defect */
#define ICASE (1 << 2) /* CAS defect */
#define ICRCE (1 << 3) /* CRC defect */
@@ -1712,8 +1714,11 @@ _t4_spanconfig(struct file *file, struct dahdi_span *span,
if (lc->sync < 0)
lc->sync = 0;
if (lc->sync > wc->numspans)
if (lc->sync > wc->numspans) {
dev_warn(&wc->dev->dev, "WARNING: Cannot set priority on span %d to %d. Please set to a number between 1 and %d\n",
span->spanno, lc->sync, wc->numspans);
lc->sync = 0;
}
/* remove this span number from the current sync sources, if there */
for(i = 0; i < wc->numspans; i++) {
@@ -1890,6 +1895,33 @@ static void setup_chunks(struct t4 *wc, int which)
}
}
static int __t4_hardware_init_1(struct t4 *wc, unsigned int cardflags,
bool first_time);
static int __t4_hardware_init_2(struct t4 *wc, bool first_time);
static int t4_hardware_stop(struct t4 *wc);
static void t4_framer_reset(struct t4 *wc)
{
const bool first_time = false;
bool have_vpm = wc->vpm != NULL;
if (have_vpm) {
release_vpm450m(wc->vpm);
wc->vpm = NULL;
}
t4_hardware_stop(wc);
__t4_set_sclk_src(wc, WC_SELF, 0, 0);
__t4_hardware_init_1(wc, wc->devtype->flags, first_time);
__t4_hardware_init_2(wc, first_time);
if (have_vpm) {
t4_vpm_init(wc);
wc->dmactrl |= (wc->vpm) ? T4_VPM_PRESENT : 0;
t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
}
setup_chunks(wc, 0);
wc->lastindex = 0;
}
/**
* t4_serial_setup - Setup serial parameters and system interface.
* @wc: The card to configure.
@@ -1899,6 +1931,7 @@ static void t4_serial_setup(struct t4 *wc)
{
unsigned long flags;
unsigned int unit;
bool reset_required = false;
if (debug) {
dev_info(&wc->dev->dev,
@@ -1906,6 +1939,14 @@ static void t4_serial_setup(struct t4 *wc)
wc->numspans);
}
spin_lock_irqsave(&wc->reglock, flags);
reset_required = wc->reset_required > 0;
wc->reset_required = 0;
spin_unlock_irqrestore(&wc->reglock, flags);
if (reset_required)
t4_framer_reset(wc);
spin_lock_irqsave(&wc->reglock, flags);
/* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from
* channel 0 */
@@ -2056,6 +2097,7 @@ static void t4_span_assigned(struct dahdi_span *span)
struct t4 *wc = tspan->owner;
struct dahdi_span *pos;
unsigned int unassigned_spans = 0;
unsigned long flags;
/* We use this to make sure all the spans are assigned before
* running the serial setup. */
@@ -2064,8 +2106,14 @@ static void t4_span_assigned(struct dahdi_span *span)
++unassigned_spans;
}
if (0 == unassigned_spans)
if (0 == unassigned_spans) {
t4_serial_setup(wc);
set_bit(T4_CHECK_TIMING, &wc->checkflag);
spin_lock_irqsave(&wc->reglock, flags);
__t4_set_sclk_src(wc, WC_SELF, 0, 0);
spin_unlock_irqrestore(&wc->reglock, flags);
}
}
static void free_wc(struct t4 *wc)
@@ -2227,6 +2275,7 @@ static int t4_set_linemode(struct dahdi_span *span, enum spantypes linemode)
enum linemode mode;
const char *old_name;
static DEFINE_MUTEX(linemode_lock);
unsigned long flags;
dev_dbg(&wc->dev->dev, "Setting '%s' to '%s'\n", span->name,
dahdi_spantype2str(linemode));
@@ -2234,6 +2283,10 @@ static int t4_set_linemode(struct dahdi_span *span, enum spantypes linemode)
if (span->spantype == linemode)
return 0;
spin_lock_irqsave(&wc->reglock, flags);
wc->reset_required = 1;
spin_unlock_irqrestore(&wc->reglock, flags);
/* Do not allow the t1e1 member to be changed by multiple threads. */
mutex_lock(&linemode_lock);
old_name = dahdi_spantype2str(span->spantype);
@@ -2830,6 +2883,10 @@ static void __t4_configure_e1(struct t4 *wc, int unit, int lineconfig)
__t4_framer_out(wc, unit, 0x17, 0x04 | imr3extra); /* IMR3: AIS */
__t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: We care about slips on transmit */
__t4_framer_out(wc, unit, 0x2f, 0x00);
__t4_framer_out(wc, unit, 0x30, 0x00);
__t4_framer_out(wc, unit, 0x31, 0x00);
dev_info(&wc->dev->dev, "TE%dXXP: Span %d configured for %s/%s%s\n",
wc->numspans, unit + 1, framing, line, crc4);
}
@@ -4306,7 +4363,7 @@ static void t4_vpm_init(struct t4 *wc)
laws[x] = 1;
}
vpm_capacity = get_vpm450m_capacity(wc);
vpm_capacity = get_vpm450m_capacity(&wc->dev->dev);
if (vpm_capacity != wc->numspans * 32) {
dev_info(&wc->dev->dev, "Disabling VPMOCT%03d. TE%dXXP"\
" requires a VPMOCT%03d", vpm_capacity,
@@ -4378,7 +4435,8 @@ static void t4_vpm_init(struct t4 *wc)
return;
}
if (!(wc->vpm = init_vpm450m(wc, laws, wc->numspans, firmware))) {
wc->vpm = init_vpm450m(&wc->dev->dev, laws, wc->numspans, firmware);
if (!wc->vpm) {
dev_notice(&wc->dev->dev, "VPM450: Failed to initialize\n");
if (firmware != &embedded_firmware)
release_firmware(firmware);
@@ -4865,17 +4923,18 @@ cleanup:
return res;
}
static int t4_hardware_init_1(struct t4 *wc, unsigned int cardflags)
static int
__t4_hardware_init_1(struct t4 *wc, unsigned int cardflags, bool first_time)
{
unsigned int version;
int res;
version = t4_pci_in(wc, WC_VERSION);
if (is_octal(wc)) {
if (is_octal(wc) && first_time) {
dev_info(&wc->dev->dev, "Firmware Version: %01x.%02x\n",
(version & 0xf00) >> 8,
version & 0xff);
} else {
} else if (first_time) {
dev_info(&wc->dev->dev, "Firmware Version: %08x\n", version);
}
if (debug) {
@@ -4923,30 +4982,33 @@ static int t4_hardware_init_1(struct t4 *wc, unsigned int cardflags)
t4_pci_out(wc, WC_INTR, 0x00000000);
/* Read T1/E1 status */
if (!strcasecmp("auto", default_linemode)) {
if (-1 == t1e1override) {
wc->t1e1 = (((t4_pci_in(wc, WC_LEDS)) & 0x0f00) >> 8);
wc->t1e1 &= 0xf;
if (is_octal(wc)) {
wc->t1e1 |= ((t4_pci_in(wc, WC_LEDS2)) &
0x0f00) >> 4;
if (first_time) {
if (!strcasecmp("auto", default_linemode)) {
if (-1 == t1e1override) {
wc->t1e1 = (((t4_pci_in(wc, WC_LEDS)) &
0x0f00) >> 8);
wc->t1e1 &= 0xf;
if (is_octal(wc)) {
wc->t1e1 |= ((t4_pci_in(wc, WC_LEDS2)) &
0x0f00) >> 4;
}
} else {
dev_warn(&wc->dev->dev,
"'t1e1override' is deprecated. Please use 'default_linemode'.\n");
wc->t1e1 = t1e1override & 0xf;
}
} else if (!strcasecmp("t1", default_linemode)) {
wc->t1e1 = 0;
} else if (!strcasecmp("e1", default_linemode)) {
wc->t1e1 = 0xff;
} else if (!strcasecmp("j1", default_linemode)) {
wc->t1e1 = 0;
j1mode = 1;
} else {
dev_warn(&wc->dev->dev, "'t1e1override' is deprecated. "
"Please use 'default_linemode'.\n");
wc->t1e1 = t1e1override & 0xf;
dev_err(&wc->dev->dev, "'%s' is an unknown linemode.\n",
default_linemode);
wc->t1e1 = 0;
}
} else if (!strcasecmp("t1", default_linemode)) {
wc->t1e1 = 0;
} else if (!strcasecmp("e1", default_linemode)) {
wc->t1e1 = 0xff;
} else if (!strcasecmp("j1", default_linemode)) {
wc->t1e1 = 0;
j1mode = 1;
} else {
dev_err(&wc->dev->dev, "'%s' is an unknown linemode.\n",
default_linemode);
wc->t1e1 = 0;
}
wc->order = ((t4_pci_in(wc, WC_LEDS)) & 0xf0000000) >> 28;
@@ -4975,7 +5037,12 @@ static int t4_hardware_init_1(struct t4 *wc, unsigned int cardflags)
return 0;
}
static int t4_hardware_init_2(struct t4 *wc)
static int t4_hardware_init_1(struct t4 *wc, unsigned int cardflags)
{
return __t4_hardware_init_1(wc, cardflags, true);
}
static int __t4_hardware_init_2(struct t4 *wc, bool first_time)
{
int x;
unsigned int regval;
@@ -5007,13 +5074,14 @@ static int t4_hardware_init_2(struct t4 *wc)
if (!is_octal(wc)) {
regval = t4_framer_in(wc, 0, 0x4a);
if (regval == 0x05) {
if (first_time && regval == 0x05) {
dev_info(&wc->dev->dev, "FALC Framer Version: 2.1 or "
"earlier\n");
} else if (regval == 0x20) {
dev_info(&wc->dev->dev, "FALC Framer Version: 3.1\n");
if (first_time)
dev_info(&wc->dev->dev, "FALC Framer Version: 3.1\n");
wc->falc31 = 1;
} else {
} else if (first_time) {
dev_info(&wc->dev->dev, "FALC Framer Version: Unknown "
"(VSTR = 0x%02x)\n", regval);
}
@@ -5033,14 +5101,24 @@ static int t4_hardware_init_2(struct t4 *wc)
t4_pci_in(wc, x));
}
}
wc->gpio = 0x00000000;
t4_pci_out(wc, WC_GPIO, wc->gpio);
t4_gpio_setdir(wc, (1 << 17), (1 << 17));
t4_gpio_setdir(wc, (0xff), (0xff));
return 0;
}
static int t4_hardware_init_2(struct t4 *wc)
{
return __t4_hardware_init_2(wc, true);
}
static int __devinit t4_launch(struct t4 *wc)
{
int x;
int res;
unsigned long flags;
if (test_bit(DAHDI_FLAGBIT_REGISTERED, &wc->tspans[0]->span.flags))
return 0;
@@ -5072,17 +5150,14 @@ static int __devinit t4_launch(struct t4 *wc)
&wc->ddev->spans);
}
tasklet_init(&wc->t4_tlet, t4_isr_bh, (unsigned long)wc);
res = dahdi_register_device(wc->ddev, &wc->dev->dev);
if (res) {
dev_err(&wc->dev->dev, "Failed to register with DAHDI.\n");
return res;
}
set_bit(T4_CHECK_TIMING, &wc->checkflag);
spin_lock_irqsave(&wc->reglock, flags);
__t4_set_sclk_src(wc, WC_SELF, 0, 0);
spin_unlock_irqrestore(&wc->reglock, flags);
tasklet_init(&wc->t4_tlet, t4_isr_bh, (unsigned long)wc);
return 0;
}
@@ -5296,10 +5371,6 @@ t4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_info(&wc->dev->dev,
"Found a Wildcard: %s\n", wc->devtype->desc);
}
wc->gpio = 0x00000000;
t4_pci_out(wc, WC_GPIO, wc->gpio);
t4_gpio_setdir(wc, (1 << 17), (1 << 17));
t4_gpio_setdir(wc, (0xff), (0xff));
#ifdef VPM_SUPPORT
if (!wc->vpm) {

View File

@@ -31,99 +31,62 @@
#include <stdbool.h>
#include "vpm450m.h"
#include "oct6100api/oct6100_api.h"
#include <oct612x.h>
/* API for Octasic access */
UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime)
static int wct4xxp_oct612x_write(struct oct612x_context *context,
u32 address, u16 value)
{
/* Why couldn't they just take a timeval like everyone else? */
struct timeval tv;
unsigned long long total_usecs;
unsigned int mask = ~0;
do_gettimeofday(&tv);
total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) +
(((unsigned long long)(tv.tv_usec)));
f_pTime->aulWallTimeUs[0] = (total_usecs & mask);
f_pTime->aulWallTimeUs[1] = (total_usecs >> 32);
return cOCT6100_ERR_OK;
struct t4 *wc = dev_get_drvdata(context->dev);
oct_set_reg(wc, address, value);
return 0;
}
UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength)
static int wct4xxp_oct612x_read(struct oct612x_context *context, u32 address,
u16 *value)
{
memset(f_pAddress, f_ulPattern, f_ulLength);
return cOCT6100_ERR_OK;
struct t4 *wc = dev_get_drvdata(context->dev);
*value = (u16)oct_get_reg(wc, address);
return 0;
}
UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength)
static int wct4xxp_oct612x_write_smear(struct oct612x_context *context,
u32 address, u16 value, size_t count)
{
memcpy(f_pDestination, f_pSource, f_ulLength);
return cOCT6100_ERR_OK;
struct t4 *wc = dev_get_drvdata(context->dev);
int i;
for (i = 0; i < count; ++i)
oct_set_reg(wc, address + (i << 1), value);
return 0;
}
UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate)
static int wct4xxp_oct612x_write_burst(struct oct612x_context *context,
u32 address, const u16 *buffer,
size_t count)
{
return cOCT6100_ERR_OK;
struct t4 *wc = dev_get_drvdata(context->dev);
int i;
for (i = 0; i < count; ++i)
oct_set_reg(wc, address + (i << 1), buffer[i]);
return 0;
}
UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy)
static int wct4xxp_oct612x_read_burst(struct oct612x_context *context,
u32 address, u16 *buffer, size_t count)
{
#ifdef OCTASIC_DEBUG
printk(KERN_DEBUG "I should never be called! (destroy serialize object)\n");
#endif
return cOCT6100_ERR_OK;
struct t4 *wc = dev_get_drvdata(context->dev);
int i;
for (i = 0; i < count; ++i)
buffer[i] = oct_get_reg(wc, address + (i << 1));
return 0;
}
UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize)
{
/* Not needed */
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease)
{
/* Not needed */
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams)
{
oct_set_reg(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData);
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams)
{
unsigned int x;
for (x=0;x<f_pSmearParams->ulWriteLength;x++) {
oct_set_reg(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData);
}
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams)
{
unsigned int x;
for (x=0;x<f_pBurstParams->ulWriteLength;x++) {
oct_set_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]);
}
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams)
{
*(f_pReadParams->pusReadData) = oct_get_reg(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress);
return cOCT6100_ERR_OK;
}
UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams)
{
unsigned int x;
for (x=0;x<f_pBurstParams->ulReadLength;x++) {
f_pBurstParams->pusReadData[x] = oct_get_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1));
}
return cOCT6100_ERR_OK;
}
static const struct oct612x_ops wct4xxp_oct612x_ops = {
.write = wct4xxp_oct612x_write,
.read = wct4xxp_oct612x_read,
.write_smear = wct4xxp_oct612x_write_smear,
.write_burst = wct4xxp_oct612x_write_burst,
.read_burst = wct4xxp_oct612x_read_burst,
};
#define SOUT_G168_1100GB_ON 0x40000004
#define SOUT_DTMF_1 0x40000011
@@ -173,6 +136,7 @@ UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams)
struct vpm450m {
tPOCT6100_INSTANCE_API pApiInstance;
struct oct612x_context context;
UINT32 aulEchoChanHndl[256];
int chanflags[256];
int ecmode[256];
@@ -256,8 +220,10 @@ void vpm450m_set_alaw_companding(struct vpm450m *vpm450m, int channel,
pr_notice("Failed to apply echo can changes on channel %d %d %08x!\n",
vpm450m->aulEchoChanHndl[channel], channel, ulResult);
} else {
pr_info("Changed companding on channel %d to %s.\n", channel,
(alaw) ? "alaw" : "ulaw");
if (debug) {
pr_info("Changed companding on channel %d to %s.\n",
channel, (alaw) ? "alaw" : "ulaw");
}
if (alaw)
vpm450m->chanflags[channel] |= FLAG_ALAW;
else
@@ -451,14 +417,18 @@ int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start
return 0;
}
unsigned int get_vpm450m_capacity(void *wc)
unsigned int get_vpm450m_capacity(struct device *device)
{
struct oct612x_context context;
UINT32 ulResult;
tOCT6100_API_GET_CAPACITY_PINS CapacityPins;
context.dev = device;
context.ops = &wct4xxp_oct612x_ops;
Oct6100ApiGetCapacityPinsDef(&CapacityPins);
CapacityPins.pProcessContext = wc;
CapacityPins.pProcessContext = &context;
CapacityPins.ulMemoryType = cOCT6100_MEM_TYPE_DDR;
CapacityPins.fEnableMemClkOut = TRUE;
CapacityPins.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ;
@@ -472,7 +442,8 @@ unsigned int get_vpm450m_capacity(void *wc)
return CapacityPins.ulCapacityValue;
}
struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct firmware *firmware)
struct vpm450m *init_vpm450m(struct device *device, int *isalaw,
int numspans, const struct firmware *firmware)
{
tOCT6100_CHIP_OPEN *ChipOpen;
tOCT6100_GET_INSTANCE_SIZE InstanceSize;
@@ -487,6 +458,8 @@ struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct f
return NULL;
memset(vpm450m, 0, sizeof(struct vpm450m));
vpm450m->context.dev = device;
vpm450m->context.ops = &wct4xxp_oct612x_ops;
if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) {
kfree(vpm450m);
@@ -515,7 +488,7 @@ struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct f
ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ;
Oct6100GetInstanceSizeDef(&InstanceSize);
ChipOpen->pProcessContext = wc;
ChipOpen->pProcessContext = &vpm450m->context;
ChipOpen->pbyImageFile = firmware->data;
ChipOpen->ulImageSize = firmware->size;

View File

@@ -25,6 +25,7 @@
#include <linux/firmware.h>
struct t4;
struct vpm450m;
/* From driver */
@@ -32,8 +33,9 @@ unsigned int oct_get_reg(void *data, unsigned int reg);
void oct_set_reg(void *data, unsigned int reg, unsigned int val);
/* From vpm450m */
struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct firmware *firmware);
unsigned int get_vpm450m_capacity(void *wc);
struct vpm450m *init_vpm450m(struct device *device, int *isalaw,
int numspans, const struct firmware *firmware);
unsigned int get_vpm450m_capacity(struct device *device);
void vpm450m_setec(struct vpm450m *instance, int channel, int eclen);
void vpm450m_setdtmf(struct vpm450m *instance, int channel, int dtmfdetect, int dtmfmute);
int vpm450m_checkirq(struct vpm450m *vpm450m);
@@ -42,4 +44,6 @@ void release_vpm450m(struct vpm450m *instance);
void vpm450m_set_alaw_companding(struct vpm450m *vpm450m,
int channel, bool alaw);
extern int debug;
#endif

File diff suppressed because it is too large Load Diff

3591
drivers/dahdi/wcte43x-base.c Normal file

File diff suppressed because it is too large Load Diff

947
drivers/dahdi/wcxb.c Normal file
View File

@@ -0,0 +1,947 @@
/*
* wcxb SPI library
*
* Copyright (C) 2013 Digium, Inc.
*
* All rights reserved.
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 26)
#define HAVE_RATELIMIT
#include <linux/ratelimit.h>
#endif
#include <dahdi/kernel.h>
#include <stdbool.h>
#include "wcxb.h"
#include "wcxb_spi.h"
#include "wcxb_flash.h"
/* The definition for Surprise Down was added in Linux 3.6 in (a0dee2e PCI: misc
* pci_reg additions). It may be backported though so we won't check for the
* version. */
#ifndef PCI_ERR_UNC_SURPDN
#define PCI_ERR_UNC_SURPDN 0x20
#endif
/* FPGA Status definitions */
#define OCT_CPU_RESET (1 << 0)
#define OCT_CPU_DRAM_CKE (1 << 1)
#define STATUS_LED_GREEN (1 << 9)
#define STATUS_LED_RED (1 << 10)
#define FALC_CPU_RESET (1 << 11)
/* Descriptor ring definitions */
#define DRING_SIZE (1 << 7) /* Must be in multiples of 2 */
#define DRING_SIZE_MASK (DRING_SIZE-1)
#define DESC_EOR (1 << 0)
#define DESC_INT (1 << 1)
#define DESC_OWN (1 << 31)
#define DESC_DEFAULT_STATUS 0xdeadbeef
#define DMA_CHAN_SIZE 128
/* Echocan definitions */
#define OCT_OFFSET (xb->membase + 0x10000)
#define OCT_CONTROL_REG (OCT_OFFSET + 0)
#define OCT_DATA_REG (OCT_OFFSET + 0x4)
#define OCT_ADDRESS_HIGH_REG (OCT_OFFSET + 0x8)
#define OCT_ADDRESS_LOW_REG (OCT_OFFSET + 0xa)
#define OCT_DIRECT_WRITE_MASK 0x3001
#define OCT_INDIRECT_READ_MASK 0x0101
#define OCT_INDIRECT_WRITE_MASK 0x3101
/* DMA definitions */
#define TDM_DRING_ADDR 0x2000
#define TDM_CONTROL (TDM_DRING_ADDR + 0x4)
#define ENABLE_ECHOCAN_TDM (1 << 0)
#define TDM_RECOVER_CLOCK (1 << 1)
#define ENABLE_DMA (1 << 2)
#define DMA_RUNNING (1 << 3)
#define DMA_LOOPBACK (1 << 4)
#define AUTHENTICATED (1 << 5)
#define TDM_VERSION (TDM_DRING_ADDR + 0x24)
/* Interrupt definitions */
#define INTERRUPT_CONTROL 0x300
#define ISR (INTERRUPT_CONTROL + 0x0)
#define IPR (INTERRUPT_CONTROL + 0x4)
#define IER (INTERRUPT_CONTROL + 0x8)
#define IAR (INTERRUPT_CONTROL + 0xc)
#define SIE (INTERRUPT_CONTROL + 0x10)
#define CIE (INTERRUPT_CONTROL + 0x14)
#define IVR (INTERRUPT_CONTROL + 0x18)
#define MER (INTERRUPT_CONTROL + 0x1c)
#define MER_ME (1<<0)
#define MER_HIE (1<<1)
#define DESC_UNDERRUN (1<<0)
#define DESC_COMPLETE (1<<1)
#define OCT_INT (1<<2)
#define FALC_INT (1<<3)
#define SPI_INT (1<<4)
#define FLASH_SPI_BASE 0x200
struct wcxb_hw_desc {
volatile __be32 status;
__be32 tx_buf;
__be32 rx_buf;
volatile __be32 control;
} __packed;
struct wcxb_meta_desc {
void *tx_buf_virt;
void *rx_buf_virt;
};
static inline bool wcxb_is_pcie(const struct wcxb *xb)
{
#ifndef WCXB_PCI_DEV_DOES_NOT_HAVE_IS_PCIE
return (xb->pdev->is_pcie > 0);
#else
return (xb->flags.is_pcie > 0);
#endif
}
static const unsigned int CLK_SRC_MASK = ((1 << 13) | (1 << 12) | (1 << 1));
enum wcxb_clock_sources wcxb_get_clksrc(struct wcxb *xb)
{
static const u32 SELF = 0x0;
static const u32 RECOVER = (1 << 1);
static const u32 SLAVE = (1 << 12) | (1 << 1);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&xb->lock, flags);
reg = ioread32be(xb->membase + TDM_CONTROL) & CLK_SRC_MASK;
spin_unlock_irqrestore(&xb->lock, flags);
if (SELF == reg)
return WCXB_CLOCK_SELF;
else if (RECOVER == reg)
return WCXB_CLOCK_RECOVER;
else if (SLAVE == reg)
return WCXB_CLOCK_SLAVE;
else
WARN_ON(1);
return WCXB_CLOCK_SELF;
}
void wcxb_set_clksrc(struct wcxb *xb, enum wcxb_clock_sources clksrc)
{
unsigned long flags;
u32 clkbits = 0;
switch (clksrc) {
case WCXB_CLOCK_RECOVER:
if (xb->flags.drive_timing_cable)
clkbits = (1<<13) | (1 << 1);
else
clkbits = (1 << 1);
break;
case WCXB_CLOCK_SELF:
if (xb->flags.drive_timing_cable)
clkbits = (1<<13);
else
clkbits = 0;
break;
case WCXB_CLOCK_SLAVE:
/* When we're slave, do not ever drive the timing cable. */
clkbits = (1<<12) | (1 << 1);
break;
};
/* set new clock select */
spin_lock_irqsave(&xb->lock, flags);
if (!wcxb_is_stopped(xb)) {
dev_err(&xb->pdev->dev, "ERROR: Cannot set clock source while DMA engine is running.\n");
} else {
u32 reg;
reg = ioread32be(xb->membase + TDM_CONTROL);
reg &= ~CLK_SRC_MASK;
reg |= (clkbits & CLK_SRC_MASK);
iowrite32be(reg, xb->membase + TDM_CONTROL);
}
spin_unlock_irqrestore(&xb->lock, flags);
}
void wcxb_enable_echocan(struct wcxb *xb)
{
u32 reg;
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
reg = ioread32be(xb->membase + TDM_CONTROL);
reg |= ENABLE_ECHOCAN_TDM;
iowrite32be(reg, xb->membase + TDM_CONTROL);
spin_unlock_irqrestore(&xb->lock, flags);
}
void wcxb_disable_echocan(struct wcxb *xb)
{
u32 reg;
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
reg = ioread32be(xb->membase + TDM_CONTROL);
reg &= ~ENABLE_ECHOCAN_TDM;
iowrite32be(reg, xb->membase + TDM_CONTROL);
spin_unlock_irqrestore(&xb->lock, flags);
}
void wcxb_reset_echocan(struct wcxb *xb)
{
unsigned long flags;
int reg;
spin_lock_irqsave(&xb->lock, flags);
reg = ioread32be(xb->membase);
iowrite32be((reg & ~OCT_CPU_RESET), xb->membase);
spin_unlock_irqrestore(&xb->lock, flags);
msleep_interruptible(1);
spin_lock_irqsave(&xb->lock, flags);
reg = ioread32be(xb->membase);
iowrite32be((reg | OCT_CPU_RESET), xb->membase);
spin_unlock_irqrestore(&xb->lock, flags);
dev_dbg(&xb->pdev->dev, "Reset octasic\n");
}
bool wcxb_is_echocan_present(struct wcxb *xb)
{
return 0x1 == ioread16be(OCT_CONTROL_REG);
}
void wcxb_enable_echocan_dram(struct wcxb *xb)
{
unsigned long flags;
int reg;
spin_lock_irqsave(&xb->lock, flags);
reg = ioread32be(xb->membase);
iowrite32be((reg | OCT_CPU_DRAM_CKE), xb->membase);
spin_unlock_irqrestore(&xb->lock, flags);
}
u16 wcxb_get_echocan_reg(struct wcxb *xb, u32 address)
{
uint16_t highaddress = ((address >> 20) & 0xfff);
uint16_t lowaddress = ((address >> 4) & 0xfffff);
unsigned long stop = jiffies + HZ/10;
unsigned long flags;
u16 ret;
spin_lock_irqsave(&xb->lock, flags);
iowrite16be(highaddress, OCT_ADDRESS_HIGH_REG);
iowrite16be(lowaddress, OCT_ADDRESS_LOW_REG);
iowrite16be(OCT_INDIRECT_READ_MASK | ((address & 0xe) << 8),
OCT_CONTROL_REG);
do {
ret = ioread16be(OCT_CONTROL_REG);
} while ((ret & (1<<8)) && time_before(jiffies, stop));
WARN_ON_ONCE(time_after_eq(jiffies, stop));
ret = ioread16be(OCT_DATA_REG);
spin_unlock_irqrestore(&xb->lock, flags);
return ret;
}
void wcxb_set_echocan_reg(struct wcxb *xb, u32 address, u16 val)
{
unsigned long flags;
uint16_t ret;
uint16_t highaddress = ((address >> 20) & 0xfff);
uint16_t lowaddress = ((address >> 4) & 0xffff);
unsigned long stop = jiffies + HZ/10;
spin_lock_irqsave(&xb->lock, flags);
iowrite16be(highaddress, OCT_ADDRESS_HIGH_REG);
iowrite16be(lowaddress, OCT_ADDRESS_LOW_REG);
iowrite16be(val, OCT_DATA_REG);
iowrite16be(OCT_INDIRECT_WRITE_MASK | ((address & 0xe) << 8),
OCT_CONTROL_REG);
/* No write should take longer than 100ms */
do {
ret = ioread16be(OCT_CONTROL_REG);
} while ((ret & (1<<8)) && time_before(jiffies, stop));
spin_unlock_irqrestore(&xb->lock, flags);
WARN_ON_ONCE(time_after_eq(jiffies, stop));
}
#ifdef HAVE_RATELIMIT
static DEFINE_RATELIMIT_STATE(_underrun_rl, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
#endif
/* wcxb_reset_dring needs to be called with xb->lock held. */
static void _wcxb_reset_dring(struct wcxb *xb)
{
int x;
struct wcxb_meta_desc *mdesc;
struct wcxb_hw_desc *hdesc = NULL;
xb->dma_head = xb->dma_tail = 0;
if (unlikely(xb->latency > DRING_SIZE)) {
#ifdef HAVE_RATELIMIT
if (__ratelimit(&_underrun_rl)) {
#else
if (printk_ratelimit()) {
#endif
dev_info(&xb->pdev->dev,
"Oops! Tried to increase latency past buffer size.\n");
}
xb->latency = DRING_SIZE;
}
for (x = 0; x < xb->latency; x++) {
dma_addr_t dma_tmp;
mdesc = &xb->meta_dring[x];
hdesc = &xb->hw_dring[x];
hdesc->status = cpu_to_be32(DESC_DEFAULT_STATUS);
if (!mdesc->tx_buf_virt) {
mdesc->tx_buf_virt =
dma_pool_alloc(xb->pool, GFP_ATOMIC, &dma_tmp);
hdesc->tx_buf = cpu_to_be32(dma_tmp);
mdesc->rx_buf_virt =
dma_pool_alloc(xb->pool, GFP_ATOMIC, &dma_tmp);
hdesc->rx_buf = cpu_to_be32(dma_tmp);
}
hdesc->control = cpu_to_be32(DESC_INT|DESC_OWN);
BUG_ON(!mdesc->tx_buf_virt || !mdesc->rx_buf_virt);
}
BUG_ON(!hdesc);
/* Set end of ring bit in last descriptor to force hw to loop around */
hdesc->control |= cpu_to_be32(DESC_EOR);
iowrite32be(xb->hw_dring_phys, xb->membase + TDM_DRING_ADDR);
}
static void wcxb_handle_dma(struct wcxb *xb)
{
struct wcxb_meta_desc *mdesc;
while (!(xb->hw_dring[xb->dma_tail].control & cpu_to_be32(DESC_OWN))) {
u_char *frame;
mdesc = &xb->meta_dring[xb->dma_tail];
frame = mdesc->rx_buf_virt;
xb->ops->handle_receive(xb, frame);
xb->dma_tail =
(xb->dma_tail == xb->latency-1) ? 0 : xb->dma_tail + 1;
mdesc = &xb->meta_dring[xb->dma_head];
frame = mdesc->tx_buf_virt;
xb->ops->handle_transmit(xb, frame);
wmb();
xb->hw_dring[xb->dma_head].control |= cpu_to_be32(DESC_OWN);
xb->dma_head =
(xb->dma_head == xb->latency-1) ? 0 : xb->dma_head + 1;
}
}
static irqreturn_t _wcxb_isr(int irq, void *dev_id)
{
struct wcxb *xb = dev_id;
unsigned int limit = 8;
u32 pending;
pending = ioread32be(xb->membase + ISR);
if (!pending)
return IRQ_NONE;
do {
iowrite32be(pending, xb->membase + IAR);
if (pending & DESC_UNDERRUN) {
u32 reg;
/* bump latency */
spin_lock(&xb->lock);
if (!xb->flags.latency_locked) {
xb->latency++;
#ifdef HAVE_RATELIMIT
if (__ratelimit(&_underrun_rl)) {
#else
if (printk_ratelimit()) {
#endif
dev_info(&xb->pdev->dev,
"Underrun detected by hardware. Latency bumped to: %dms\n",
xb->latency);
}
}
/* re-setup dma ring */
_wcxb_reset_dring(xb);
/* set dma enable bit */
reg = ioread32be(xb->membase + TDM_CONTROL);
reg |= ENABLE_DMA;
iowrite32be(reg, xb->membase + TDM_CONTROL);
spin_unlock(&xb->lock);
}
if (pending & DESC_COMPLETE) {
xb->framecount++;
wcxb_handle_dma(xb);
}
if (NULL != xb->ops->handle_interrupt)
xb->ops->handle_interrupt(xb, pending);
pending = ioread32be(xb->membase + ISR);
} while (pending && --limit);
return IRQ_HANDLED;
}
DAHDI_IRQ_HANDLER(wcxb_isr)
{
irqreturn_t ret;
unsigned long flags;
local_irq_save(flags);
ret = _wcxb_isr(irq, dev_id);
local_irq_restore(flags);
return ret;
}
static int wcxb_alloc_dring(struct wcxb *xb, const char *board_name)
{
xb->meta_dring =
kzalloc(sizeof(struct wcxb_meta_desc) * DRING_SIZE,
GFP_KERNEL);
if (!xb->meta_dring)
return -ENOMEM;
xb->hw_dring = dma_alloc_coherent(&xb->pdev->dev,
sizeof(struct wcxb_hw_desc) * DRING_SIZE,
&xb->hw_dring_phys,
GFP_KERNEL);
if (!xb->hw_dring) {
kfree(xb->meta_dring);
return -ENOMEM;
}
xb->pool = dma_pool_create(board_name, &xb->pdev->dev,
PAGE_SIZE, PAGE_SIZE, 0);
if (!xb->pool) {
kfree(xb->meta_dring);
dma_free_coherent(&xb->pdev->dev,
sizeof(struct wcxb_hw_desc) * DRING_SIZE,
xb->hw_dring,
xb->hw_dring_phys);
return -ENOMEM;
}
return 0;
}
/**
* wcxb_soft_reset - Set interface registers back to known good values.
*
* This represents the normal default state after a reset of the FPGA. This
* function is preferred over the hard reset function.
*
*/
static void wcxb_soft_reset(struct wcxb *xb)
{
/* digium_gpo */
iowrite32be(0x0, xb->membase);
/* xps_intc */
iowrite32be(0x0, xb->membase + 0x300);
iowrite32be(0x0, xb->membase + 0x308);
iowrite32be(0x0, xb->membase + 0x310);
iowrite32be(0x0, xb->membase + 0x31C);
/* xps_spi_config_flash */
iowrite32be(0xA, xb->membase + 0x200);
/* tdm engine */
iowrite32be(0x0, xb->membase + 0x2000);
iowrite32be(0x0, xb->membase + 0x2004);
}
static void _wcxb_hard_reset(struct wcxb *xb)
{
struct pci_dev *const pdev = xb->pdev;
u32 microblaze_version;
unsigned long stop_time = jiffies + msecs_to_jiffies(2000);
pci_save_state(pdev);
iowrite32be(0xe00, xb->membase + TDM_CONTROL);
/* This sleep is to give FPGA time to bring up the PCI/PCIe interface */
msleep(200);
pci_restore_state(pdev);
/* Wait for the Microblaze CPU to complete it's startup */
do {
msleep(20);
/* Can return either 0xffff or 0 before it's fully booted */
microblaze_version = ioread32be(xb->membase + 0x2018) ?: 0xffff;
} while (time_before(jiffies, stop_time)
&& 0xffff == microblaze_version);
}
/*
* Since the FPGA hard reset drops the PCIe link we need to disable
* error reporting on the upsteam link. Otherwise Surprise Down errors
* may be reported in reponse to the link going away.
*
* NOTE: We cannot use pci_disable_pcie_error_reporting() because it will not
* disable error reporting if the system firmware is attached to the advanced
* error reporting mechanism.
*/
static void _wcxb_pcie_hard_reset(struct wcxb *xb)
{
struct pci_dev *const parent = xb->pdev->bus->self;
u32 aer_mask;
int pos;
if (!wcxb_is_pcie(xb))
return;
pos = pci_find_ext_capability(parent, PCI_EXT_CAP_ID_ERR);
if (pos) {
pci_read_config_dword(parent, pos + PCI_ERR_UNCOR_MASK,
&aer_mask);
pci_write_config_dword(parent, pos + PCI_ERR_UNCOR_MASK,
aer_mask | PCI_ERR_UNC_SURPDN);
}
_wcxb_hard_reset(xb);
if (pos) {
pci_write_config_dword(parent, pos + PCI_ERR_UNCOR_MASK,
aer_mask);
/* Clear the error as well from the status register. */
pci_write_config_dword(parent, pos + PCI_ERR_UNCOR_STATUS,
PCI_ERR_UNC_SURPDN);
}
return;
}
/**
* wcxb_hard_reset - Reset FPGA and reload firmware.
*
* This may be called in the context of device probe and therefore the PCI
* device may be locked.
*
*/
static void wcxb_hard_reset(struct wcxb *xb)
{
if (wcxb_is_pcie(xb))
_wcxb_pcie_hard_reset(xb);
else
_wcxb_hard_reset(xb);
}
int wcxb_init(struct wcxb *xb, const char *board_name, u32 int_mode)
{
int res = 0;
struct pci_dev *pdev = xb->pdev;
u32 tdm_control;
if (pci_enable_device(pdev))
return -EIO;
pci_set_master(pdev);
#ifdef WCXB_PCI_DEV_DOES_NOT_HAVE_IS_PCIE
xb->flags.is_pcie = pci_find_capability(pdev, PCI_CAP_ID_EXP) ? 1 : 0;
#endif
WARN_ON(!pdev);
if (!pdev)
return -EINVAL;
xb->latency = WCXB_DEFAULT_LATENCY;
spin_lock_init(&xb->lock);
xb->membase = pci_iomap(pdev, 0, 0);
if (pci_request_regions(pdev, board_name))
dev_info(&xb->pdev->dev, "Unable to request regions\n");
wcxb_soft_reset(xb);
res = wcxb_alloc_dring(xb, board_name);
if (res) {
dev_err(&xb->pdev->dev,
"Failed to allocate descriptor rings.\n");
goto fail_exit;
}
/* Enable writes to fpga status register */
iowrite32be(0, xb->membase + 0x04);
xb->flags.have_msi = (int_mode) ? 0 : (0 == pci_enable_msi(pdev));
if (request_irq(pdev->irq, wcxb_isr,
(xb->flags.have_msi) ? 0 : DAHDI_IRQ_SHARED,
board_name, xb)) {
dev_notice(&xb->pdev->dev, "Unable to request IRQ %d\n",
pdev->irq);
res = -EIO;
goto fail_exit;
}
iowrite32be(0, xb->membase + TDM_CONTROL);
tdm_control = ioread32be(xb->membase + TDM_CONTROL);
if (!(tdm_control & 0x20)) {
dev_err(&xb->pdev->dev,
"This board is not authenticated and may not function properly.\n");
msleep(1000);
} else {
dev_dbg(&xb->pdev->dev, "Authenticated. %08x\n", tdm_control);
}
return res;
fail_exit:
pci_release_regions(xb->pdev);
return res;
}
void wcxb_stop_dma(struct wcxb *xb)
{
unsigned long flags;
u32 reg;
/* Quiesce DMA engine interrupts */
spin_lock_irqsave(&xb->lock, flags);
reg = ioread32be(xb->membase + TDM_CONTROL);
reg &= ~ENABLE_DMA;
iowrite32be(reg, xb->membase + TDM_CONTROL);
spin_unlock_irqrestore(&xb->lock, flags);
}
int wcxb_wait_for_stop(struct wcxb *xb, unsigned long timeout_ms)
{
unsigned long stop;
stop = jiffies + msecs_to_jiffies(timeout_ms);
do {
if (time_after(jiffies, stop))
return -EIO;
else
cpu_relax();
} while (!wcxb_is_stopped(xb));
return 0;
}
void wcxb_disable_interrupts(struct wcxb *xb)
{
iowrite32be(0, xb->membase + IER);
}
void wcxb_stop(struct wcxb *xb)
{
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
/* Stop everything */
iowrite32be(0, xb->membase + TDM_CONTROL);
iowrite32be(0, xb->membase + IER);
iowrite32be(0, xb->membase + MER);
iowrite32be(-1, xb->membase + IAR);
/* Flush quiesce commands before exit */
ioread32be(xb->membase);
spin_unlock_irqrestore(&xb->lock, flags);
synchronize_irq(xb->pdev->irq);
}
bool wcxb_is_stopped(struct wcxb *xb)
{
return !(ioread32be(xb->membase + TDM_CONTROL) & DMA_RUNNING);
}
static void wcxb_free_dring(struct wcxb *xb)
{
struct wcxb_meta_desc *mdesc;
struct wcxb_hw_desc *hdesc;
int i;
/* Free tx/rx buffs */
for (i = 0; i < DRING_SIZE; i++) {
mdesc = &xb->meta_dring[i];
hdesc = &xb->hw_dring[i];
if (mdesc->tx_buf_virt) {
dma_pool_free(xb->pool,
mdesc->tx_buf_virt,
be32_to_cpu(hdesc->tx_buf));
dma_pool_free(xb->pool,
mdesc->rx_buf_virt,
be32_to_cpu(hdesc->rx_buf));
}
}
dma_pool_destroy(xb->pool);
dma_free_coherent(&xb->pdev->dev,
sizeof(struct wcxb_hw_desc) * DRING_SIZE,
xb->hw_dring,
xb->hw_dring_phys);
kfree(xb->meta_dring);
}
void wcxb_release(struct wcxb *xb)
{
wcxb_stop(xb);
synchronize_irq(xb->pdev->irq);
free_irq(xb->pdev->irq, xb);
if (xb->flags.have_msi)
pci_disable_msi(xb->pdev);
if (xb->membase)
pci_iounmap(xb->pdev, xb->membase);
wcxb_free_dring(xb);
pci_release_regions(xb->pdev);
pci_disable_device(xb->pdev);
return;
}
int wcxb_start(struct wcxb *xb)
{
u32 reg;
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
_wcxb_reset_dring(xb);
/* Enable hardware interrupts */
iowrite32be(-1, xb->membase + IAR);
iowrite32be(DESC_UNDERRUN|DESC_COMPLETE, xb->membase + IER);
/* iowrite32be(0x3f7, xb->membase + IER); */
iowrite32be(MER_ME|MER_HIE, xb->membase + MER);
/* Start the DMA engine processing. */
reg = ioread32be(xb->membase + TDM_CONTROL);
reg |= ENABLE_DMA;
iowrite32be(reg, xb->membase + TDM_CONTROL);
spin_unlock_irqrestore(&xb->lock, flags);
return 0;
}
struct wcxb_meta_block {
__le32 chksum;
__le32 version;
__le32 size;
} __packed;
struct wcxb_firm_header {
u8 header[6];
__le32 chksum;
u8 pad[18];
__le32 version;
} __packed;
static u32 wcxb_get_firmware_version(struct wcxb *xb)
{
u32 version = 0;
/* Two version registers are read and catenated into one */
/* Firmware version goes in bits upper byte */
version = ((ioread32be(xb->membase + 0x400) & 0xffff)<<16);
/* Microblaze version goes in lower word */
version += ioread32be(xb->membase + 0x2018);
return version;
}
static int wcxb_update_firmware(struct wcxb *xb, const struct firmware *fw,
const char *filename)
{
u32 tdm_control;
static const int APPLICATION_ADDRESS = 0x200000;
static const int META_BLOCK_OFFSET = 0x170000;
static const int ERASE_BLOCK_SIZE = 0x010000;
static const int END_OFFSET = APPLICATION_ADDRESS + META_BLOCK_OFFSET +
ERASE_BLOCK_SIZE;
struct wcxb_spi_master *flash_spi_master;
struct wcxb_spi_device *flash_spi_device;
struct wcxb_meta_block meta;
int offset;
struct wcxb_firm_header *head = (struct wcxb_firm_header *)(fw->data);
if (fw->size > (META_BLOCK_OFFSET + sizeof(*head))) {
dev_err(&xb->pdev->dev,
"Firmware is too large to fit in available space.\n");
return -EINVAL;
}
meta.size = cpu_to_le32(fw->size);
meta.version = head->version;
meta.chksum = head->chksum;
flash_spi_master = wcxb_spi_master_create(&xb->pdev->dev,
xb->membase + FLASH_SPI_BASE,
false);
flash_spi_device = wcxb_spi_device_create(flash_spi_master, 0);
dev_info(&xb->pdev->dev,
"Uploading %s. This can take up to 30 seconds.\n", filename);
/* First erase all the blocks in the application area. */
offset = APPLICATION_ADDRESS;
while (offset < END_OFFSET) {
wcxb_flash_sector_erase(flash_spi_device, offset);
offset += ERASE_BLOCK_SIZE;
}
/* Then write the new firmware file. */
wcxb_flash_write(flash_spi_device, APPLICATION_ADDRESS,
&fw->data[sizeof(struct wcxb_firm_header)],
fw->size - sizeof(struct wcxb_firm_header));
/* Finally, update the meta block. */
wcxb_flash_write(flash_spi_device,
APPLICATION_ADDRESS + META_BLOCK_OFFSET,
&meta, sizeof(meta));
/* Reset fpga after loading firmware */
dev_info(&xb->pdev->dev, "Firmware load complete. Reseting device.\n");
tdm_control = ioread32be(xb->membase + TDM_CONTROL);
wcxb_hard_reset(xb);
iowrite32be(0, xb->membase + 0x04);
iowrite32be(tdm_control, xb->membase + TDM_CONTROL);
wcxb_spi_device_destroy(flash_spi_device);
wcxb_spi_master_destroy(flash_spi_master);
return 0;
}
int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version,
const char *firmware_filename, bool force_firmware)
{
const struct firmware *fw;
const struct wcxb_firm_header *header;
int res = 0;
u32 crc;
u32 version = 0;
version = wcxb_get_firmware_version(xb);
if (0xff000000 == (version & 0xff000000)) {
dev_info(&xb->pdev->dev,
"Invalid firmware %x. Please check your hardware.\n",
version);
return -EIO;
}
if ((expected_version == version) && !force_firmware) {
dev_info(&xb->pdev->dev, "Firmware version: %x\n", version);
return 0;
}
if (force_firmware) {
dev_info(&xb->pdev->dev,
"force_firmware module parameter is set. Forcing firmware load, regardless of version\n");
} else {
dev_info(&xb->pdev->dev,
"Firmware version %x is running, but we require version %x.\n",
version, expected_version);
}
res = request_firmware(&fw, firmware_filename, &xb->pdev->dev);
if (res) {
dev_info(&xb->pdev->dev,
"Firmware '%s' not available from userspace.\n",
firmware_filename);
goto cleanup;
}
header = (const struct wcxb_firm_header *)fw->data;
/* Check the crc */
crc = crc32(~0, &fw->data[10], fw->size - 10) ^ ~0;
if (memcmp("DIGIUM", header->header, sizeof(header->header)) ||
(le32_to_cpu(header->chksum) != crc)) {
dev_info(&xb->pdev->dev,
"%s is invalid. Please reinstall.\n",
firmware_filename);
goto cleanup;
}
/* Check the file vs required firmware versions */
if (le32_to_cpu(header->version) != expected_version) {
dev_err(&xb->pdev->dev,
"Existing firmware file %s is version %x, but we require %x. Please install the correct firmware file.\n",
firmware_filename, le32_to_cpu(header->version),
expected_version);
res = -EIO;
goto cleanup;
}
dev_info(&xb->pdev->dev, "Found %s (version: %x) Preparing for flash\n",
firmware_filename, header->version);
res = wcxb_update_firmware(xb, fw, firmware_filename);
version = wcxb_get_firmware_version(xb);
dev_info(&xb->pdev->dev, "Reset into firmware version: %x\n", version);
if ((expected_version != version) && !force_firmware) {
/* On the off chance that the interface is in a state where it
* cannot boot into the updated firmware image, power cycling
* the card can recover. A simple "reset" of the computer is not
* sufficient, power has to be removed completely. */
dev_err(&xb->pdev->dev,
"The wrong firmware is running after update. Please power cycle and try again.\n");
res = -EIO;
goto cleanup;
}
if (res) {
dev_info(&xb->pdev->dev,
"Failed to load firmware %s\n", firmware_filename);
}
cleanup:
release_firmware(fw);
return res;
}

184
drivers/dahdi/wcxb.h Normal file
View File

@@ -0,0 +1,184 @@
/*
* wcxb SPI library
*
* Copyright (C) 2013 Digium, Inc.
*
* All rights reserved.
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#ifndef __WCXB_H__
#define __WCXB_H__
#define WCXB_DEFAULT_LATENCY 3U
#define WCXB_DEFAULT_MAXLATENCY 20U
#define WCXB_DMA_CHAN_SIZE 128
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
/* The is_pcie member was backported but I'm not sure in which version. */
# ifndef RHEL_RELEASE_VERSION
#define WCXB_PCI_DEV_DOES_NOT_HAVE_IS_PCIE
# endif
#else
#endif
struct wcxb;
struct wcxb_operations {
void (*handle_receive)(struct wcxb *xb, void *frame);
void (*handle_transmit)(struct wcxb *xb, void *frame);
void (*handle_error)(struct wcxb *xb);
void (*handle_interrupt)(struct wcxb *xb, u32 pending);
};
struct wcxb_meta_desc;
struct wcxb_hw_desc;
struct wcxb {
struct pci_dev *pdev;
spinlock_t lock;
const struct wcxb_operations *ops;
unsigned int *debug;
unsigned int max_latency;
unsigned int latency;
struct {
u32 have_msi:1;
u32 latency_locked:1;
u32 drive_timing_cable:1;
#ifdef WCXB_PCI_DEV_DOES_NOT_HAVE_IS_PCIE
u32 is_pcie:1;
#endif
} flags;
void __iomem *membase;
struct wcxb_meta_desc *meta_dring;
struct wcxb_hw_desc *hw_dring;
unsigned int dma_head;
unsigned int dma_tail;
dma_addr_t hw_dring_phys;
struct dma_pool *pool;
unsigned long framecount;
};
extern int wcxb_init(struct wcxb *xb, const char *board_name, u32 int_mode);
extern void wcxb_release(struct wcxb *xb);
extern int wcxb_start(struct wcxb *xb);
extern void wcxb_stop(struct wcxb *xb);
extern int wcxb_wait_for_stop(struct wcxb *xb, unsigned long timeout_ms);
extern bool wcxb_is_stopped(struct wcxb *xb);
enum wcxb_clock_sources {
WCXB_CLOCK_SELF, /* Use the internal oscillator for timing. */
WCXB_CLOCK_RECOVER, /* Recover the clock from a framer. */
#ifdef RPC_RCLK
WCXB_CLOCK_RECOVER_ALT, /* Recover the clock from a framer. */
#endif
WCXB_CLOCK_SLAVE /* Recover clock from any timing header. */
};
extern enum wcxb_clock_sources wcxb_get_clksrc(struct wcxb *xb);
extern void wcxb_set_clksrc(struct wcxb *xb, enum wcxb_clock_sources clksrc);
static inline void wcxb_enable_timing_header_driver(struct wcxb *xb)
{
xb->flags.drive_timing_cable = 1;
}
static inline bool wcxb_is_timing_header_driver_enabled(struct wcxb *xb)
{
return 1 == xb->flags.drive_timing_cable;
}
static inline void wcxb_disable_timing_header_driver(struct wcxb *xb)
{
xb->flags.drive_timing_cable = 0;
}
extern int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version,
const char *firmware_filename,
bool force_firmware);
extern void wcxb_stop_dma(struct wcxb *xb);
extern void wcxb_disable_interrupts(struct wcxb *xb);
static inline void wcxb_gpio_set(struct wcxb *xb, u32 bits)
{
u32 reg;
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
reg = ioread32be(xb->membase);
iowrite32be(reg | bits, xb->membase);
spin_unlock_irqrestore(&xb->lock, flags);
}
static inline void wcxb_gpio_clear(struct wcxb *xb, u32 bits)
{
u32 reg;
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
reg = ioread32be(xb->membase);
iowrite32be(reg & (~bits), xb->membase);
spin_unlock_irqrestore(&xb->lock, flags);
}
static inline void
wcxb_set_maxlatency(struct wcxb *xb, unsigned int max_latency)
{
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
xb->max_latency = clamp(max_latency,
xb->latency,
WCXB_DEFAULT_MAXLATENCY);
spin_unlock_irqrestore(&xb->lock, flags);
}
static inline void
wcxb_set_minlatency(struct wcxb *xb, unsigned int min_latency)
{
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
xb->latency = clamp(min_latency, WCXB_DEFAULT_LATENCY,
WCXB_DEFAULT_MAXLATENCY);
spin_unlock_irqrestore(&xb->lock, flags);
}
static inline void
wcxb_lock_latency(struct wcxb *xb)
{
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
xb->flags.latency_locked = 1;
spin_unlock_irqrestore(&xb->lock, flags);
return;
}
static inline void
wcxb_unlock_latency(struct wcxb *xb)
{
unsigned long flags;
spin_lock_irqsave(&xb->lock, flags);
xb->flags.latency_locked = 0;
spin_unlock_irqrestore(&xb->lock, flags);
return;
}
/* Interface for the echocan block */
extern void wcxb_enable_echocan(struct wcxb *xb);
extern void wcxb_disable_echocan(struct wcxb *xb);
extern void wcxb_reset_echocan(struct wcxb *xb);
extern void wcxb_enable_echocan_dram(struct wcxb *xb);
extern bool wcxb_is_echocan_present(struct wcxb *xb);
extern u16 wcxb_get_echocan_reg(struct wcxb *xb, u32 address);
extern void wcxb_set_echocan_reg(struct wcxb *xb, u32 address, u16 val);
#endif

170
drivers/dahdi/wcxb_flash.c Normal file
View File

@@ -0,0 +1,170 @@
/*
* wcxb SPI library
*
* Copyright (C) 2013 Digium, Inc.
*
* All rights reserved.
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/sched.h>
#include "wcxb_spi.h"
#include "wcxb_flash.h"
#define FLASH_PAGE_PROGRAM 0x02
#define FLASH_READ 0x03
#define FLASH_READ_STATUS 0x05
#define FLASH_WRITE_ENABLE 0x06
#define FLASH_SECTOR_ERASE 0xd8
static int wcxb_flash_read_status_register(struct wcxb_spi_device *spi,
u8 *status)
{
u8 command[] = {
FLASH_READ_STATUS,
};
struct wcxb_spi_transfer t_cmd = {
.tx_buf = command,
.len = sizeof(command),
};
struct wcxb_spi_transfer t_serial = {
.rx_buf = status,
.len = 1,
};
struct wcxb_spi_message m;
wcxb_spi_message_init(&m);
wcxb_spi_message_add_tail(&t_cmd, &m);
wcxb_spi_message_add_tail(&t_serial, &m);
return wcxb_spi_sync(spi, &m);
}
int wcxb_flash_read(struct wcxb_spi_device *spi, unsigned int address,
void *data, size_t len)
{
u8 command[] = {
FLASH_READ,
(address & 0xff0000) >> 16,
(address & 0xff00) >> 8,
(address & 0xff)
};
struct wcxb_spi_transfer t_cmd = {
.tx_buf = command,
.len = sizeof(command),
};
struct wcxb_spi_transfer t_serial = {
.rx_buf = data,
.len = len,
};
struct wcxb_spi_message m;
wcxb_spi_message_init(&m);
wcxb_spi_message_add_tail(&t_cmd, &m);
wcxb_spi_message_add_tail(&t_serial, &m);
return wcxb_spi_sync(spi, &m);
}
static int wcxb_flash_wait_until_not_busy(struct wcxb_spi_device *spi)
{
int res;
u8 status;
unsigned long stop = jiffies + 5*HZ;
do {
res = wcxb_flash_read_status_register(spi, &status);
} while (!res && (status & 0x1) && time_before(jiffies, stop));
if (!res)
return res;
if (time_after_eq(jiffies, stop))
return -EIO;
return 0;
}
static int wcxb_flash_write_enable(struct wcxb_spi_device *spi)
{
u8 command = FLASH_WRITE_ENABLE;
return wcxb_spi_write(spi, &command, 1);
}
int wcxb_flash_sector_erase(struct wcxb_spi_device *spi,
unsigned int address)
{
int res;
u8 command[] = {FLASH_SECTOR_ERASE, (address >> 16)&0xff, 0x00, 0x00};
/* Sector must be on 64KB boundary. */
if (address & 0xffff)
return -EINVAL;
/* Start the erase. */
res = wcxb_flash_write_enable(spi);
if (res)
return res;
res = wcxb_spi_write(spi, &command, sizeof(command));
if (res)
return res;
return wcxb_flash_wait_until_not_busy(spi);
}
int wcxb_flash_write(struct wcxb_spi_device *spi, unsigned int address,
const void *data, size_t len)
{
int res;
const size_t FLASH_PAGE_SIZE = 256;
u8 command[] = {
FLASH_PAGE_PROGRAM,
(address & 0xff0000) >> 16,
(address & 0xff00) >> 8,
0x00,
};
struct wcxb_spi_transfer t_cmd = {
.tx_buf = command,
.len = sizeof(command),
};
struct wcxb_spi_transfer t_data = {
.tx_buf = data,
.len = len,
};
struct wcxb_spi_message m;
/* We need to write on page size boundaries */
WARN_ON(address & 0xff);
wcxb_spi_message_init(&m);
wcxb_spi_message_add_tail(&t_cmd, &m);
wcxb_spi_message_add_tail(&t_data, &m);
while (len) {
res = wcxb_flash_write_enable(spi);
if (res)
return res;
command[1] = (address >> 16) & 0xff;
command[2] = (address >> 8) & 0xff;
t_data.tx_buf = data;
t_data.len = min(len, FLASH_PAGE_SIZE);
res = wcxb_spi_sync(spi, &m);
WARN_ON(res);
if (res)
return res;
res = wcxb_flash_wait_until_not_busy(spi);
WARN_ON(res);
if (res)
return res;
len -= t_data.len;
address += t_data.len;
data += t_data.len;
}
return 0;
}

View File

@@ -0,0 +1,34 @@
/*
* wcxb SPI library
*
* Copyright (C) 2013 Digium, Inc.
*
* All rights reserved.
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#ifndef __WCXB_FLASH_H__
#define __WCXB_FLASH_H__
extern int wcxb_flash_read(struct wcxb_spi_device *spi, unsigned int address,
void *data, size_t len);
extern int wcxb_flash_sector_erase(struct wcxb_spi_device *spi, unsigned int
address);
extern int wcxb_flash_write(struct wcxb_spi_device *spi, unsigned int address,
const void *data, size_t len);
#endif

382
drivers/dahdi/wcxb_spi.c Normal file
View File

@@ -0,0 +1,382 @@
/*
* wcxb SPI library
*
* Copyright (C) 2013 Digium, Inc.
*
* All rights reserved.
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/completion.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <dahdi/kernel.h>
#include "wcxb_spi.h"
#define BBIT(n) (1UL << (31UL - (n)))
#define SPISRR 0x40
#define SPISRR_RESET 0x0000000a /* Resets Device */
#define SPICR 0x60
#define SPICR_LSB_FIRST BBIT(22) /* LSB First. 0=MSB first transfer */
#define SPICR_MASTER_INHIBIT BBIT(23) /* Master Transaction Inhibit */
#define SPICR_SLAVE_SELECT BBIT(24) /* Manual Slave Select Assert Enable */
#define SPICR_RX_FIFO_RESET BBIT(25) /* Receive FIFO Reset */
#define SPICR_TX_FIFO_RESET BBIT(26) /* Transmit FIFO Reset */
#define SPICR_CPHA BBIT(27) /* Clock Phase */
#define SPICR_CPOL BBIT(28) /* Clock Polarity 0=Active High */
#define SPICR_MASTER BBIT(29) /* Master Enable */
#define SPICR_SPE BBIT(30) /* SPI System Enable */
#define SPICR_LOOP BBIT(31) /* Local Loopback Mode */
#define SPICR_START_TRANSFER (SPICR_CPHA | SPICR_CPOL | \
SPICR_MASTER | SPICR_SPE)
#define SPICR_READY_TRANSFER (SPICR_MASTER_INHIBIT | SPICR_START_TRANSFER)
#define SPISR 0x64 /* SPI Status Register */
#define SPISR_SLAVE_MODE_SEL BBIT(26) /* Slave Mode Select Flag */
#define SPISR_MODF BBIT(27) /* Mode-Fault Error Flag */
#define SPISR_TX_FULL BBIT(28) /* Transmit FIFO Full */
#define SPISR_TX_EMPTY BBIT(29) /* Transmit FIFO Empty */
#define SPISR_RX_FULL BBIT(30) /* Receive FIFO Full */
#define SPISR_RX_EMPTY BBIT(31) /* Receive FIFO Empty */
#define SPIDTR 0x68 /* SPI Data Transmit Register */
#define SPIDRR 0x6c /* SPI Data Receive Register */
#define SPISSR 0x70 /* SPI Slave Select Register */
#undef SUCCESS
#define SUCCESS 0
struct wcxb_spi_master {
struct device *parent;
struct list_head message_queue;
spinlock_t lock;
void __iomem *base;
struct wcxb_spi_message *cur_msg;
struct wcxb_spi_transfer *cur_transfer;
u16 bytes_left;
u16 bytes_in_fifo;
const u8 *cur_tx_byte;
u8 *cur_rx_byte;
u16 auto_cs:1;
};
static inline void _wcxb_assert_chip_select(struct wcxb_spi_master *master,
unsigned int cs)
{
const int cs_mask = ~(1UL << cs);
iowrite32be(cs_mask, master->base + SPISSR);
ioread32be(master->base + SPISSR);
}
static inline void _wcxb_clear_chip_select(struct wcxb_spi_master *master)
{
iowrite32be(~(0), master->base + SPISSR);
ioread32(master->base + SPISSR);
}
static inline void wcxb_spi_reset_controller(struct wcxb_spi_master *master)
{
u32 spicr = SPICR_READY_TRANSFER;
spicr |= (master->auto_cs) ? 0 : SPICR_SLAVE_SELECT;
iowrite32be(SPISRR_RESET, master->base + SPISRR);
iowrite32be(spicr, master->base + SPICR);
iowrite32be(0xffffffff, master->base + SPISSR);
}
struct wcxb_spi_master *wcxb_spi_master_create(struct device *parent,
void __iomem *membase,
bool auto_cs)
{
struct wcxb_spi_master *master = NULL;
master = kzalloc(sizeof(struct wcxb_spi_master), GFP_KERNEL);
if (!master)
goto error_exit;
spin_lock_init(&master->lock);
INIT_LIST_HEAD(&master->message_queue);
master->base = membase;
master->parent = parent;
master->auto_cs = (auto_cs) ? 1 : 0;
wcxb_spi_reset_controller(master);
return master;
error_exit:
kfree(master);
return NULL;
}
void wcxb_spi_master_destroy(struct wcxb_spi_master *master)
{
struct wcxb_spi_message *m;
if (!master)
return;
while (!list_empty(&master->message_queue)) {
m = list_first_entry(&master->message_queue,
struct wcxb_spi_message, node);
list_del(&m->node);
if (m->complete)
m->complete(m->arg);
}
kfree(master);
return;
}
static inline bool is_txfifo_empty(const struct wcxb_spi_master *master)
{
return ((ioread32(master->base + SPISR) &
cpu_to_be32(SPISR_TX_EMPTY)) > 0);
}
static const u8 DUMMY_TX = 0xff;
static u8 DUMMY_RX;
static void _wcxb_spi_transfer_to_fifo(struct wcxb_spi_master *master)
{
const unsigned int FIFO_SIZE = 16;
u32 spicr;
while (master->bytes_left && master->bytes_in_fifo < FIFO_SIZE) {
iowrite32be(*master->cur_tx_byte, master->base + SPIDTR);
master->bytes_in_fifo++;
master->bytes_left--;
if (&DUMMY_TX != master->cur_tx_byte)
master->cur_tx_byte++;
}
spicr = (master->auto_cs) ? SPICR_START_TRANSFER :
SPICR_START_TRANSFER | SPICR_SLAVE_SELECT;
iowrite32be(spicr, master->base + SPICR);
}
static void _wcxb_spi_transfer_from_fifo(struct wcxb_spi_master *master)
{
u32 spicr;
while (master->bytes_in_fifo) {
*master->cur_rx_byte = ioread32be(master->base + SPIDRR);
if (&DUMMY_RX != master->cur_rx_byte)
master->cur_rx_byte++;
--master->bytes_in_fifo;
}
spicr = SPICR_START_TRANSFER;
spicr |= (master->auto_cs) ? 0 : SPICR_SLAVE_SELECT;
iowrite32be(spicr | SPICR_MASTER_INHIBIT, master->base + SPICR);
}
static void _wcxb_spi_start_transfer(struct wcxb_spi_master *master,
struct wcxb_spi_transfer *t)
{
#ifdef DEBUG
if (!t || !master || (!t->tx_buf && !t->rx_buf) ||
master->cur_transfer) {
WARN_ON(1);
return;
}
#endif
master->cur_transfer = t;
master->bytes_left = t->len;
master->cur_tx_byte = (t->tx_buf) ?: &DUMMY_TX;
master->cur_rx_byte = (t->rx_buf) ?: &DUMMY_RX;
_wcxb_spi_transfer_to_fifo(master);
}
/**
* _wcxb_spi_start_message - Start a new message transferring.
*
* Must be called with master->lock held.
*
*/
static int _wcxb_spi_start_message(struct wcxb_spi_master *master,
struct wcxb_spi_message *message)
{
struct wcxb_spi_transfer *t;
if (master->cur_msg) {
/* There is already a message in progress. Queue for later. */
list_add_tail(&message->node, &master->message_queue);
return 0;
}
if (!message->spi) {
dev_dbg(master->parent,
"Queueing message without SPI device specified?\n");
return -EINVAL;
};
master->cur_msg = message;
_wcxb_assert_chip_select(master, message->spi->chip_select);
t = list_first_entry(&message->transfers,
struct wcxb_spi_transfer, node);
_wcxb_spi_start_transfer(master, t);
return 0;
}
/**
* wcxb_spi_complete_message - Complete the current message.
*
* Called after all transfers in current message have been completed. This will
* complete the current message and start the next queued message if there are
* any.
*
* Must be called with the master->lock held.
*
*/
static void _wcxb_spi_complete_cur_msg(struct wcxb_spi_master *master)
{
struct wcxb_spi_message *message;
if (!master->cur_msg)
return;
message = master->cur_msg;
message->status = SUCCESS;
_wcxb_clear_chip_select(master);
master->cur_msg = NULL;
if (!list_empty(&master->message_queue)) {
message = list_first_entry(&master->message_queue,
struct wcxb_spi_message, node);
list_del(&message->node);
_wcxb_spi_start_message(master, message);
}
return;
}
static inline bool
_wcxb_spi_is_last_transfer(const struct wcxb_spi_transfer *t,
const struct wcxb_spi_message *message)
{
return t->node.next == &message->transfers;
}
static inline struct wcxb_spi_transfer *
_wcxb_spi_next_transfer(struct wcxb_spi_transfer *t)
{
return list_entry(t->node.next, struct wcxb_spi_transfer, node);
}
/**
* wcxb_spi_handle_interrupt - Drives the transfers forward.
*
* Doesn't necessarily need to be called in the context of a real interrupt, but
* should be called with interrupts disabled on the local CPU.
*
*/
void wcxb_spi_handle_interrupt(struct wcxb_spi_master *master)
{
struct wcxb_spi_message *msg = master->cur_msg;
struct wcxb_spi_transfer *t = master->cur_transfer;
void (*complete)(void *arg) = NULL;
unsigned long flags;
/* Check if we're not in the middle of a transfer, or not finished with
* a part of one. */
spin_lock_irqsave(&master->lock, flags);
if (!msg || !is_txfifo_empty(master))
goto done;
#ifdef DEBUG
if (!t) {
dev_dbg(master->parent,
"No current transfer in %s\n", __func__);
goto done;
}
#endif
/* First read any data out of the receive FIFO into the current
* transfer. */
_wcxb_spi_transfer_from_fifo(master);
if (master->bytes_left) {
/* The current transfer isn't finished. */
_wcxb_spi_transfer_to_fifo(master);
goto done;
}
/* The current transfer is finished. Check for another transfer in this
* message or complete it and look for another message to start. */
master->cur_transfer = NULL;
if (_wcxb_spi_is_last_transfer(t, msg)) {
complete = msg->complete;
_wcxb_spi_complete_cur_msg(master);
} else {
t = _wcxb_spi_next_transfer(t);
_wcxb_spi_start_transfer(master, t);
}
done:
spin_unlock_irqrestore(&master->lock, flags);
/* Do not call the complete call back under the bus lock. */
if (complete)
complete(msg->arg);
return;
}
int wcxb_spi_async(struct wcxb_spi_device *spi,
struct wcxb_spi_message *message)
{
int res;
unsigned long flags;
WARN_ON(!spi || !message || !spi->master);
if (list_empty(&message->transfers)) {
/* No transfers in this message? */
if (message->complete)
message->complete(message->arg);
message->status = -EINVAL;
return 0;
}
message->status = -EINPROGRESS;
message->spi = spi;
spin_lock_irqsave(&spi->master->lock, flags);
res = _wcxb_spi_start_message(spi->master, message);
spin_unlock_irqrestore(&spi->master->lock, flags);
return res;
}
static void wcxb_spi_complete_message(void *arg)
{
complete((struct completion *)arg);
}
int wcxb_spi_sync(struct wcxb_spi_device *spi, struct wcxb_spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
WARN_ON(!spi || !spi->master);
message->complete = wcxb_spi_complete_message;
message->arg = &done;
wcxb_spi_async(spi, message);
/* TODO: There has got to be a better way to do this. */
while (!try_wait_for_completion(&done)) {
wcxb_spi_handle_interrupt(spi->master);
cpu_relax();
}
return message->status;
}

116
drivers/dahdi/wcxb_spi.h Normal file
View File

@@ -0,0 +1,116 @@
/*
* wcxb SPI library
*
* Copyright (C) 2013 Digium, Inc.
*
* All rights reserved.
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#ifndef __WCXB_SPI_H
#define __WCXB_SPI_H
#include <linux/spi/spi.h>
#include <stdbool.h>
struct wcxb_spi_transfer {
const void *tx_buf;
void *rx_buf;
u32 len:16;
u16 delay_usecs;
struct list_head node;
};
struct wcxb_spi_message {
struct list_head transfers;
struct list_head node;
struct wcxb_spi_device *spi;
void (*complete)(void *arg);
void *arg;
int status;
};
struct wcxb_spi_master;
struct wcxb_spi_device {
struct wcxb_spi_master *master;
u16 chip_select;
};
extern struct wcxb_spi_master *wcxb_spi_master_create(struct device *parent,
void __iomem *base, bool auto_cs);
extern void wcxb_spi_master_destroy(struct wcxb_spi_master *master);
extern int wcxb_spi_sync(struct wcxb_spi_device *spi,
struct wcxb_spi_message *message);
extern int wcxb_spi_async(struct wcxb_spi_device *spi,
struct wcxb_spi_message *message);
extern void wcxb_spi_handle_interrupt(struct wcxb_spi_master *master);
static inline struct wcxb_spi_device *
wcxb_spi_device_create(struct wcxb_spi_master *master, u16 chip_select)
{
struct wcxb_spi_device *spi = kzalloc(sizeof(*spi), GFP_KERNEL);
if (!spi)
return NULL;
spi->master = master;
spi->chip_select = chip_select;
return spi;
}
static inline void wcxb_spi_device_destroy(struct wcxb_spi_device *spi)
{
kfree(spi);
}
static inline void wcxb_spi_message_init(struct wcxb_spi_message *m)
{
memset(m, 0, sizeof(*m));
INIT_LIST_HEAD(&m->transfers);
}
static inline void wcxb_spi_message_add_tail(struct wcxb_spi_transfer *t,
struct wcxb_spi_message *m)
{
list_add_tail(&t->node, &m->transfers);
}
static inline int
wcxb_spi_write(struct wcxb_spi_device *spi, const void *buffer, size_t len)
{
struct wcxb_spi_transfer t = {
.tx_buf = buffer,
.len = len,
};
struct wcxb_spi_message m;
wcxb_spi_message_init(&m);
wcxb_spi_message_add_tail(&t, &m);
return wcxb_spi_sync(spi, &m);
}
static inline int
wcxb_spi_read(struct wcxb_spi_device *spi, void *buffer, size_t len)
{
struct wcxb_spi_transfer t = {
.rx_buf = buffer,
.len = len,
};
struct wcxb_spi_message m;
wcxb_spi_message_init(&m);
wcxb_spi_message_add_tail(&t, &m);
return wcxb_spi_sync(spi, &m);
}
#endif

View File

@@ -654,6 +654,95 @@ static void __do_mute_dtmf(xpd_t *xpd, int pos, bool muteit)
CALL_PHONE_METHOD(card_pcm_recompute, xpd, priv->search_fsk_pattern);
}
struct ring_reg_param {
int is_indirect;
int regno;
uint8_t h_val;
uint8_t l_val;
};
enum ring_types {
RING_TYPE_NEON = 0,
RING_TYPE_TRAPEZ,
RING_TYPE_NORMAL,
};
struct byte_pair {
uint8_t h_val;
uint8_t l_val;
};
struct ring_reg_params {
const int is_indirect;
const int regno;
struct byte_pair values[1 + RING_TYPE_NORMAL - RING_TYPE_NEON];
};
#define REG_ENTRY(di, reg, vh1, vl1, vh2, vl2, vh3, vl3) \
{ (di), (reg), .values = { \
[RING_TYPE_NEON] = { .h_val = (vh1), .l_val = (vl1) }, \
[RING_TYPE_TRAPEZ] = { .h_val = (vh2), .l_val = (vl2) }, \
[RING_TYPE_NORMAL] = { .h_val = (vh3), .l_val = (vl3) }, \
}, \
}
static struct ring_reg_params ring_parameters[] = {
/* INDIR REG NEON TRAPEZ NORMAL */
REG_ENTRY(1, 0x16, 0xE8, 0x03, 0xC8, 0x00, 0x00, 0x00),
REG_ENTRY(1, 0x15, 0xEF, 0x7B, 0xAB, 0x5E, 0x77, 0x01),
REG_ENTRY(1, 0x14, 0x9F, 0x00, 0x8C, 0x01, 0xFD, 0x7E),
REG_ENTRY(0, 0x22, 0x00, 0x19, 0x00, 0x01, 0x00, 0x00),
REG_ENTRY(0, 0x30, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00),
REG_ENTRY(0, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00),
REG_ENTRY(0, 0x32, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00),
REG_ENTRY(0, 0x33, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00),
REG_ENTRY(1, 0x1D, 0x00, 0x46, 0x00, 0x36, 0x00, 0x36),
};
static int send_ring_parameters(xbus_t *xbus, xpd_t *xpd, int pos,
enum ring_types rtype)
{
const struct ring_reg_params *p;
const struct byte_pair *v;
int ret = 0;
int i;
if (rtype < RING_TYPE_NEON || rtype > RING_TYPE_NORMAL)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(ring_parameters); i++) {
p = &ring_parameters[i];
v = &(p->values[rtype]);
if (p->is_indirect) {
LINE_DBG(REGS, xpd, pos,
"[%d] 0x%02X: I 0x%02X 0x%02X\n",
i, p->regno, v->h_val, v->l_val);
ret = SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
p->regno, v->h_val, v->l_val);
if (ret < 0) {
LINE_ERR(xpd, pos,
"Failed: 0x%02X: I 0x%02X, 0x%02X\n",
p->regno, v->h_val, v->l_val);
break;
}
} else {
LINE_DBG(REGS, xpd, pos, "[%d] 0x%02X: D 0x%02X\n",
i, p->regno, v->l_val);
ret = SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
p->regno, v->l_val);
if (ret < 0) {
LINE_ERR(xpd, pos,
"Failed: 0x%02X: D 0x%02X\n",
p->regno, v->l_val);
break;
}
}
}
return ret;
}
static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos,
unsigned int msg_waiting)
{
@@ -667,81 +756,15 @@ static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos,
/* A write to register 0x40 will now turn on/off the VM led */
LINE_DBG(SIGNAL, xpd, pos, "NEON\n");
BIT_SET(priv->neon_blinking, pos);
ret +=
SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x16,
0xE8, 0x03);
ret +=
SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x15,
0xEF, 0x7B);
ret +=
SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x14,
0x9F, 0x00);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x22, 0x19);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x30, 0xE0);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x31, 0x01);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x32, 0xF0);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x33, 0x05);
ret +=
SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x1D,
0x00, 0x46);
ret = send_ring_parameters(xbus, xpd, pos, RING_TYPE_NEON);
} else if (ring_trapez) {
LINE_DBG(SIGNAL, xpd, pos, "RINGER: Trapez ring\n");
ret +=
SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x16,
0xC8, 0x00);
ret +=
SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x15,
0xAB, 0x5E);
ret +=
SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x14,
0x8C, 0x01);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x22, 0x01);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x4A, 0x34);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x30, 0x00);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x31, 0x00);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x32, 0x00);
ret +=
SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x33, 0x00);
ret +=
SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x1D,
0x00, 0x36);
ret = send_ring_parameters(xbus, xpd, pos, RING_TYPE_TRAPEZ);
} else {
/* A write to register 0x40 will now turn on/off the ringer */
LINE_DBG(SIGNAL, xpd, pos, "RINGER\n");
BIT_CLR(priv->neon_blinking, pos);
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x16, 0x00, 0x00);
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x15, 0x77, 0x01);
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x14, 0xFD, 0x7E);
ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x22, 0x00);
ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x30, 0x00);
ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x31, 0x00);
ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x32, 0x00);
ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x33, 0x00);
/* High Vbat~ -82V[Dc] */
ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x4A, 0x34);
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE,
0x1D, 0x00, 0x36);
ret = send_ring_parameters(xbus, xpd, pos, RING_TYPE_NORMAL);
}
return (ret ? -EPROTO : 0);
}
@@ -1838,9 +1861,134 @@ static const struct file_operations proc_xpd_metering_ops = {
#endif
#endif
static DEVICE_ATTR_READER(fxs_ring_registers_show, dev, buf)
{
xpd_t *xpd;
struct FXS_priv_data *priv;
unsigned long flags;
const struct ring_reg_params *p;
const struct byte_pair *v;
enum ring_types rtype;
int len = 0;
int i;
BUG_ON(!dev);
xpd = dev_to_xpd(dev);
if (!xpd)
return -ENODEV;
priv = xpd->priv;
BUG_ON(!priv);
spin_lock_irqsave(&xpd->lock, flags);
len += sprintf(buf + len, "# Reg#: D/I\tNEON \tTRAPEZ \tNORMAL \n");
for (i = 0; i < ARRAY_SIZE(ring_parameters); i++) {
p = &ring_parameters[i];
len += sprintf(buf + len, "[%d] 0x%02X: %c",
i, p->regno, (p->is_indirect) ? 'I' : 'D');
for (rtype = RING_TYPE_NEON; rtype <= RING_TYPE_NORMAL; rtype++) {
v = &(p->values[rtype]);
if (p->is_indirect)
len += sprintf(buf + len, "\t0x%02X 0x%02X",
v->h_val, v->l_val);
else
len += sprintf(buf + len, "\t0x%02X ----",
v->l_val);
}
len += sprintf(buf + len, "\n");
}
spin_unlock_irqrestore(&xpd->lock, flags);
return len;
}
static DEVICE_ATTR_WRITER(fxs_ring_registers_store, dev, buf, count)
{
xpd_t *xpd;
struct FXS_priv_data *priv;
unsigned long flags;
char rtype_name[MAX_PROC_WRITE];
enum ring_types rtype;
struct ring_reg_params *params;
struct byte_pair *v;
int regno;
int h_val;
int l_val;
int ret;
int i;
BUG_ON(!dev);
xpd = dev_to_xpd(dev);
if (!xpd)
return -ENODEV;
priv = xpd->priv;
BUG_ON(!priv);
ret = sscanf(buf, "%10s %X %X %X\n",
rtype_name, &regno, &h_val, &l_val);
if (ret < 3 || ret > 4) {
XPD_ERR(xpd, "Bad input: '%s'\n", buf);
XPD_ERR(xpd, "# Correct input\n");
XPD_ERR(xpd, "{NEON|TRAPEZ|NORMAL} <regno> <byte> [<byte>]\n");
goto invalid_input;
}
if (strcasecmp("NEON", rtype_name) == 0)
rtype = RING_TYPE_NEON;
else if (strcasecmp("TRAPEZ", rtype_name) == 0)
rtype = RING_TYPE_TRAPEZ;
else if (strcasecmp("NORMAL", rtype_name) == 0)
rtype = RING_TYPE_NORMAL;
else {
XPD_ERR(xpd, "Unknown ring type '%s' (NEON/TRAPEZ/NORMAL)\n",
rtype_name);
goto invalid_input;
}
params = NULL;
for (i = 0; i < ARRAY_SIZE(ring_parameters); i++) {
if (ring_parameters[i].regno == regno) {
params = &ring_parameters[i];
break;
}
}
if (!params) {
XPD_ERR(xpd, "Bad register 0x%X\n", regno);
goto invalid_input;
}
if (params->is_indirect) {
if (ret != 4) {
XPD_ERR(xpd,
"Missing low-byte (0x%X is indirect register)\n",
regno);
goto invalid_input;
}
XPD_INFO(xpd, "%s Indirect 0x%X <=== 0x%X 0x%X\n",
rtype_name, regno, h_val, l_val);
} else {
if (ret != 3) {
XPD_ERR(xpd,
"Should give exactly one value (0x%X is direct register)\n",
regno);
goto invalid_input;
}
l_val = h_val;
h_val = 0;
XPD_INFO(xpd, "%s Direct 0x%X <=== 0x%X\n",
rtype_name, regno, h_val);
}
spin_lock_irqsave(&xpd->lock, flags);
v = &(params->values[rtype]);
v->h_val = h_val;
v->l_val = l_val;
spin_unlock_irqrestore(&xpd->lock, flags);
return count;
invalid_input:
return -EINVAL;
}
static DEVICE_ATTR(fxs_ring_registers, S_IRUGO | S_IWUSR,
fxs_ring_registers_show,
fxs_ring_registers_store);
static int fxs_xpd_probe(struct device *dev)
{
xpd_t *xpd;
int ret;
xpd = dev_to_xpd(dev);
/* Is it our device? */
@@ -1850,7 +1998,15 @@ static int fxs_xpd_probe(struct device *dev)
return -EINVAL;
}
XPD_DBG(DEVICES, xpd, "SYSFS\n");
ret = device_create_file(dev, &dev_attr_fxs_ring_registers);
if (ret) {
XPD_ERR(xpd, "%s: device_create_file(fxs_ring_registers) failed: %d\n",
__func__, ret);
goto fail_fxs_ring_registers;
}
return 0;
fail_fxs_ring_registers:
return ret;
}
static int fxs_xpd_remove(struct device *dev)
@@ -1859,6 +2015,7 @@ static int fxs_xpd_remove(struct device *dev)
xpd = dev_to_xpd(dev);
XPD_DBG(DEVICES, xpd, "SYSFS\n");
device_remove_file(dev, &dev_attr_fxs_ring_registers);
return 0;
}

View File

@@ -511,6 +511,11 @@ HANDLER_DEF(GLOBAL, AB_DESCRIPTION)
ret = -EPROTO;
goto proto_err;
}
if (units[0].addr.unit != 0 || units[0].addr.subunit != 0) {
XBUS_NOTICE(xbus, "No first module. Astribank unusable.\n");
ret = -EPROTO;
goto proto_err;
}
if (!xbus_setstate(xbus, XBUS_STATE_RECVD_DESC)) {
ret = -EPROTO;
goto proto_err;
@@ -568,6 +573,7 @@ HANDLER_DEF(GLOBAL, AB_DESCRIPTION)
}
goto out;
proto_err:
xbus_setstate(xbus, XBUS_STATE_FAIL);
dump_packet("AB_DESCRIPTION", pack, DBG_ANY);
out:
return ret;

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
USB_FW = USB_FW.hex USB_FW.201.hex USB_RECOV.hex
FPGA_FW = FPGA_FXS.hex FPGA_1141.hex FPGA_1151.hex FPGA_1161.hex \
FPGA_1161.201.hex
FPGA_1161.201.hex FPGA_1161.202.hex
PIC_FW = PIC_TYPE_1.hex PIC_TYPE_2.hex PIC_TYPE_3.hex PIC_TYPE_4.hex
OCT_FW = $(wildcard OCT6104E-256D.ima)
FIRMWARES = $(USB_FW) $(FPGA_FW) $(PIC_FW) $(OCT_FW)
@@ -20,4 +20,7 @@ install:
mkdir -p $(TARGET)
install $(SCRIPTS) $(TARGET)/
install -m 644 ../XppConfig.pm $(FIRMWARES) $(TARGET)/
if [ ! -r $(TARGET)/USB_FW.202.hex ]; then \
ln -s USB_FW.201.hex $(TARGET)/USB_FW.202.hex;\
fi

View File

@@ -0,0 +1 @@
USB_FW.201.hex

View File

@@ -59,6 +59,7 @@ my %settings;
$settings{debug} = 0;
$settings{fxs_skip_calib} = 0;
my $chipregs;
my $ring_registers;
sub logit {
print STDERR "$unit_id: @_\n";
@@ -104,6 +105,10 @@ if (-t STDERR) {
logit "OLD DRIVER: does not use /sys chipregs. Falling back to /proc"
if -f $chipregs;
}
$ring_registers = sprintf "/sys/bus/xpds/devices/%02d:%1d:0/fxs_ring_registers",
$ENV{XBUS_NUMBER}, $ENV{UNIT_NUMBER};
logit "OLD DRIVER: missing '$ring_registers' -- fallback to hard-coded defaults"
unless -f $ring_registers;
}
sub set_output() {
@@ -147,6 +152,16 @@ sub write_to_slic_file($) {
}
sub write_to_ring_register($) {
my $write_str = shift;
open(SLICS,">$ring_registers") or
die("Failed writing to ring_registers file $ring_registers");
print SLICS $write_str;
close(SLICS) or die "Failed writing '$write_str' to '$ring_registers': $!";
main::mysleep(0.001);
}
sub read_reg($$$) {
my $read_slic = shift;
my $read_reg = shift;
@@ -298,7 +313,7 @@ sub init_early_direct_regs() {
return write_to_slic_file("#
* WD 08 00 # Audio Path Loopback Control
* WD 6C 01
* WD 4A 3F # High Battery Voltage
* WD 4A 34 # High Battery Voltage
* WD 4B $lbv # Low Battery Voltage
* WD 49 $vcm # Common Mode Voltage (VCM)
* WD 40 00 # Line Feed Control
@@ -442,6 +457,10 @@ sub check_slics() {
return @slics;
}
sub overwrite_ring_registers() {
write_to_ring_register("NEON 0x33 0x12");
}
package main;
main::debug "Starting '$0'";
@@ -470,6 +489,7 @@ while(<DATA>) {
print "$_\n";
}
close REG;
FXS::overwrite_ring_registers();
main::debug "Ending '$0'";
close STDERR;

View File

@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#ifdef PROTOCOL_DEBUG
@@ -937,6 +938,8 @@ static void xbus_free_ddev(xbus_t *xbus)
xbus->ddev = NULL;
}
static DEFINE_MUTEX(dahdi_registration_mutex);
int xbus_register_dahdi_device(xbus_t *xbus)
{
int i;
@@ -944,6 +947,11 @@ int xbus_register_dahdi_device(xbus_t *xbus)
int ret;
XBUS_DBG(DEVICES, xbus, "Entering %s\n", __func__);
ret = mutex_lock_interruptible(&dahdi_registration_mutex);
if (ret < 0) {
XBUS_ERR(xbus, "dahdi_registration_mutex already taken\n");
goto err;
}
if (xbus_is_registered(xbus)) {
XBUS_ERR(xbus, "Already registered to DAHDI\n");
WARN_ON(1);
@@ -1008,17 +1016,26 @@ int xbus_register_dahdi_device(xbus_t *xbus)
xpd_dahdi_postregister(xpd);
}
}
return 0;
ret = 0;
out:
mutex_unlock(&dahdi_registration_mutex);
return ret;
err:
xbus_free_ddev(xbus);
return ret;
goto out;
}
void xbus_unregister_dahdi_device(xbus_t *xbus)
{
int i;
int ret;
XBUS_NOTICE(xbus, "%s\n", __func__);
ret = mutex_lock_interruptible(&dahdi_registration_mutex);
if (ret < 0) {
XBUS_ERR(xbus, "dahdi_registration_mutex already taken\n");
return;
}
for (i = 0; i < MAX_XPDS; i++) {
xpd_t *xpd = xpd_of(xbus, i);
xpd_dahdi_preunregister(xpd);
@@ -1033,6 +1050,7 @@ void xbus_unregister_dahdi_device(xbus_t *xbus)
xpd_t *xpd = xpd_of(xbus, i);
xpd_dahdi_postunregister(xpd);
}
mutex_unlock(&dahdi_registration_mutex);
}
/*
@@ -1592,8 +1610,6 @@ static bool xpds_done(xbus_t *xbus)
{
if (XBUS_IS(xbus, FAIL))
return 1; /* Nothing to wait for */
if (!XBUS_IS(xbus, RECVD_DESC))
return 1; /* We are not in the initialization phase */
if (xbus->worker.xpds_init_done)
return 1; /* All good */
/* Keep waiting */
@@ -1622,14 +1638,9 @@ int waitfor_xpds(xbus_t *xbus, char *buf)
len = -ENODEV;
goto out;
}
if (worker->num_units == 0) {
XBUS_ERR(xbus, "No cards. Skipping.\n");
goto out;
}
XBUS_DBG(DEVICES, xbus,
"Waiting for card init of %d XPD's max %d seconds (%p)\n",
worker->num_units, INITIALIZATION_TIMEOUT / HZ,
&worker->wait_for_xpd_initialization);
"Waiting for card init of XPDs max %d seconds\n",
INITIALIZATION_TIMEOUT / HZ);
ret =
wait_event_interruptible_timeout(worker->
wait_for_xpd_initialization,

View File

@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#ifdef PROTOCOL_DEBUG
#include <linux/ctype.h>
@@ -582,6 +583,8 @@ static DEVICE_ATTR_READER(span_show, dev, buf)
return len;
}
static DEFINE_MUTEX(span_store_mutex);
/*
* For backward compatibility with old dahdi-tools
* Remove after dahdi_registration is upgraded
@@ -603,6 +606,11 @@ static DEVICE_ATTR_WRITER(span_store, dev, buf, count)
return -ENODEV;
XPD_DBG(DEVICES, xpd, "%s -- deprecated (should use pinned-spans)\n",
(dahdi_reg) ? "register" : "unregister");
ret = mutex_lock_interruptible(&span_store_mutex);
if (ret < 0) {
XBUS_ERR(xpd->xbus, "span_store_mutex already taken\n");
return ret;
}
if (xbus_is_registered(xpd->xbus)) {
if (dahdi_reg) {
XPD_DBG(DEVICES, xpd,
@@ -620,6 +628,7 @@ static DEVICE_ATTR_WRITER(span_store, dev, buf, count)
xbus_register_dahdi_device(xpd->xbus);
}
}
mutex_unlock(&span_store_mutex);
return count;
}

View File

@@ -40,11 +40,10 @@
/* #define SHORT_FLASH_TIME */
/*
* Uncomment to disable calibration and/or DC/DC converter tests
* Uncomment to disable calibration tests
* (not generally recommended)
*/
/* #define NO_CALIBRATION */
/* #define NO_DCDC */
/*
* Boost ring voltage (Higher ring voltage, takes more power)

View File

@@ -1411,6 +1411,25 @@ static inline void *PDE_DATA(const struct inode *inode)
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
#define KERN_CONT ""
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
# ifndef RHEL_RELEASE_VERSION
/* I'm not sure which 5.x release this was backported into. */
static inline int try_wait_for_completion(struct completion *x)
{
unsigned long flags;
int ret = 1;
spin_lock_irqsave(&x->wait.lock, flags);
if (!x->done)
ret = 0;
else
x->done--;
spin_unlock_irqrestore(&x->wait.lock, flags);
return ret;
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
@@ -1450,8 +1469,11 @@ void dahdi_pci_disable_link_state(struct pci_dev *pdev, int state);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
#ifndef __packed
#define __packed __attribute__((packed))
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
#ifndef __packed
#define __packed __attribute__((packed))
#endif
#include <linux/ctype.h>
@@ -1479,6 +1501,7 @@ static inline int strcasecmp(const char *s1, const char *s2)
#endif /* 2.6.22 */
#endif /* 2.6.25 */
#endif /* 2.6.26 */
#endif /* 2.6.27 */
#endif /* 2.6.31 */
#endif /* 3.10.0 */
@@ -1571,6 +1594,17 @@ struct mutex {
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
#endif
/* If KBUILD_MODNAME is not defined in a compilation unit, then the dev_dbg
* macro will not work properly. */
#ifndef KBUILD_MODNAME
#undef dev_dbg
#ifdef DEBUG
#define dev_dbg dev_info
#else
#define dev_dbg(...) do { } while (0)
#endif
#endif
/* The dbg_* ones use a magical variable 'debug' and the user should be
* aware of that.
*/