mirror of https://gitee.com/openkylin/linux.git
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1958 commits) net: pack skb_shared_info more efficiently net_sched: red: split red_parms into parms and vars net_sched: sfq: extend limits cnic: Improve error recovery on bnx2x devices cnic: Re-init dev->stats_addr after chip reset net_sched: Bug in netem reordering bna: fix sparse warnings/errors bna: make ethtool_ops and strings const xgmac: cleanups net: make ethtool_ops const vmxnet3" make ethtool ops const xen-netback: make ops structs const virtio_net: Pass gfp flags when allocating rx buffers. ixgbe: FCoE: Add support for ndo_get_fcoe_hbainfo() call netdev: FCoE: Add new ndo_get_fcoe_hbainfo() call igb: reset PHY after recovering from PHY power down igb: add basic runtime PM support igb: Add support for byte queue limits. e1000: cleanup CE4100 MDIO registers access e1000: unmap ce4100_gbe_mdio_base_virt in e1000_remove ...
This commit is contained in:
commit
9753dfe19a
|
@ -44,8 +44,8 @@ Features:
|
|||
- oom-killer disable knob and oom-notifier
|
||||
- Root cgroup has no limit controls.
|
||||
|
||||
Kernel memory and Hugepages are not under control yet. We just manage
|
||||
pages on LRU. To add more controls, we have to take care of performance.
|
||||
Kernel memory support is work in progress, and the current version provides
|
||||
basically functionality. (See Section 2.7)
|
||||
|
||||
Brief summary of control files.
|
||||
|
||||
|
@ -72,6 +72,9 @@ Brief summary of control files.
|
|||
memory.oom_control # set/show oom controls.
|
||||
memory.numa_stat # show the number of memory usage per numa node
|
||||
|
||||
memory.kmem.tcp.limit_in_bytes # set/show hard limit for tcp buf memory
|
||||
memory.kmem.tcp.usage_in_bytes # show current tcp buf memory allocation
|
||||
|
||||
1. History
|
||||
|
||||
The memory controller has a long history. A request for comments for the memory
|
||||
|
@ -255,6 +258,27 @@ When oom event notifier is registered, event will be delivered.
|
|||
per-zone-per-cgroup LRU (cgroup's private LRU) is just guarded by
|
||||
zone->lru_lock, it has no lock of its own.
|
||||
|
||||
2.7 Kernel Memory Extension (CONFIG_CGROUP_MEM_RES_CTLR_KMEM)
|
||||
|
||||
With the Kernel memory extension, the Memory Controller is able to limit
|
||||
the amount of kernel memory used by the system. Kernel memory is fundamentally
|
||||
different than user memory, since it can't be swapped out, which makes it
|
||||
possible to DoS the system by consuming too much of this precious resource.
|
||||
|
||||
Kernel memory limits are not imposed for the root cgroup. Usage for the root
|
||||
cgroup may or may not be accounted.
|
||||
|
||||
Currently no soft limit is implemented for kernel memory. It is future work
|
||||
to trigger slab reclaim when those limits are reached.
|
||||
|
||||
2.7.1 Current Kernel Memory resources accounted
|
||||
|
||||
* sockets memory pressure: some sockets protocols have memory pressure
|
||||
thresholds. The Memory Controller allows them to be controlled individually
|
||||
per cgroup, instead of globally.
|
||||
|
||||
* tcp memory pressure: sockets memory pressure for the tcp protocol.
|
||||
|
||||
3. User Interface
|
||||
|
||||
0. Configuration
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
Network priority cgroup
|
||||
-------------------------
|
||||
|
||||
The Network priority cgroup provides an interface to allow an administrator to
|
||||
dynamically set the priority of network traffic generated by various
|
||||
applications
|
||||
|
||||
Nominally, an application would set the priority of its traffic via the
|
||||
SO_PRIORITY socket option. This however, is not always possible because:
|
||||
|
||||
1) The application may not have been coded to set this value
|
||||
2) The priority of application traffic is often a site-specific administrative
|
||||
decision rather than an application defined one.
|
||||
|
||||
This cgroup allows an administrator to assign a process to a group which defines
|
||||
the priority of egress traffic on a given interface. Network priority groups can
|
||||
be created by first mounting the cgroup filesystem.
|
||||
|
||||
# mount -t cgroup -onet_prio none /sys/fs/cgroup/net_prio
|
||||
|
||||
With the above step, the initial group acting as the parent accounting group
|
||||
becomes visible at '/sys/fs/cgroup/net_prio'. This group includes all tasks in
|
||||
the system. '/sys/fs/cgroup/net_prio/tasks' lists the tasks in this cgroup.
|
||||
|
||||
Each net_prio cgroup contains two files that are subsystem specific
|
||||
|
||||
net_prio.prioidx
|
||||
This file is read-only, and is simply informative. It contains a unique integer
|
||||
value that the kernel uses as an internal representation of this cgroup.
|
||||
|
||||
net_prio.ifpriomap
|
||||
This file contains a map of the priorities assigned to traffic originating from
|
||||
processes in this group and egressing the system on various interfaces. It
|
||||
contains a list of tuples in the form <ifname priority>. Contents of this file
|
||||
can be modified by echoing a string into the file using the same tuple format.
|
||||
for example:
|
||||
|
||||
echo "eth0 5" > /sys/fs/cgroups/net_prio/iscsi/net_prio.ifpriomap
|
||||
|
||||
This command would force any traffic originating from processes belonging to the
|
||||
iscsi net_prio cgroup and egressing on interface eth0 to have the priority of
|
||||
said traffic set to the value 5. The parent accounting group also has a
|
||||
writeable 'net_prio.ifpriomap' file that can be used to set a system default
|
||||
priority.
|
||||
|
||||
Priorities are set immediately prior to queueing a frame to the device
|
||||
queueing discipline (qdisc) so priorities will be assigned prior to the hardware
|
||||
queue selection being made.
|
||||
|
||||
One usage for the net_prio cgroup is with mqprio qdisc allowing application
|
||||
traffic to be steered to hardware/driver based traffic classes. These mappings
|
||||
can then be managed by administrators or other networking protocols such as
|
||||
DCBX.
|
|
@ -0,0 +1,15 @@
|
|||
* Calxeda Highbank 10Gb XGMAC Ethernet
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "calxeda,hb-xgmac"
|
||||
- reg : Address and length of the register set for the device
|
||||
- interrupts : Should contain 3 xgmac interrupts. The 1st is main interrupt.
|
||||
The 2nd is pwr mgt interrupt. The 3rd is low power state interrupt.
|
||||
|
||||
Example:
|
||||
|
||||
ethernet@fff50000 {
|
||||
compatible = "calxeda,hb-xgmac";
|
||||
reg = <0xfff50000 0x1000>;
|
||||
interrupts = <0 77 4 0 78 4 0 79 4>;
|
||||
};
|
|
@ -0,0 +1,53 @@
|
|||
Memory mapped Bosch CC770 and Intel AN82527 CAN controller
|
||||
|
||||
Note: The CC770 is a CAN controller from Bosch, which is 100%
|
||||
compatible with the old AN82527 from Intel, but with "bugs" being fixed.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "bosch,cc770" for the CC770 and "intc,82527"
|
||||
for the AN82527.
|
||||
|
||||
- reg : should specify the chip select, address offset and size required
|
||||
to map the registers of the controller. The size is usually 0x80.
|
||||
|
||||
- interrupts : property with a value describing the interrupt source
|
||||
(number and sensitivity) required for the controller.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- bosch,external-clock-frequency : frequency of the external oscillator
|
||||
clock in Hz. Note that the internal clock frequency used by the
|
||||
controller is half of that value. If not specified, a default
|
||||
value of 16000000 (16 MHz) is used.
|
||||
|
||||
- bosch,clock-out-frequency : slock frequency in Hz on the CLKOUT pin.
|
||||
If not specified or if the specified value is 0, the CLKOUT pin
|
||||
will be disabled.
|
||||
|
||||
- bosch,slew-rate : slew rate of the CLKOUT signal. If not specified,
|
||||
a resonable value will be calculated.
|
||||
|
||||
- bosch,disconnect-rx0-input : see data sheet.
|
||||
|
||||
- bosch,disconnect-rx1-input : see data sheet.
|
||||
|
||||
- bosch,disconnect-tx1-output : see data sheet.
|
||||
|
||||
- bosch,polarity-dominant : see data sheet.
|
||||
|
||||
- bosch,divide-memory-clock : see data sheet.
|
||||
|
||||
- bosch,iso-low-speed-mux : see data sheet.
|
||||
|
||||
For further information, please have a look to the CC770 or AN82527.
|
||||
|
||||
Examples:
|
||||
|
||||
can@3,100 {
|
||||
compatible = "bosch,cc770";
|
||||
reg = <3 0x100 0x80>;
|
||||
interrupts = <2 0>;
|
||||
interrupt-parent = <&mpic>;
|
||||
bosch,external-clock-frequency = <16000000>;
|
||||
};
|
|
@ -263,8 +263,7 @@ Who: Ravikiran Thirumalai <kiran@scalex86.org>
|
|||
|
||||
What: Code that is now under CONFIG_WIRELESS_EXT_SYSFS
|
||||
(in net/core/net-sysfs.c)
|
||||
When: After the only user (hal) has seen a release with the patches
|
||||
for enough time, probably some time in 2010.
|
||||
When: 3.5
|
||||
Why: Over 1K .text/.data size reduction, data is available in other
|
||||
ways (ioctls)
|
||||
Who: Johannes Berg <johannes@sipsolutions.net>
|
||||
|
|
|
@ -144,6 +144,8 @@ nfc.txt
|
|||
- The Linux Near Field Communication (NFS) subsystem.
|
||||
olympic.txt
|
||||
- IBM PCI Pit/Pit-Phy/Olympic Token Ring driver info.
|
||||
openvswitch.txt
|
||||
- Open vSwitch developer documentation.
|
||||
operstates.txt
|
||||
- Overview of network interface operational states.
|
||||
packet_mmap.txt
|
||||
|
|
|
@ -200,15 +200,16 @@ abled during run time. Following log_levels are defined:
|
|||
|
||||
0 - All debug output disabled
|
||||
1 - Enable messages related to routing / flooding / broadcasting
|
||||
2 - Enable route or tt entry added / changed / deleted
|
||||
3 - Enable all messages
|
||||
2 - Enable messages related to route added / changed / deleted
|
||||
4 - Enable messages related to translation table operations
|
||||
7 - Enable all messages
|
||||
|
||||
The debug output can be changed at runtime using the file
|
||||
/sys/class/net/bat0/mesh/log_level. e.g.
|
||||
|
||||
# echo 2 > /sys/class/net/bat0/mesh/log_level
|
||||
|
||||
will enable debug messages for when routes or TTs change.
|
||||
will enable debug messages for when routes change.
|
||||
|
||||
|
||||
BATCTL
|
||||
|
|
|
@ -196,6 +196,23 @@ or, for backwards compatibility, the option value. E.g.,
|
|||
|
||||
The parameters are as follows:
|
||||
|
||||
active_slave
|
||||
|
||||
Specifies the new active slave for modes that support it
|
||||
(active-backup, balance-alb and balance-tlb). Possible values
|
||||
are the name of any currently enslaved interface, or an empty
|
||||
string. If a name is given, the slave and its link must be up in order
|
||||
to be selected as the new active slave. If an empty string is
|
||||
specified, the current active slave is cleared, and a new active
|
||||
slave is selected automatically.
|
||||
|
||||
Note that this is only available through the sysfs interface. No module
|
||||
parameter by this name exists.
|
||||
|
||||
The normal value of this option is the name of the currently
|
||||
active slave, or the empty string if there is no active slave or
|
||||
the current mode does not use an active slave.
|
||||
|
||||
ad_select
|
||||
|
||||
Specifies the 802.3ad aggregation selection logic to use. The
|
||||
|
|
|
@ -78,3 +78,30 @@ in software. This is currently WIP.
|
|||
|
||||
See header include/net/mac802154.h and several drivers in drivers/ieee802154/.
|
||||
|
||||
6LoWPAN Linux implementation
|
||||
============================
|
||||
|
||||
The IEEE 802.15.4 standard specifies an MTU of 128 bytes, yielding about 80
|
||||
octets of actual MAC payload once security is turned on, on a wireless link
|
||||
with a link throughput of 250 kbps or less. The 6LoWPAN adaptation format
|
||||
[RFC4944] was specified to carry IPv6 datagrams over such constrained links,
|
||||
taking into account limited bandwidth, memory, or energy resources that are
|
||||
expected in applications such as wireless Sensor Networks. [RFC4944] defines
|
||||
a Mesh Addressing header to support sub-IP forwarding, a Fragmentation header
|
||||
to support the IPv6 minimum MTU requirement [RFC2460], and stateless header
|
||||
compression for IPv6 datagrams (LOWPAN_HC1 and LOWPAN_HC2) to reduce the
|
||||
relatively large IPv6 and UDP headers down to (in the best case) several bytes.
|
||||
|
||||
In Semptember 2011 the standard update was published - [RFC6282].
|
||||
It deprecates HC1 and HC2 compression and defines IPHC encoding format which is
|
||||
used in this Linux implementation.
|
||||
|
||||
All the code related to 6lowpan you may find in files: net/ieee802154/6lowpan.*
|
||||
|
||||
To setup 6lowpan interface you need (busybox release > 1.17.0):
|
||||
1. Add IEEE802.15.4 interface and initialize PANid;
|
||||
2. Add 6lowpan interface by command like:
|
||||
# ip link add link wpan0 name lowpan0 type lowpan
|
||||
3. Set MAC (if needs):
|
||||
# ip link set lowpan0 address de:ad:be:ef:ca:fe:ba:be
|
||||
4. Bring up 'lowpan0' interface
|
||||
|
|
|
@ -539,12 +539,14 @@ static int if_getconfig(char *ifname)
|
|||
metric = 0;
|
||||
} else
|
||||
metric = ifr.ifr_metric;
|
||||
printf("The result of SIOCGIFMETRIC is %d\n", metric);
|
||||
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0)
|
||||
mtu = 0;
|
||||
else
|
||||
mtu = ifr.ifr_mtu;
|
||||
printf("The result of SIOCGIFMTU is %d\n", mtu);
|
||||
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) < 0) {
|
||||
|
|
|
@ -31,6 +31,16 @@ neigh/default/gc_thresh3 - INTEGER
|
|||
when using large numbers of interfaces and when communicating
|
||||
with large numbers of directly-connected peers.
|
||||
|
||||
neigh/default/unres_qlen_bytes - INTEGER
|
||||
The maximum number of bytes which may be used by packets
|
||||
queued for each unresolved address by other network layers.
|
||||
(added in linux 3.3)
|
||||
|
||||
neigh/default/unres_qlen - INTEGER
|
||||
The maximum number of packets which may be queued for each
|
||||
unresolved address by other network layers.
|
||||
(deprecated in linux 3.3) : use unres_qlen_bytes instead.
|
||||
|
||||
mtu_expires - INTEGER
|
||||
Time, in seconds, that cached PMTU information is kept.
|
||||
|
||||
|
@ -165,6 +175,9 @@ tcp_congestion_control - STRING
|
|||
connections. The algorithm "reno" is always available, but
|
||||
additional choices may be available based on kernel configuration.
|
||||
Default is set as part of kernel configuration.
|
||||
For passive connections, the listener congestion control choice
|
||||
is inherited.
|
||||
[see setsockopt(listenfd, SOL_TCP, TCP_CONGESTION, "name" ...) ]
|
||||
|
||||
tcp_cookie_size - INTEGER
|
||||
Default size of TCP Cookie Transactions (TCPCT) option, that may be
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
Open vSwitch datapath developer documentation
|
||||
=============================================
|
||||
|
||||
The Open vSwitch kernel module allows flexible userspace control over
|
||||
flow-level packet processing on selected network devices. It can be
|
||||
used to implement a plain Ethernet switch, network device bonding,
|
||||
VLAN processing, network access control, flow-based network control,
|
||||
and so on.
|
||||
|
||||
The kernel module implements multiple "datapaths" (analogous to
|
||||
bridges), each of which can have multiple "vports" (analogous to ports
|
||||
within a bridge). Each datapath also has associated with it a "flow
|
||||
table" that userspace populates with "flows" that map from keys based
|
||||
on packet headers and metadata to sets of actions. The most common
|
||||
action forwards the packet to another vport; other actions are also
|
||||
implemented.
|
||||
|
||||
When a packet arrives on a vport, the kernel module processes it by
|
||||
extracting its flow key and looking it up in the flow table. If there
|
||||
is a matching flow, it executes the associated actions. If there is
|
||||
no match, it queues the packet to userspace for processing (as part of
|
||||
its processing, userspace will likely set up a flow to handle further
|
||||
packets of the same type entirely in-kernel).
|
||||
|
||||
|
||||
Flow key compatibility
|
||||
----------------------
|
||||
|
||||
Network protocols evolve over time. New protocols become important
|
||||
and existing protocols lose their prominence. For the Open vSwitch
|
||||
kernel module to remain relevant, it must be possible for newer
|
||||
versions to parse additional protocols as part of the flow key. It
|
||||
might even be desirable, someday, to drop support for parsing
|
||||
protocols that have become obsolete. Therefore, the Netlink interface
|
||||
to Open vSwitch is designed to allow carefully written userspace
|
||||
applications to work with any version of the flow key, past or future.
|
||||
|
||||
To support this forward and backward compatibility, whenever the
|
||||
kernel module passes a packet to userspace, it also passes along the
|
||||
flow key that it parsed from the packet. Userspace then extracts its
|
||||
own notion of a flow key from the packet and compares it against the
|
||||
kernel-provided version:
|
||||
|
||||
- If userspace's notion of the flow key for the packet matches the
|
||||
kernel's, then nothing special is necessary.
|
||||
|
||||
- If the kernel's flow key includes more fields than the userspace
|
||||
version of the flow key, for example if the kernel decoded IPv6
|
||||
headers but userspace stopped at the Ethernet type (because it
|
||||
does not understand IPv6), then again nothing special is
|
||||
necessary. Userspace can still set up a flow in the usual way,
|
||||
as long as it uses the kernel-provided flow key to do it.
|
||||
|
||||
- If the userspace flow key includes more fields than the
|
||||
kernel's, for example if userspace decoded an IPv6 header but
|
||||
the kernel stopped at the Ethernet type, then userspace can
|
||||
forward the packet manually, without setting up a flow in the
|
||||
kernel. This case is bad for performance because every packet
|
||||
that the kernel considers part of the flow must go to userspace,
|
||||
but the forwarding behavior is correct. (If userspace can
|
||||
determine that the values of the extra fields would not affect
|
||||
forwarding behavior, then it could set up a flow anyway.)
|
||||
|
||||
How flow keys evolve over time is important to making this work, so
|
||||
the following sections go into detail.
|
||||
|
||||
|
||||
Flow key format
|
||||
---------------
|
||||
|
||||
A flow key is passed over a Netlink socket as a sequence of Netlink
|
||||
attributes. Some attributes represent packet metadata, defined as any
|
||||
information about a packet that cannot be extracted from the packet
|
||||
itself, e.g. the vport on which the packet was received. Most
|
||||
attributes, however, are extracted from headers within the packet,
|
||||
e.g. source and destination addresses from Ethernet, IP, or TCP
|
||||
headers.
|
||||
|
||||
The <linux/openvswitch.h> header file defines the exact format of the
|
||||
flow key attributes. For informal explanatory purposes here, we write
|
||||
them as comma-separated strings, with parentheses indicating arguments
|
||||
and nesting. For example, the following could represent a flow key
|
||||
corresponding to a TCP packet that arrived on vport 1:
|
||||
|
||||
in_port(1), eth(src=e0:91:f5:21:d0:b2, dst=00:02:e3:0f:80:a4),
|
||||
eth_type(0x0800), ipv4(src=172.16.0.20, dst=172.18.0.52, proto=17, tos=0,
|
||||
frag=no), tcp(src=49163, dst=80)
|
||||
|
||||
Often we ellipsize arguments not important to the discussion, e.g.:
|
||||
|
||||
in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...)
|
||||
|
||||
|
||||
Basic rule for evolving flow keys
|
||||
---------------------------------
|
||||
|
||||
Some care is needed to really maintain forward and backward
|
||||
compatibility for applications that follow the rules listed under
|
||||
"Flow key compatibility" above.
|
||||
|
||||
The basic rule is obvious:
|
||||
|
||||
------------------------------------------------------------------
|
||||
New network protocol support must only supplement existing flow
|
||||
key attributes. It must not change the meaning of already defined
|
||||
flow key attributes.
|
||||
------------------------------------------------------------------
|
||||
|
||||
This rule does have less-obvious consequences so it is worth working
|
||||
through a few examples. Suppose, for example, that the kernel module
|
||||
did not already implement VLAN parsing. Instead, it just interpreted
|
||||
the 802.1Q TPID (0x8100) as the Ethertype then stopped parsing the
|
||||
packet. The flow key for any packet with an 802.1Q header would look
|
||||
essentially like this, ignoring metadata:
|
||||
|
||||
eth(...), eth_type(0x8100)
|
||||
|
||||
Naively, to add VLAN support, it makes sense to add a new "vlan" flow
|
||||
key attribute to contain the VLAN tag, then continue to decode the
|
||||
encapsulated headers beyond the VLAN tag using the existing field
|
||||
definitions. With this change, an TCP packet in VLAN 10 would have a
|
||||
flow key much like this:
|
||||
|
||||
eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...)
|
||||
|
||||
But this change would negatively affect a userspace application that
|
||||
has not been updated to understand the new "vlan" flow key attribute.
|
||||
The application could, following the flow compatibility rules above,
|
||||
ignore the "vlan" attribute that it does not understand and therefore
|
||||
assume that the flow contained IP packets. This is a bad assumption
|
||||
(the flow only contains IP packets if one parses and skips over the
|
||||
802.1Q header) and it could cause the application's behavior to change
|
||||
across kernel versions even though it follows the compatibility rules.
|
||||
|
||||
The solution is to use a set of nested attributes. This is, for
|
||||
example, why 802.1Q support uses nested attributes. A TCP packet in
|
||||
VLAN 10 is actually expressed as:
|
||||
|
||||
eth(...), eth_type(0x8100), vlan(vid=10, pcp=0), encap(eth_type(0x0800),
|
||||
ip(proto=6, ...), tcp(...)))
|
||||
|
||||
Notice how the "eth_type", "ip", and "tcp" flow key attributes are
|
||||
nested inside the "encap" attribute. Thus, an application that does
|
||||
not understand the "vlan" key will not see either of those attributes
|
||||
and therefore will not misinterpret them. (Also, the outer eth_type
|
||||
is still 0x8100, not changed to 0x0800.)
|
||||
|
||||
Handling malformed packets
|
||||
--------------------------
|
||||
|
||||
Don't drop packets in the kernel for malformed protocol headers, bad
|
||||
checksums, etc. This would prevent userspace from implementing a
|
||||
simple Ethernet switch that forwards every packet.
|
||||
|
||||
Instead, in such a case, include an attribute with "empty" content.
|
||||
It doesn't matter if the empty content could be valid protocol values,
|
||||
as long as those values are rarely seen in practice, because userspace
|
||||
can always forward all packets with those values to userspace and
|
||||
handle them individually.
|
||||
|
||||
For example, consider a packet that contains an IP header that
|
||||
indicates protocol 6 for TCP, but which is truncated just after the IP
|
||||
header, so that the TCP header is missing. The flow key for this
|
||||
packet would include a tcp attribute with all-zero src and dst, like
|
||||
this:
|
||||
|
||||
eth(...), eth_type(0x0800), ip(proto=6, ...), tcp(src=0, dst=0)
|
||||
|
||||
As another example, consider a packet with an Ethernet type of 0x8100,
|
||||
indicating that a VLAN TCI should follow, but which is truncated just
|
||||
after the Ethernet type. The flow key for this packet would include
|
||||
an all-zero-bits vlan and an empty encap attribute, like this:
|
||||
|
||||
eth(...), eth_type(0x8100), vlan(0), encap()
|
||||
|
||||
Unlike a TCP packet with source and destination ports 0, an
|
||||
all-zero-bits VLAN TCI is not that rare, so the CFI bit (aka
|
||||
VLAN_TAG_PRESENT inside the kernel) is ordinarily set in a vlan
|
||||
attribute expressly to allow this situation to be distinguished.
|
||||
Thus, the flow key in this second example unambiguously indicates a
|
||||
missing or malformed VLAN TCI.
|
||||
|
||||
Other rules
|
||||
-----------
|
||||
|
||||
The other rules for flow keys are much less subtle:
|
||||
|
||||
- Duplicate attributes are not allowed at a given nesting level.
|
||||
|
||||
- Ordering of attributes is not significant.
|
||||
|
||||
- When the kernel sends a given flow key to userspace, it always
|
||||
composes it the same way. This allows userspace to hash and
|
||||
compare entire flow keys that it may not be able to fully
|
||||
interpret.
|
|
@ -155,7 +155,7 @@ As capture, each frame contains two parts:
|
|||
|
||||
/* fill sockaddr_ll struct to prepare binding */
|
||||
my_addr.sll_family = AF_PACKET;
|
||||
my_addr.sll_protocol = ETH_P_ALL;
|
||||
my_addr.sll_protocol = htons(ETH_P_ALL);
|
||||
my_addr.sll_ifindex = s_ifr.ifr_ifindex;
|
||||
|
||||
/* bind socket to eth0 */
|
||||
|
|
|
@ -208,7 +208,7 @@ The counter in rps_dev_flow_table values records the length of the current
|
|||
CPU's backlog when a packet in this flow was last enqueued. Each backlog
|
||||
queue has a head counter that is incremented on dequeue. A tail counter
|
||||
is computed as head counter + queue length. In other words, the counter
|
||||
in rps_dev_flow_table[i] records the last element in flow i that has
|
||||
in rps_dev_flow[i] records the last element in flow i that has
|
||||
been enqueued onto the currently designated CPU for flow i (of course,
|
||||
entry i is actually selected by hash and multiple flows may hash to the
|
||||
same entry i).
|
||||
|
@ -224,7 +224,7 @@ following is true:
|
|||
|
||||
- The current CPU's queue head counter >= the recorded tail counter
|
||||
value in rps_dev_flow[i]
|
||||
- The current CPU is unset (equal to NR_CPUS)
|
||||
- The current CPU is unset (equal to RPS_NO_CPU)
|
||||
- The current CPU is offline
|
||||
|
||||
After this check, the packet is sent to the (possibly updated) current
|
||||
|
@ -235,7 +235,7 @@ CPU.
|
|||
|
||||
==== RFS Configuration
|
||||
|
||||
RFS is only available if the kconfig symbol CONFIG_RFS is enabled (on
|
||||
RFS is only available if the kconfig symbol CONFIG_RPS is enabled (on
|
||||
by default for SMP). The functionality remains disabled until explicitly
|
||||
configured. The number of entries in the global flow table is set through:
|
||||
|
||||
|
@ -258,7 +258,7 @@ For a single queue device, the rps_flow_cnt value for the single queue
|
|||
would normally be configured to the same value as rps_sock_flow_entries.
|
||||
For a multi-queue device, the rps_flow_cnt for each queue might be
|
||||
configured as rps_sock_flow_entries / N, where N is the number of
|
||||
queues. So for instance, if rps_flow_entries is set to 32768 and there
|
||||
queues. So for instance, if rps_sock_flow_entries is set to 32768 and there
|
||||
are 16 configured receive queues, rps_flow_cnt for each queue might be
|
||||
configured as 2048.
|
||||
|
||||
|
|
|
@ -4,14 +4,16 @@ Copyright (C) 2007-2010 STMicroelectronics Ltd
|
|||
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
|
||||
|
||||
This is the driver for the MAC 10/100/1000 on-chip Ethernet controllers
|
||||
(Synopsys IP blocks); it has been fully tested on STLinux platforms.
|
||||
(Synopsys IP blocks).
|
||||
|
||||
Currently this network device driver is for all STM embedded MAC/GMAC
|
||||
(i.e. 7xxx/5xxx SoCs) and it's known working on other platforms i.e. ARM SPEAr.
|
||||
(i.e. 7xxx/5xxx SoCs), SPEAr (arm), Loongson1B (mips) and XLINX XC2V3000
|
||||
FF1152AMT0221 D1215994A VIRTEX FPGA board.
|
||||
|
||||
DWC Ether MAC 10/100/1000 Universal version 3.41a and DWC Ether MAC 10/100
|
||||
Universal version 4.0 have been used for developing the first code
|
||||
implementation.
|
||||
DWC Ether MAC 10/100/1000 Universal version 3.60a (and older) and DWC Ether MAC 10/100
|
||||
Universal version 4.0 have been used for developing this driver.
|
||||
|
||||
This driver supports both the platform bus and PCI.
|
||||
|
||||
Please, for more information also visit: www.stlinux.com
|
||||
|
||||
|
@ -277,5 +279,5 @@ In fact, these can generate an huge amount of debug messages.
|
|||
|
||||
6) TODO:
|
||||
o XGMAC is not supported.
|
||||
o Review the timer optimisation code to use an embedded device that will be
|
||||
available in new chip generations.
|
||||
o Add the EEE - Energy Efficient Ethernet
|
||||
o Add the PTP - precision time protocol
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Team devices are driven from userspace via libteam library which is here:
|
||||
https://github.com/jpirko/libteam
|
17
MAINTAINERS
17
MAINTAINERS
|
@ -4854,6 +4854,14 @@ S: Maintained
|
|||
T: git git://openrisc.net/~jonas/linux
|
||||
F: arch/openrisc
|
||||
|
||||
OPENVSWITCH
|
||||
M: Jesse Gross <jesse@nicira.com>
|
||||
L: dev@openvswitch.org
|
||||
W: http://openvswitch.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jesse/openvswitch.git
|
||||
S: Maintained
|
||||
F: net/openvswitch/
|
||||
|
||||
OPL4 DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
|
@ -5373,6 +5381,7 @@ S: Supported
|
|||
F: drivers/scsi/qla4xxx/
|
||||
|
||||
QLOGIC QLA3XXX NETWORK DRIVER
|
||||
M: Jitendra Kalsaria <jitendra.kalsaria@qlogic.com>
|
||||
M: Ron Mercer <ron.mercer@qlogic.com>
|
||||
M: linux-driver@qlogic.com
|
||||
L: netdev@vger.kernel.org
|
||||
|
@ -5892,7 +5901,6 @@ F: drivers/net/ethernet/emulex/benet/
|
|||
|
||||
SFC NETWORK DRIVER
|
||||
M: Solarflare linux maintainers <linux-net-drivers@solarflare.com>
|
||||
M: Steve Hodgson <shodgson@solarflare.com>
|
||||
M: Ben Hutchings <bhutchings@solarflare.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
|
@ -6500,6 +6508,13 @@ W: http://tcp-lp-mod.sourceforge.net/
|
|||
S: Maintained
|
||||
F: net/ipv4/tcp_lp.c
|
||||
|
||||
TEAM DRIVER
|
||||
M: Jiri Pirko <jpirko@redhat.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/net/team/
|
||||
F: include/linux/if_team.h
|
||||
|
||||
TEGRA SUPPORT
|
||||
M: Colin Cross <ccross@android.com>
|
||||
M: Olof Johansson <olof@lixom.net>
|
||||
|
|
|
@ -69,6 +69,9 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
/* O_NONBLOCK clashes with the bits used for socket types. Therefore we
|
||||
* have to define SOCK_NONBLOCK to a different value here.
|
||||
*/
|
||||
|
|
|
@ -62,4 +62,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
|
|
@ -62,4 +62,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* __ASM_AVR32_SOCKET_H */
|
||||
|
|
|
@ -64,6 +64,9 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
||||
|
||||
|
|
|
@ -62,5 +62,8 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
||||
|
|
|
@ -62,4 +62,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
|
|
@ -71,4 +71,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_IA64_SOCKET_H */
|
||||
|
|
|
@ -62,4 +62,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_M32R_SOCKET_H */
|
||||
|
|
|
@ -62,4 +62,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
|
|
@ -95,7 +95,7 @@ struct mace_video {
|
|||
* Ethernet interface
|
||||
*/
|
||||
struct mace_ethernet {
|
||||
volatile unsigned long mac_ctrl;
|
||||
volatile u64 mac_ctrl;
|
||||
volatile unsigned long int_stat;
|
||||
volatile unsigned long dma_ctrl;
|
||||
volatile unsigned long timer;
|
||||
|
|
|
@ -82,6 +82,9 @@ To add: #define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/** sock_type - Socket types
|
||||
|
|
|
@ -62,4 +62,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
|
|
@ -61,6 +61,9 @@
|
|||
|
||||
#define SO_RXQ_OVFL 0x4021
|
||||
|
||||
#define SO_WIFI_STATUS 0x4022
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
/* O_NONBLOCK clashes with the bits used for socket types. Therefore we
|
||||
* have to define SOCK_NONBLOCK to a different value here.
|
||||
*/
|
||||
|
|
|
@ -352,7 +352,7 @@ localbus@a0005000 {
|
|||
ranges = <
|
||||
0 0x0 0xfc000000 0x04000000 // NOR FLASH bank 1
|
||||
1 0x0 0xf8000000 0x08000000 // NOR FLASH bank 0
|
||||
2 0x0 0xa3000000 0x00008000 // CAN (2 x i82527)
|
||||
2 0x0 0xa3000000 0x00008000 // CAN (2 x CC770)
|
||||
3 0x0 0xa3010000 0x00008000 // NAND FLASH
|
||||
|
||||
>;
|
||||
|
@ -393,18 +393,27 @@ partition@7f80000 {
|
|||
};
|
||||
|
||||
/* Note: CAN support needs be enabled in U-Boot */
|
||||
can0@2,0 {
|
||||
compatible = "intel,82527"; // Bosch CC770
|
||||
can@2,0 {
|
||||
compatible = "bosch,cc770"; // Bosch CC770
|
||||
reg = <2 0x0 0x100>;
|
||||
interrupts = <4 1>;
|
||||
interrupt-parent = <&mpic>;
|
||||
bosch,external-clock-frequency = <16000000>;
|
||||
bosch,disconnect-rx1-input;
|
||||
bosch,disconnect-tx1-output;
|
||||
bosch,iso-low-speed-mux;
|
||||
bosch,clock-out-frequency = <16000000>;
|
||||
};
|
||||
|
||||
can1@2,100 {
|
||||
compatible = "intel,82527"; // Bosch CC770
|
||||
can@2,100 {
|
||||
compatible = "bosch,cc770"; // Bosch CC770
|
||||
reg = <2 0x100 0x100>;
|
||||
interrupts = <4 1>;
|
||||
interrupt-parent = <&mpic>;
|
||||
bosch,external-clock-frequency = <16000000>;
|
||||
bosch,disconnect-rx1-input;
|
||||
bosch,disconnect-tx1-output;
|
||||
bosch,iso-low-speed-mux;
|
||||
};
|
||||
|
||||
/* Note: NAND support needs to be enabled in U-Boot */
|
||||
|
|
|
@ -352,7 +352,7 @@ localbus@e0005000 {
|
|||
ranges = <
|
||||
0 0x0 0xfc000000 0x04000000 // NOR FLASH bank 1
|
||||
1 0x0 0xf8000000 0x08000000 // NOR FLASH bank 0
|
||||
2 0x0 0xe3000000 0x00008000 // CAN (2 x i82527)
|
||||
2 0x0 0xe3000000 0x00008000 // CAN (2 x CC770)
|
||||
3 0x0 0xe3010000 0x00008000 // NAND FLASH
|
||||
|
||||
>;
|
||||
|
@ -393,18 +393,27 @@ partition@7f80000 {
|
|||
};
|
||||
|
||||
/* Note: CAN support needs be enabled in U-Boot */
|
||||
can0@2,0 {
|
||||
compatible = "intel,82527"; // Bosch CC770
|
||||
can@2,0 {
|
||||
compatible = "bosch,cc770"; // Bosch CC770
|
||||
reg = <2 0x0 0x100>;
|
||||
interrupts = <4 1>;
|
||||
interrupt-parent = <&mpic>;
|
||||
bosch,external-clock-frequency = <16000000>;
|
||||
bosch,disconnect-rx1-input;
|
||||
bosch,disconnect-tx1-output;
|
||||
bosch,iso-low-speed-mux;
|
||||
bosch,clock-out-frequency = <16000000>;
|
||||
};
|
||||
|
||||
can1@2,100 {
|
||||
compatible = "intel,82527"; // Bosch CC770
|
||||
can@2,100 {
|
||||
compatible = "bosch,cc770"; // Bosch CC770
|
||||
reg = <2 0x100 0x100>;
|
||||
interrupts = <4 1>;
|
||||
interrupt-parent = <&mpic>;
|
||||
bosch,external-clock-frequency = <16000000>;
|
||||
bosch,disconnect-rx1-input;
|
||||
bosch,disconnect-tx1-output;
|
||||
bosch,iso-low-speed-mux;
|
||||
};
|
||||
|
||||
/* Note: NAND support needs to be enabled in U-Boot */
|
||||
|
|
|
@ -57,6 +57,7 @@ localbus@fff00100 {
|
|||
|
||||
ranges = <
|
||||
0x0 0x0 0x40000000 0x800000
|
||||
0x3 0x0 0xc0000000 0x200
|
||||
>;
|
||||
|
||||
flash@0,0 {
|
||||
|
@ -67,6 +68,30 @@ flash@0,0 {
|
|||
bank-width = <4>;
|
||||
device-width = <2>;
|
||||
};
|
||||
|
||||
/* Note: CAN support needs be enabled in U-Boot */
|
||||
can@3,0 {
|
||||
compatible = "intc,82527";
|
||||
reg = <3 0x0 0x80>;
|
||||
interrupts = <8 1>;
|
||||
interrupt-parent = <&PIC>;
|
||||
bosch,external-clock-frequency = <16000000>;
|
||||
bosch,disconnect-rx1-input;
|
||||
bosch,disconnect-tx1-output;
|
||||
bosch,iso-low-speed-mux;
|
||||
bosch,clock-out-frequency = <16000000>;
|
||||
};
|
||||
|
||||
can@3,100 {
|
||||
compatible = "intc,82527";
|
||||
reg = <3 0x100 0x80>;
|
||||
interrupts = <8 1>;
|
||||
interrupt-parent = <&PIC>;
|
||||
bosch,external-clock-frequency = <16000000>;
|
||||
bosch,disconnect-rx1-input;
|
||||
bosch,disconnect-tx1-output;
|
||||
bosch,iso-low-speed-mux;
|
||||
};
|
||||
};
|
||||
|
||||
soc@fff00000 {
|
||||
|
|
|
@ -69,4 +69,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_POWERPC_SOCKET_H */
|
||||
|
|
|
@ -70,4 +70,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
|
|
@ -58,6 +58,9 @@
|
|||
|
||||
#define SO_RXQ_OVFL 0x0024
|
||||
|
||||
#define SO_WIFI_STATUS 0x0025
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
/* Security levels - as per NRL IPv6 - don't actually do anything */
|
||||
#define SO_SECURITY_AUTHENTICATION 0x5001
|
||||
#define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002
|
||||
|
|
|
@ -73,4 +73,7 @@
|
|||
|
||||
#define SO_RXQ_OVFL 40
|
||||
|
||||
#define SO_WIFI_STATUS 41
|
||||
#define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
|
||||
#endif /* _XTENSA_SOCKET_H */
|
||||
|
|
|
@ -1320,8 +1320,8 @@ static void rx_dle_intr(struct atm_dev *dev)
|
|||
if (ia_vcc == NULL)
|
||||
{
|
||||
atomic_inc(&vcc->stats->rx_err);
|
||||
atm_return(vcc, skb->truesize);
|
||||
dev_kfree_skb_any(skb);
|
||||
atm_return(vcc, atm_guess_pdu2truesize(len));
|
||||
goto INCR_DLE;
|
||||
}
|
||||
// get real pkt length pwang_test
|
||||
|
@ -1334,8 +1334,8 @@ static void rx_dle_intr(struct atm_dev *dev)
|
|||
atomic_inc(&vcc->stats->rx_err);
|
||||
IF_ERR(printk("rx_dle_intr: Bad AAL5 trailer %d (skb len %d)",
|
||||
length, skb->len);)
|
||||
atm_return(vcc, skb->truesize);
|
||||
dev_kfree_skb_any(skb);
|
||||
atm_return(vcc, atm_guess_pdu2truesize(len));
|
||||
goto INCR_DLE;
|
||||
}
|
||||
skb_trim(skb, length);
|
||||
|
|
|
@ -18,6 +18,9 @@ void bcma_bus_unregister(struct bcma_bus *bus);
|
|||
int __init bcma_bus_early_register(struct bcma_bus *bus,
|
||||
struct bcma_device *core_cc,
|
||||
struct bcma_device *core_mips);
|
||||
#ifdef CONFIG_PM
|
||||
int bcma_bus_resume(struct bcma_bus *bus);
|
||||
#endif
|
||||
|
||||
/* scan.c */
|
||||
int bcma_bus_scan(struct bcma_bus *bus);
|
||||
|
|
|
@ -21,48 +21,58 @@ static void bcma_host_pci_switch_core(struct bcma_device *core)
|
|||
pr_debug("Switched to core: 0x%X\n", core->id.id);
|
||||
}
|
||||
|
||||
static u8 bcma_host_pci_read8(struct bcma_device *core, u16 offset)
|
||||
/* Provides access to the requested core. Returns base offset that has to be
|
||||
* used. It makes use of fixed windows when possible. */
|
||||
static u16 bcma_host_pci_provide_access_to_core(struct bcma_device *core)
|
||||
{
|
||||
switch (core->id.id) {
|
||||
case BCMA_CORE_CHIPCOMMON:
|
||||
return 3 * BCMA_CORE_SIZE;
|
||||
case BCMA_CORE_PCIE:
|
||||
return 2 * BCMA_CORE_SIZE;
|
||||
}
|
||||
|
||||
if (core->bus->mapped_core != core)
|
||||
bcma_host_pci_switch_core(core);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 bcma_host_pci_read8(struct bcma_device *core, u16 offset)
|
||||
{
|
||||
offset += bcma_host_pci_provide_access_to_core(core);
|
||||
return ioread8(core->bus->mmio + offset);
|
||||
}
|
||||
|
||||
static u16 bcma_host_pci_read16(struct bcma_device *core, u16 offset)
|
||||
{
|
||||
if (core->bus->mapped_core != core)
|
||||
bcma_host_pci_switch_core(core);
|
||||
offset += bcma_host_pci_provide_access_to_core(core);
|
||||
return ioread16(core->bus->mmio + offset);
|
||||
}
|
||||
|
||||
static u32 bcma_host_pci_read32(struct bcma_device *core, u16 offset)
|
||||
{
|
||||
if (core->bus->mapped_core != core)
|
||||
bcma_host_pci_switch_core(core);
|
||||
offset += bcma_host_pci_provide_access_to_core(core);
|
||||
return ioread32(core->bus->mmio + offset);
|
||||
}
|
||||
|
||||
static void bcma_host_pci_write8(struct bcma_device *core, u16 offset,
|
||||
u8 value)
|
||||
{
|
||||
if (core->bus->mapped_core != core)
|
||||
bcma_host_pci_switch_core(core);
|
||||
offset += bcma_host_pci_provide_access_to_core(core);
|
||||
iowrite8(value, core->bus->mmio + offset);
|
||||
}
|
||||
|
||||
static void bcma_host_pci_write16(struct bcma_device *core, u16 offset,
|
||||
u16 value)
|
||||
{
|
||||
if (core->bus->mapped_core != core)
|
||||
bcma_host_pci_switch_core(core);
|
||||
offset += bcma_host_pci_provide_access_to_core(core);
|
||||
iowrite16(value, core->bus->mmio + offset);
|
||||
}
|
||||
|
||||
static void bcma_host_pci_write32(struct bcma_device *core, u16 offset,
|
||||
u32 value)
|
||||
{
|
||||
if (core->bus->mapped_core != core)
|
||||
bcma_host_pci_switch_core(core);
|
||||
offset += bcma_host_pci_provide_access_to_core(core);
|
||||
iowrite32(value, core->bus->mmio + offset);
|
||||
}
|
||||
|
||||
|
@ -224,6 +234,41 @@ static void bcma_host_pci_remove(struct pci_dev *dev)
|
|||
pci_set_drvdata(dev, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bcma_host_pci_suspend(struct pci_dev *dev, pm_message_t state)
|
||||
{
|
||||
/* Host specific */
|
||||
pci_save_state(dev);
|
||||
pci_disable_device(dev);
|
||||
pci_set_power_state(dev, pci_choose_state(dev, state));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcma_host_pci_resume(struct pci_dev *dev)
|
||||
{
|
||||
struct bcma_bus *bus = pci_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
/* Host specific */
|
||||
pci_set_power_state(dev, 0);
|
||||
err = pci_enable_device(dev);
|
||||
if (err)
|
||||
return err;
|
||||
pci_restore_state(dev);
|
||||
|
||||
/* Bus specific */
|
||||
err = bcma_bus_resume(bus);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* CONFIG_PM */
|
||||
# define bcma_host_pci_suspend NULL
|
||||
# define bcma_host_pci_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
|
||||
|
@ -239,6 +284,8 @@ static struct pci_driver bcma_pci_bridge_driver = {
|
|||
.id_table = bcma_pci_bridge_tbl,
|
||||
.probe = bcma_host_pci_probe,
|
||||
.remove = bcma_host_pci_remove,
|
||||
.suspend = bcma_host_pci_suspend,
|
||||
.resume = bcma_host_pci_resume,
|
||||
};
|
||||
|
||||
int __init bcma_host_pci_init(void)
|
||||
|
|
|
@ -240,6 +240,22 @@ int __init bcma_bus_early_register(struct bcma_bus *bus,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int bcma_bus_resume(struct bcma_bus *bus)
|
||||
{
|
||||
struct bcma_device *core;
|
||||
|
||||
/* Init CC core */
|
||||
core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
|
||||
if (core) {
|
||||
bus->drv_cc.setup_done = false;
|
||||
bcma_core_chipcommon_init(&bus->drv_cc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int __bcma_driver_register(struct bcma_driver *drv, struct module *owner)
|
||||
{
|
||||
drv->drv.name = drv->name;
|
||||
|
|
|
@ -129,6 +129,9 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
|
|||
u16 v;
|
||||
int i;
|
||||
|
||||
bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
|
||||
SSB_SPROM_REVISION_REV;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
|
||||
*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
|
||||
|
@ -136,12 +139,70 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
|
|||
|
||||
bus->sprom.board_rev = sprom[SPOFF(SSB_SPROM8_BOARDREV)];
|
||||
|
||||
bus->sprom.txpid2g[0] = (sprom[SPOFF(SSB_SPROM4_TXPID2G01)] &
|
||||
SSB_SPROM4_TXPID2G0) >> SSB_SPROM4_TXPID2G0_SHIFT;
|
||||
bus->sprom.txpid2g[1] = (sprom[SPOFF(SSB_SPROM4_TXPID2G01)] &
|
||||
SSB_SPROM4_TXPID2G1) >> SSB_SPROM4_TXPID2G1_SHIFT;
|
||||
bus->sprom.txpid2g[2] = (sprom[SPOFF(SSB_SPROM4_TXPID2G23)] &
|
||||
SSB_SPROM4_TXPID2G2) >> SSB_SPROM4_TXPID2G2_SHIFT;
|
||||
bus->sprom.txpid2g[3] = (sprom[SPOFF(SSB_SPROM4_TXPID2G23)] &
|
||||
SSB_SPROM4_TXPID2G3) >> SSB_SPROM4_TXPID2G3_SHIFT;
|
||||
|
||||
bus->sprom.txpid5gl[0] = (sprom[SPOFF(SSB_SPROM4_TXPID5GL01)] &
|
||||
SSB_SPROM4_TXPID5GL0) >> SSB_SPROM4_TXPID5GL0_SHIFT;
|
||||
bus->sprom.txpid5gl[1] = (sprom[SPOFF(SSB_SPROM4_TXPID5GL01)] &
|
||||
SSB_SPROM4_TXPID5GL1) >> SSB_SPROM4_TXPID5GL1_SHIFT;
|
||||
bus->sprom.txpid5gl[2] = (sprom[SPOFF(SSB_SPROM4_TXPID5GL23)] &
|
||||
SSB_SPROM4_TXPID5GL2) >> SSB_SPROM4_TXPID5GL2_SHIFT;
|
||||
bus->sprom.txpid5gl[3] = (sprom[SPOFF(SSB_SPROM4_TXPID5GL23)] &
|
||||
SSB_SPROM4_TXPID5GL3) >> SSB_SPROM4_TXPID5GL3_SHIFT;
|
||||
|
||||
bus->sprom.txpid5g[0] = (sprom[SPOFF(SSB_SPROM4_TXPID5G01)] &
|
||||
SSB_SPROM4_TXPID5G0) >> SSB_SPROM4_TXPID5G0_SHIFT;
|
||||
bus->sprom.txpid5g[1] = (sprom[SPOFF(SSB_SPROM4_TXPID5G01)] &
|
||||
SSB_SPROM4_TXPID5G1) >> SSB_SPROM4_TXPID5G1_SHIFT;
|
||||
bus->sprom.txpid5g[2] = (sprom[SPOFF(SSB_SPROM4_TXPID5G23)] &
|
||||
SSB_SPROM4_TXPID5G2) >> SSB_SPROM4_TXPID5G2_SHIFT;
|
||||
bus->sprom.txpid5g[3] = (sprom[SPOFF(SSB_SPROM4_TXPID5G23)] &
|
||||
SSB_SPROM4_TXPID5G3) >> SSB_SPROM4_TXPID5G3_SHIFT;
|
||||
|
||||
bus->sprom.txpid5gh[0] = (sprom[SPOFF(SSB_SPROM4_TXPID5GH01)] &
|
||||
SSB_SPROM4_TXPID5GH0) >> SSB_SPROM4_TXPID5GH0_SHIFT;
|
||||
bus->sprom.txpid5gh[1] = (sprom[SPOFF(SSB_SPROM4_TXPID5GH01)] &
|
||||
SSB_SPROM4_TXPID5GH1) >> SSB_SPROM4_TXPID5GH1_SHIFT;
|
||||
bus->sprom.txpid5gh[2] = (sprom[SPOFF(SSB_SPROM4_TXPID5GH23)] &
|
||||
SSB_SPROM4_TXPID5GH2) >> SSB_SPROM4_TXPID5GH2_SHIFT;
|
||||
bus->sprom.txpid5gh[3] = (sprom[SPOFF(SSB_SPROM4_TXPID5GH23)] &
|
||||
SSB_SPROM4_TXPID5GH3) >> SSB_SPROM4_TXPID5GH3_SHIFT;
|
||||
|
||||
bus->sprom.boardflags_lo = sprom[SPOFF(SSB_SPROM8_BFLLO)];
|
||||
bus->sprom.boardflags_hi = sprom[SPOFF(SSB_SPROM8_BFLHI)];
|
||||
bus->sprom.boardflags2_lo = sprom[SPOFF(SSB_SPROM8_BFL2LO)];
|
||||
bus->sprom.boardflags2_hi = sprom[SPOFF(SSB_SPROM8_BFL2HI)];
|
||||
|
||||
bus->sprom.country_code = sprom[SPOFF(SSB_SPROM8_CCODE)];
|
||||
|
||||
bus->sprom.fem.ghz2.tssipos = (sprom[SPOFF(SSB_SPROM8_FEM2G)] &
|
||||
SSB_SROM8_FEM_TSSIPOS) >> SSB_SROM8_FEM_TSSIPOS_SHIFT;
|
||||
bus->sprom.fem.ghz2.extpa_gain = (sprom[SPOFF(SSB_SPROM8_FEM2G)] &
|
||||
SSB_SROM8_FEM_EXTPA_GAIN) >> SSB_SROM8_FEM_EXTPA_GAIN_SHIFT;
|
||||
bus->sprom.fem.ghz2.pdet_range = (sprom[SPOFF(SSB_SPROM8_FEM2G)] &
|
||||
SSB_SROM8_FEM_PDET_RANGE) >> SSB_SROM8_FEM_PDET_RANGE_SHIFT;
|
||||
bus->sprom.fem.ghz2.tr_iso = (sprom[SPOFF(SSB_SPROM8_FEM2G)] &
|
||||
SSB_SROM8_FEM_TR_ISO) >> SSB_SROM8_FEM_TR_ISO_SHIFT;
|
||||
bus->sprom.fem.ghz2.antswlut = (sprom[SPOFF(SSB_SPROM8_FEM2G)] &
|
||||
SSB_SROM8_FEM_ANTSWLUT) >> SSB_SROM8_FEM_ANTSWLUT_SHIFT;
|
||||
|
||||
bus->sprom.fem.ghz5.tssipos = (sprom[SPOFF(SSB_SPROM8_FEM5G)] &
|
||||
SSB_SROM8_FEM_TSSIPOS) >> SSB_SROM8_FEM_TSSIPOS_SHIFT;
|
||||
bus->sprom.fem.ghz5.extpa_gain = (sprom[SPOFF(SSB_SPROM8_FEM5G)] &
|
||||
SSB_SROM8_FEM_EXTPA_GAIN) >> SSB_SROM8_FEM_EXTPA_GAIN_SHIFT;
|
||||
bus->sprom.fem.ghz5.pdet_range = (sprom[SPOFF(SSB_SPROM8_FEM5G)] &
|
||||
SSB_SROM8_FEM_PDET_RANGE) >> SSB_SROM8_FEM_PDET_RANGE_SHIFT;
|
||||
bus->sprom.fem.ghz5.tr_iso = (sprom[SPOFF(SSB_SPROM8_FEM5G)] &
|
||||
SSB_SROM8_FEM_TR_ISO) >> SSB_SROM8_FEM_TR_ISO_SHIFT;
|
||||
bus->sprom.fem.ghz5.antswlut = (sprom[SPOFF(SSB_SPROM8_FEM5G)] &
|
||||
SSB_SROM8_FEM_ANTSWLUT) >> SSB_SROM8_FEM_ANTSWLUT_SHIFT;
|
||||
}
|
||||
|
||||
int bcma_sprom_get(struct bcma_bus *bus)
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <net/bluetooth/bluetooth.h>
|
||||
|
||||
#define VERSION "1.0"
|
||||
#define ATH3K_FIRMWARE "ath3k-1.fw"
|
||||
|
||||
#define ATH3K_DNLOAD 0x01
|
||||
#define ATH3K_GETSTATE 0x05
|
||||
|
@ -400,9 +401,15 @@ static int ath3k_probe(struct usb_interface *intf,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) {
|
||||
BT_ERR("Error loading firmware");
|
||||
return -EIO;
|
||||
ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT)
|
||||
BT_ERR("Firmware file \"%s\" not found",
|
||||
ATH3K_FIRMWARE);
|
||||
else
|
||||
BT_ERR("Firmware file \"%s\" request failed (err=%d)",
|
||||
ATH3K_FIRMWARE, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ath3k_load_firmware(udev, firmware);
|
||||
|
@ -441,4 +448,4 @@ MODULE_AUTHOR("Atheros Communications");
|
|||
MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE("ath3k-1.fw");
|
||||
MODULE_FIRMWARE(ATH3K_FIRMWARE);
|
||||
|
|
|
@ -751,9 +751,7 @@ static void bfusb_disconnect(struct usb_interface *intf)
|
|||
|
||||
bfusb_close(hdev);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
}
|
||||
|
||||
|
|
|
@ -844,9 +844,7 @@ static int bluecard_close(bluecard_info_t *info)
|
|||
/* Turn FPGA off */
|
||||
outb(0x80, iobase + 0x30);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -636,9 +636,7 @@ static int bt3c_close(bt3c_info_t *info)
|
|||
|
||||
bt3c_hci_close(hdev);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -565,9 +565,7 @@ static int btuart_close(btuart_info_t *info)
|
|||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -101,6 +101,7 @@ static struct usb_device_id btusb_table[] = {
|
|||
{ USB_DEVICE(0x0c10, 0x0000) },
|
||||
|
||||
/* Broadcom BCM20702A0 */
|
||||
{ USB_DEVICE(0x0a5c, 0x21e3) },
|
||||
{ USB_DEVICE(0x413c, 0x8197) },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
|
@ -315,7 +316,8 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
|
||||
err = usb_submit_urb(urb, mem_flags);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
@ -400,7 +402,8 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
|
||||
err = usb_submit_urb(urb, mem_flags);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
@ -506,15 +509,10 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
|
||||
pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
|
||||
|
||||
urb->dev = data->udev;
|
||||
urb->pipe = pipe;
|
||||
urb->context = hdev;
|
||||
urb->complete = btusb_isoc_complete;
|
||||
urb->interval = data->isoc_rx_ep->bInterval;
|
||||
usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_isoc_complete,
|
||||
hdev, data->isoc_rx_ep->bInterval);
|
||||
|
||||
urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
|
||||
urb->transfer_buffer = buf;
|
||||
urb->transfer_buffer_length = size;
|
||||
|
||||
__fill_isoc_descriptor(urb, size,
|
||||
le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
|
||||
|
@ -523,7 +521,8 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
|
||||
err = usb_submit_urb(urb, mem_flags);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
@ -727,6 +726,9 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||
usb_fill_bulk_urb(urb, data->udev, pipe,
|
||||
skb->data, skb->len, btusb_tx_complete, skb);
|
||||
|
||||
if (skb->priority >= HCI_PRIO_MAX - 1)
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
|
||||
hdev->stat.acl_tx++;
|
||||
break;
|
||||
|
||||
|
@ -770,7 +772,9 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s urb %p submission failed", hdev->name, urb);
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
kfree(urb->setup_packet);
|
||||
usb_unanchor_urb(urb);
|
||||
} else {
|
||||
|
|
|
@ -551,9 +551,7 @@ static int dtl1_close(dtl1_info_t *info)
|
|||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
|
||||
#define VERSION "1.3"
|
||||
|
||||
static bool amp;
|
||||
|
||||
struct vhci_data {
|
||||
struct hci_dev *hdev;
|
||||
|
||||
|
@ -239,6 +241,9 @@ static int vhci_open(struct inode *inode, struct file *file)
|
|||
hdev->bus = HCI_VIRTUAL;
|
||||
hdev->driver_data = data;
|
||||
|
||||
if (amp)
|
||||
hdev->dev_type = HCI_AMP;
|
||||
|
||||
hdev->open = vhci_open_dev;
|
||||
hdev->close = vhci_close_dev;
|
||||
hdev->flush = vhci_flush;
|
||||
|
@ -264,10 +269,7 @@ static int vhci_release(struct inode *inode, struct file *file)
|
|||
struct vhci_data *data = file->private_data;
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0) {
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
}
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
file->private_data = NULL;
|
||||
|
@ -306,6 +308,9 @@ static void __exit vhci_exit(void)
|
|||
module_init(vhci_init);
|
||||
module_exit(vhci_exit);
|
||||
|
||||
module_param(amp, bool, 0644);
|
||||
MODULE_PARM_DESC(amp, "Create AMP controller device");
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
|
|
|
@ -343,7 +343,7 @@ static void ieee802154_fake_setup(struct net_device *dev)
|
|||
{
|
||||
dev->addr_len = IEEE802154_ADDR_LEN;
|
||||
memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
|
||||
dev->features = NETIF_F_NO_CSUM;
|
||||
dev->features = NETIF_F_HW_CSUM;
|
||||
dev->needed_tailroom = 2; /* FCS */
|
||||
dev->mtu = 127;
|
||||
dev->tx_queue_len = 10;
|
||||
|
|
|
@ -178,6 +178,25 @@ static void queue_req(struct addr_req *req)
|
|||
mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
static int dst_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *addr)
|
||||
{
|
||||
struct neighbour *n;
|
||||
int ret;
|
||||
|
||||
rcu_read_lock();
|
||||
n = dst_get_neighbour_noref(dst);
|
||||
if (!n || !(n->nud_state & NUD_VALID)) {
|
||||
if (n)
|
||||
neigh_event_send(n, NULL);
|
||||
ret = -ENODATA;
|
||||
} else {
|
||||
ret = rdma_copy_addr(addr, dst->dev, n->ha);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int addr4_resolve(struct sockaddr_in *src_in,
|
||||
struct sockaddr_in *dst_in,
|
||||
struct rdma_dev_addr *addr)
|
||||
|
@ -185,7 +204,6 @@ static int addr4_resolve(struct sockaddr_in *src_in,
|
|||
__be32 src_ip = src_in->sin_addr.s_addr;
|
||||
__be32 dst_ip = dst_in->sin_addr.s_addr;
|
||||
struct rtable *rt;
|
||||
struct neighbour *neigh;
|
||||
struct flowi4 fl4;
|
||||
int ret;
|
||||
|
||||
|
@ -214,20 +232,7 @@ static int addr4_resolve(struct sockaddr_in *src_in,
|
|||
goto put;
|
||||
}
|
||||
|
||||
neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev);
|
||||
if (!neigh || !(neigh->nud_state & NUD_VALID)) {
|
||||
rcu_read_lock();
|
||||
neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
|
||||
rcu_read_unlock();
|
||||
ret = -ENODATA;
|
||||
if (neigh)
|
||||
goto release;
|
||||
goto put;
|
||||
}
|
||||
|
||||
ret = rdma_copy_addr(addr, neigh->dev, neigh->ha);
|
||||
release:
|
||||
neigh_release(neigh);
|
||||
ret = dst_fetch_ha(&rt->dst, addr);
|
||||
put:
|
||||
ip_rt_put(rt);
|
||||
out:
|
||||
|
@ -240,13 +245,12 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,
|
|||
struct rdma_dev_addr *addr)
|
||||
{
|
||||
struct flowi6 fl6;
|
||||
struct neighbour *neigh;
|
||||
struct dst_entry *dst;
|
||||
int ret;
|
||||
|
||||
memset(&fl6, 0, sizeof fl6);
|
||||
ipv6_addr_copy(&fl6.daddr, &dst_in->sin6_addr);
|
||||
ipv6_addr_copy(&fl6.saddr, &src_in->sin6_addr);
|
||||
fl6.daddr = dst_in->sin6_addr;
|
||||
fl6.saddr = src_in->sin6_addr;
|
||||
fl6.flowi6_oif = addr->bound_dev_if;
|
||||
|
||||
dst = ip6_route_output(&init_net, NULL, &fl6);
|
||||
|
@ -260,7 +264,7 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,
|
|||
goto put;
|
||||
|
||||
src_in->sin6_family = AF_INET6;
|
||||
ipv6_addr_copy(&src_in->sin6_addr, &fl6.saddr);
|
||||
src_in->sin6_addr = fl6.saddr;
|
||||
}
|
||||
|
||||
if (dst->dev->flags & IFF_LOOPBACK) {
|
||||
|
@ -276,16 +280,7 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,
|
|||
goto put;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
neigh = dst_get_neighbour(dst);
|
||||
if (!neigh || !(neigh->nud_state & NUD_VALID)) {
|
||||
if (neigh)
|
||||
neigh_event_send(neigh, NULL);
|
||||
ret = -ENODATA;
|
||||
} else {
|
||||
ret = rdma_copy_addr(addr, dst->dev, neigh->ha);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
ret = dst_fetch_ha(dst, addr);
|
||||
put:
|
||||
dst_release(dst);
|
||||
return ret;
|
||||
|
|
|
@ -2005,11 +2005,11 @@ static int cma_resolve_loopback(struct rdma_id_private *id_priv)
|
|||
if (cma_zero_addr(src)) {
|
||||
dst = (struct sockaddr *) &id_priv->id.route.addr.dst_addr;
|
||||
if ((src->sa_family = dst->sa_family) == AF_INET) {
|
||||
((struct sockaddr_in *) src)->sin_addr.s_addr =
|
||||
((struct sockaddr_in *) dst)->sin_addr.s_addr;
|
||||
((struct sockaddr_in *)src)->sin_addr =
|
||||
((struct sockaddr_in *)dst)->sin_addr;
|
||||
} else {
|
||||
ipv6_addr_copy(&((struct sockaddr_in6 *) src)->sin6_addr,
|
||||
&((struct sockaddr_in6 *) dst)->sin6_addr);
|
||||
((struct sockaddr_in6 *)src)->sin6_addr =
|
||||
((struct sockaddr_in6 *)dst)->sin6_addr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1338,7 +1338,6 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
|
|||
struct iwch_ep *child_ep, *parent_ep = ctx;
|
||||
struct cpl_pass_accept_req *req = cplhdr(skb);
|
||||
unsigned int hwtid = GET_TID(req);
|
||||
struct neighbour *neigh;
|
||||
struct dst_entry *dst;
|
||||
struct l2t_entry *l2t;
|
||||
struct rtable *rt;
|
||||
|
@ -1375,10 +1374,7 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
|
|||
goto reject;
|
||||
}
|
||||
dst = &rt->dst;
|
||||
rcu_read_lock();
|
||||
neigh = dst_get_neighbour(dst);
|
||||
l2t = t3_l2t_get(tdev, neigh, neigh->dev);
|
||||
rcu_read_unlock();
|
||||
l2t = t3_l2t_get(tdev, dst, NULL);
|
||||
if (!l2t) {
|
||||
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
|
||||
__func__);
|
||||
|
@ -1889,7 +1885,6 @@ static int is_loopback_dst(struct iw_cm_id *cm_id)
|
|||
int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
||||
{
|
||||
struct iwch_dev *h = to_iwch_dev(cm_id->device);
|
||||
struct neighbour *neigh;
|
||||
struct iwch_ep *ep;
|
||||
struct rtable *rt;
|
||||
int err = 0;
|
||||
|
@ -1947,13 +1942,7 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||
goto fail3;
|
||||
}
|
||||
ep->dst = &rt->dst;
|
||||
|
||||
rcu_read_lock();
|
||||
neigh = dst_get_neighbour(ep->dst);
|
||||
|
||||
/* get a l2t entry */
|
||||
ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev);
|
||||
rcu_read_unlock();
|
||||
ep->l2t = t3_l2t_get(ep->com.tdev, ep->dst, NULL);
|
||||
if (!ep->l2t) {
|
||||
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
|
||||
err = -ENOMEM;
|
||||
|
|
|
@ -1556,6 +1556,67 @@ static void get_4tuple(struct cpl_pass_accept_req *req,
|
|||
return;
|
||||
}
|
||||
|
||||
static int import_ep(struct c4iw_ep *ep, __be32 peer_ip, struct dst_entry *dst,
|
||||
struct c4iw_dev *cdev, bool clear_mpa_v1)
|
||||
{
|
||||
struct neighbour *n;
|
||||
int err, step;
|
||||
|
||||
rcu_read_lock();
|
||||
n = dst_get_neighbour_noref(dst);
|
||||
err = -ENODEV;
|
||||
if (!n)
|
||||
goto out;
|
||||
err = -ENOMEM;
|
||||
if (n->dev->flags & IFF_LOOPBACK) {
|
||||
struct net_device *pdev;
|
||||
|
||||
pdev = ip_dev_find(&init_net, peer_ip);
|
||||
ep->l2t = cxgb4_l2t_get(cdev->rdev.lldi.l2t,
|
||||
n, pdev, 0);
|
||||
if (!ep->l2t)
|
||||
goto out;
|
||||
ep->mtu = pdev->mtu;
|
||||
ep->tx_chan = cxgb4_port_chan(pdev);
|
||||
ep->smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
|
||||
step = cdev->rdev.lldi.ntxq /
|
||||
cdev->rdev.lldi.nchan;
|
||||
ep->txq_idx = cxgb4_port_idx(pdev) * step;
|
||||
step = cdev->rdev.lldi.nrxq /
|
||||
cdev->rdev.lldi.nchan;
|
||||
ep->ctrlq_idx = cxgb4_port_idx(pdev);
|
||||
ep->rss_qid = cdev->rdev.lldi.rxq_ids[
|
||||
cxgb4_port_idx(pdev) * step];
|
||||
dev_put(pdev);
|
||||
} else {
|
||||
ep->l2t = cxgb4_l2t_get(cdev->rdev.lldi.l2t,
|
||||
n, n->dev, 0);
|
||||
if (!ep->l2t)
|
||||
goto out;
|
||||
ep->mtu = dst_mtu(ep->dst);
|
||||
ep->tx_chan = cxgb4_port_chan(n->dev);
|
||||
ep->smac_idx = (cxgb4_port_viid(n->dev) & 0x7F) << 1;
|
||||
step = cdev->rdev.lldi.ntxq /
|
||||
cdev->rdev.lldi.nchan;
|
||||
ep->txq_idx = cxgb4_port_idx(n->dev) * step;
|
||||
ep->ctrlq_idx = cxgb4_port_idx(n->dev);
|
||||
step = cdev->rdev.lldi.nrxq /
|
||||
cdev->rdev.lldi.nchan;
|
||||
ep->rss_qid = cdev->rdev.lldi.rxq_ids[
|
||||
cxgb4_port_idx(n->dev) * step];
|
||||
|
||||
if (clear_mpa_v1) {
|
||||
ep->retry_with_mpa_v1 = 0;
|
||||
ep->tried_with_mpa_v1 = 0;
|
||||
}
|
||||
}
|
||||
err = 0;
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct c4iw_ep *child_ep, *parent_ep;
|
||||
|
@ -1563,18 +1624,11 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
|
|||
unsigned int stid = GET_POPEN_TID(ntohl(req->tos_stid));
|
||||
struct tid_info *t = dev->rdev.lldi.tids;
|
||||
unsigned int hwtid = GET_TID(req);
|
||||
struct neighbour *neigh;
|
||||
struct dst_entry *dst;
|
||||
struct l2t_entry *l2t;
|
||||
struct rtable *rt;
|
||||
__be32 local_ip, peer_ip;
|
||||
__be16 local_port, peer_port;
|
||||
struct net_device *pdev;
|
||||
u32 tx_chan, smac_idx;
|
||||
u16 rss_qid;
|
||||
u32 mtu;
|
||||
int step;
|
||||
int txq_idx, ctrlq_idx;
|
||||
int err;
|
||||
|
||||
parent_ep = lookup_stid(t, stid);
|
||||
PDBG("%s parent ep %p tid %u\n", __func__, parent_ep, hwtid);
|
||||
|
@ -1596,49 +1650,24 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
|
|||
goto reject;
|
||||
}
|
||||
dst = &rt->dst;
|
||||
rcu_read_lock();
|
||||
neigh = dst_get_neighbour(dst);
|
||||
if (neigh->dev->flags & IFF_LOOPBACK) {
|
||||
pdev = ip_dev_find(&init_net, peer_ip);
|
||||
BUG_ON(!pdev);
|
||||
l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh, pdev, 0);
|
||||
mtu = pdev->mtu;
|
||||
tx_chan = cxgb4_port_chan(pdev);
|
||||
smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
|
||||
step = dev->rdev.lldi.ntxq / dev->rdev.lldi.nchan;
|
||||
txq_idx = cxgb4_port_idx(pdev) * step;
|
||||
ctrlq_idx = cxgb4_port_idx(pdev);
|
||||
step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan;
|
||||
rss_qid = dev->rdev.lldi.rxq_ids[cxgb4_port_idx(pdev) * step];
|
||||
dev_put(pdev);
|
||||
} else {
|
||||
l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh, neigh->dev, 0);
|
||||
mtu = dst_mtu(dst);
|
||||
tx_chan = cxgb4_port_chan(neigh->dev);
|
||||
smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1;
|
||||
step = dev->rdev.lldi.ntxq / dev->rdev.lldi.nchan;
|
||||
txq_idx = cxgb4_port_idx(neigh->dev) * step;
|
||||
ctrlq_idx = cxgb4_port_idx(neigh->dev);
|
||||
step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan;
|
||||
rss_qid = dev->rdev.lldi.rxq_ids[
|
||||
cxgb4_port_idx(neigh->dev) * step];
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (!l2t) {
|
||||
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
|
||||
__func__);
|
||||
dst_release(dst);
|
||||
goto reject;
|
||||
}
|
||||
|
||||
child_ep = alloc_ep(sizeof(*child_ep), GFP_KERNEL);
|
||||
if (!child_ep) {
|
||||
printk(KERN_ERR MOD "%s - failed to allocate ep entry!\n",
|
||||
__func__);
|
||||
cxgb4_l2t_release(l2t);
|
||||
dst_release(dst);
|
||||
goto reject;
|
||||
}
|
||||
|
||||
err = import_ep(child_ep, peer_ip, dst, dev, false);
|
||||
if (err) {
|
||||
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
|
||||
__func__);
|
||||
dst_release(dst);
|
||||
kfree(child_ep);
|
||||
goto reject;
|
||||
}
|
||||
|
||||
state_set(&child_ep->com, CONNECTING);
|
||||
child_ep->com.dev = dev;
|
||||
child_ep->com.cm_id = NULL;
|
||||
|
@ -1651,18 +1680,11 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
|
|||
c4iw_get_ep(&parent_ep->com);
|
||||
child_ep->parent_ep = parent_ep;
|
||||
child_ep->tos = GET_POPEN_TOS(ntohl(req->tos_stid));
|
||||
child_ep->l2t = l2t;
|
||||
child_ep->dst = dst;
|
||||
child_ep->hwtid = hwtid;
|
||||
child_ep->tx_chan = tx_chan;
|
||||
child_ep->smac_idx = smac_idx;
|
||||
child_ep->rss_qid = rss_qid;
|
||||
child_ep->mtu = mtu;
|
||||
child_ep->txq_idx = txq_idx;
|
||||
child_ep->ctrlq_idx = ctrlq_idx;
|
||||
|
||||
PDBG("%s tx_chan %u smac_idx %u rss_qid %u\n", __func__,
|
||||
tx_chan, smac_idx, rss_qid);
|
||||
child_ep->tx_chan, child_ep->smac_idx, child_ep->rss_qid);
|
||||
|
||||
init_timer(&child_ep->timer);
|
||||
cxgb4_insert_tid(t, child_ep, hwtid);
|
||||
|
@ -1792,11 +1814,8 @@ static int is_neg_adv_abort(unsigned int status)
|
|||
|
||||
static int c4iw_reconnect(struct c4iw_ep *ep)
|
||||
{
|
||||
int err = 0;
|
||||
struct rtable *rt;
|
||||
struct net_device *pdev;
|
||||
struct neighbour *neigh;
|
||||
int step;
|
||||
int err = 0;
|
||||
|
||||
PDBG("%s qp %p cm_id %p\n", __func__, ep->com.qp, ep->com.cm_id);
|
||||
init_timer(&ep->timer);
|
||||
|
@ -1824,47 +1843,10 @@ static int c4iw_reconnect(struct c4iw_ep *ep)
|
|||
}
|
||||
ep->dst = &rt->dst;
|
||||
|
||||
rcu_read_lock();
|
||||
neigh = dst_get_neighbour(ep->dst);
|
||||
|
||||
/* get a l2t entry */
|
||||
if (neigh->dev->flags & IFF_LOOPBACK) {
|
||||
PDBG("%s LOOPBACK\n", __func__);
|
||||
pdev = ip_dev_find(&init_net,
|
||||
ep->com.cm_id->remote_addr.sin_addr.s_addr);
|
||||
ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
|
||||
neigh, pdev, 0);
|
||||
ep->mtu = pdev->mtu;
|
||||
ep->tx_chan = cxgb4_port_chan(pdev);
|
||||
ep->smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
|
||||
step = ep->com.dev->rdev.lldi.ntxq /
|
||||
ep->com.dev->rdev.lldi.nchan;
|
||||
ep->txq_idx = cxgb4_port_idx(pdev) * step;
|
||||
step = ep->com.dev->rdev.lldi.nrxq /
|
||||
ep->com.dev->rdev.lldi.nchan;
|
||||
ep->ctrlq_idx = cxgb4_port_idx(pdev);
|
||||
ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
|
||||
cxgb4_port_idx(pdev) * step];
|
||||
dev_put(pdev);
|
||||
} else {
|
||||
ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
|
||||
neigh, neigh->dev, 0);
|
||||
ep->mtu = dst_mtu(ep->dst);
|
||||
ep->tx_chan = cxgb4_port_chan(neigh->dev);
|
||||
ep->smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1;
|
||||
step = ep->com.dev->rdev.lldi.ntxq /
|
||||
ep->com.dev->rdev.lldi.nchan;
|
||||
ep->txq_idx = cxgb4_port_idx(neigh->dev) * step;
|
||||
ep->ctrlq_idx = cxgb4_port_idx(neigh->dev);
|
||||
step = ep->com.dev->rdev.lldi.nrxq /
|
||||
ep->com.dev->rdev.lldi.nchan;
|
||||
ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
|
||||
cxgb4_port_idx(neigh->dev) * step];
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (!ep->l2t) {
|
||||
err = import_ep(ep, ep->com.cm_id->remote_addr.sin_addr.s_addr,
|
||||
ep->dst, ep->com.dev, false);
|
||||
if (err) {
|
||||
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
|
@ -2240,13 +2222,10 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||
|
||||
int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
||||
{
|
||||
int err = 0;
|
||||
struct c4iw_dev *dev = to_c4iw_dev(cm_id->device);
|
||||
struct c4iw_ep *ep;
|
||||
struct rtable *rt;
|
||||
struct net_device *pdev;
|
||||
struct neighbour *neigh;
|
||||
int step;
|
||||
int err = 0;
|
||||
|
||||
if ((conn_param->ord > c4iw_max_read_depth) ||
|
||||
(conn_param->ird > c4iw_max_read_depth)) {
|
||||
|
@ -2307,49 +2286,10 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||
}
|
||||
ep->dst = &rt->dst;
|
||||
|
||||
rcu_read_lock();
|
||||
neigh = dst_get_neighbour(ep->dst);
|
||||
|
||||
/* get a l2t entry */
|
||||
if (neigh->dev->flags & IFF_LOOPBACK) {
|
||||
PDBG("%s LOOPBACK\n", __func__);
|
||||
pdev = ip_dev_find(&init_net,
|
||||
cm_id->remote_addr.sin_addr.s_addr);
|
||||
ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
|
||||
neigh, pdev, 0);
|
||||
ep->mtu = pdev->mtu;
|
||||
ep->tx_chan = cxgb4_port_chan(pdev);
|
||||
ep->smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
|
||||
step = ep->com.dev->rdev.lldi.ntxq /
|
||||
ep->com.dev->rdev.lldi.nchan;
|
||||
ep->txq_idx = cxgb4_port_idx(pdev) * step;
|
||||
step = ep->com.dev->rdev.lldi.nrxq /
|
||||
ep->com.dev->rdev.lldi.nchan;
|
||||
ep->ctrlq_idx = cxgb4_port_idx(pdev);
|
||||
ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
|
||||
cxgb4_port_idx(pdev) * step];
|
||||
dev_put(pdev);
|
||||
} else {
|
||||
ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
|
||||
neigh, neigh->dev, 0);
|
||||
ep->mtu = dst_mtu(ep->dst);
|
||||
ep->tx_chan = cxgb4_port_chan(neigh->dev);
|
||||
ep->smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1;
|
||||
step = ep->com.dev->rdev.lldi.ntxq /
|
||||
ep->com.dev->rdev.lldi.nchan;
|
||||
ep->txq_idx = cxgb4_port_idx(neigh->dev) * step;
|
||||
ep->ctrlq_idx = cxgb4_port_idx(neigh->dev);
|
||||
step = ep->com.dev->rdev.lldi.nrxq /
|
||||
ep->com.dev->rdev.lldi.nchan;
|
||||
ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
|
||||
cxgb4_port_idx(neigh->dev) * step];
|
||||
ep->retry_with_mpa_v1 = 0;
|
||||
ep->tried_with_mpa_v1 = 0;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (!ep->l2t) {
|
||||
err = import_ep(ep, cm_id->remote_addr.sin_addr.s_addr,
|
||||
ep->dst, ep->com.dev, true);
|
||||
if (err) {
|
||||
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,8 @@ int mlx4_MAD_IFC(struct mlx4_ib_dev *dev, int ignore_mkey, int ignore_bkey,
|
|||
|
||||
err = mlx4_cmd_box(dev->dev, inmailbox->dma, outmailbox->dma,
|
||||
in_modifier, op_modifier,
|
||||
MLX4_CMD_MAD_IFC, MLX4_CMD_TIME_CLASS_C);
|
||||
MLX4_CMD_MAD_IFC, MLX4_CMD_TIME_CLASS_C,
|
||||
MLX4_CMD_NATIVE);
|
||||
|
||||
if (!err)
|
||||
memcpy(response_mad, outmailbox->buf, 256);
|
||||
|
@ -330,7 +331,8 @@ static int iboe_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
|
|||
return IB_MAD_RESULT_FAILURE;
|
||||
|
||||
err = mlx4_cmd_box(dev->dev, 0, mailbox->dma, inmod, 0,
|
||||
MLX4_CMD_QUERY_IF_STAT, MLX4_CMD_TIME_CLASS_C);
|
||||
MLX4_CMD_QUERY_IF_STAT, MLX4_CMD_TIME_CLASS_C,
|
||||
MLX4_CMD_WRAPPED);
|
||||
if (err)
|
||||
err = IB_MAD_RESULT_FAILURE;
|
||||
else {
|
||||
|
|
|
@ -177,7 +177,7 @@ mlx4_ib_port_link_layer(struct ib_device *device, u8 port_num)
|
|||
{
|
||||
struct mlx4_dev *dev = to_mdev(device)->dev;
|
||||
|
||||
return dev->caps.port_mask & (1 << (port_num - 1)) ?
|
||||
return dev->caps.port_mask[port_num] == MLX4_PORT_TYPE_IB ?
|
||||
IB_LINK_LAYER_INFINIBAND : IB_LINK_LAYER_ETHERNET;
|
||||
}
|
||||
|
||||
|
@ -434,7 +434,7 @@ static int mlx4_ib_modify_device(struct ib_device *ibdev, int mask,
|
|||
memset(mailbox->buf, 0, 256);
|
||||
memcpy(mailbox->buf, props->node_desc, 64);
|
||||
mlx4_cmd(to_mdev(ibdev)->dev, mailbox->dma, 1, 0,
|
||||
MLX4_CMD_SET_NODE, MLX4_CMD_TIME_CLASS_A);
|
||||
MLX4_CMD_SET_NODE, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
|
||||
|
||||
mlx4_free_cmd_mailbox(to_mdev(ibdev)->dev, mailbox);
|
||||
|
||||
|
@ -463,7 +463,7 @@ static int mlx4_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_viols,
|
|||
}
|
||||
|
||||
err = mlx4_cmd(dev->dev, mailbox->dma, port, is_eth, MLX4_CMD_SET_PORT,
|
||||
MLX4_CMD_TIME_CLASS_B);
|
||||
MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
|
||||
|
||||
mlx4_free_cmd_mailbox(dev->dev, mailbox);
|
||||
return err;
|
||||
|
@ -899,7 +899,8 @@ static void update_gids_task(struct work_struct *work)
|
|||
memcpy(gids, gw->gids, sizeof gw->gids);
|
||||
|
||||
err = mlx4_cmd(dev, mailbox->dma, MLX4_SET_PORT_GID_TABLE << 8 | gw->port,
|
||||
1, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B);
|
||||
1, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
|
||||
MLX4_CMD_NATIVE);
|
||||
if (err)
|
||||
printk(KERN_WARNING "set port command failed\n");
|
||||
else {
|
||||
|
@ -1074,6 +1075,11 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
|
|||
|
||||
printk_once(KERN_INFO "%s", mlx4_ib_version);
|
||||
|
||||
if (mlx4_is_mfunc(dev)) {
|
||||
printk(KERN_WARNING "IB not yet supported in SRIOV\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mlx4_foreach_ib_transport_port(i, dev)
|
||||
num_ports++;
|
||||
|
||||
|
|
|
@ -1348,7 +1348,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
|
|||
else
|
||||
netdev = nesvnic->netdev;
|
||||
|
||||
neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, netdev);
|
||||
rcu_read_lock();
|
||||
neigh = dst_get_neighbour_noref(&rt->dst);
|
||||
if (neigh) {
|
||||
if (neigh->nud_state & NUD_VALID) {
|
||||
nes_debug(NES_DBG_CM, "Neighbor MAC address for 0x%08X"
|
||||
|
@ -1359,7 +1360,6 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
|
|||
if (!memcmp(nesadapter->arp_table[arpindex].mac_addr,
|
||||
neigh->ha, ETH_ALEN)) {
|
||||
/* Mac address same as in nes_arp_table */
|
||||
neigh_release(neigh);
|
||||
ip_rt_put(rt);
|
||||
return rc;
|
||||
}
|
||||
|
@ -1373,15 +1373,11 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
|
|||
dst_ip, NES_ARP_ADD);
|
||||
rc = nes_arp_table(nesvnic->nesdev, dst_ip, NULL,
|
||||
NES_ARP_RESOLVE);
|
||||
} else {
|
||||
neigh_event_send(neigh, NULL);
|
||||
}
|
||||
neigh_release(neigh);
|
||||
}
|
||||
|
||||
if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID))) {
|
||||
rcu_read_lock();
|
||||
neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
rcu_read_unlock();
|
||||
ip_rt_put(rt);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -1589,7 +1589,7 @@ static const struct ethtool_ops nes_ethtool_ops = {
|
|||
.set_pauseparam = nes_netdev_set_pauseparam,
|
||||
};
|
||||
|
||||
static void nes_vlan_mode(struct net_device *netdev, struct nes_device *nesdev, u32 features)
|
||||
static void nes_vlan_mode(struct net_device *netdev, struct nes_device *nesdev, netdev_features_t features)
|
||||
{
|
||||
struct nes_adapter *nesadapter = nesdev->nesadapter;
|
||||
u32 u32temp;
|
||||
|
@ -1610,7 +1610,7 @@ static void nes_vlan_mode(struct net_device *netdev, struct nes_device *nesdev,
|
|||
spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
|
||||
}
|
||||
|
||||
static u32 nes_fix_features(struct net_device *netdev, u32 features)
|
||||
static netdev_features_t nes_fix_features(struct net_device *netdev, netdev_features_t features)
|
||||
{
|
||||
/*
|
||||
* Since there is no support for separate rx/tx vlan accel
|
||||
|
@ -1624,7 +1624,7 @@ static u32 nes_fix_features(struct net_device *netdev, u32 features)
|
|||
return features;
|
||||
}
|
||||
|
||||
static int nes_set_features(struct net_device *netdev, u32 features)
|
||||
static int nes_set_features(struct net_device *netdev, netdev_features_t features)
|
||||
{
|
||||
struct nes_vnic *nesvnic = netdev_priv(netdev);
|
||||
struct nes_device *nesdev = nesvnic->nesdev;
|
||||
|
|
|
@ -171,7 +171,7 @@ static int ipoib_stop(struct net_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 ipoib_fix_features(struct net_device *dev, u32 features)
|
||||
static netdev_features_t ipoib_fix_features(struct net_device *dev, netdev_features_t features)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
|
@ -556,15 +556,13 @@ static int path_rec_start(struct net_device *dev,
|
|||
}
|
||||
|
||||
/* called with rcu_read_lock */
|
||||
static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
|
||||
static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ipoib_path *path;
|
||||
struct ipoib_neigh *neigh;
|
||||
struct neighbour *n;
|
||||
unsigned long flags;
|
||||
|
||||
n = dst_get_neighbour(skb_dst(skb));
|
||||
neigh = ipoib_neigh_alloc(n, skb->dev);
|
||||
if (!neigh) {
|
||||
++dev->stats.tx_dropped;
|
||||
|
@ -638,16 +636,13 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
|
|||
}
|
||||
|
||||
/* called with rcu_read_lock */
|
||||
static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev)
|
||||
static void ipoib_path_lookup(struct sk_buff *skb, struct neighbour *n, struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(skb->dev);
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
struct neighbour *n;
|
||||
|
||||
/* Look up path record for unicasts */
|
||||
n = dst_get_neighbour(dst);
|
||||
if (n->ha[4] != 0xff) {
|
||||
neigh_add_path(skb, dev);
|
||||
neigh_add_path(skb, n, dev);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -723,12 +718,17 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
unsigned long flags;
|
||||
|
||||
rcu_read_lock();
|
||||
if (likely(skb_dst(skb)))
|
||||
n = dst_get_neighbour(skb_dst(skb));
|
||||
|
||||
if (likely(skb_dst(skb))) {
|
||||
n = dst_get_neighbour_noref(skb_dst(skb));
|
||||
if (!n) {
|
||||
++dev->stats.tx_dropped;
|
||||
dev_kfree_skb_any(skb);
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if (likely(n)) {
|
||||
if (unlikely(!*to_ipoib_neigh(n))) {
|
||||
ipoib_path_lookup(skb, dev);
|
||||
ipoib_path_lookup(skb, n, dev);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
|
@ -751,7 +751,7 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
list_del(&neigh->list);
|
||||
ipoib_neigh_free(dev, neigh);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
ipoib_path_lookup(skb, dev);
|
||||
ipoib_path_lookup(skb, n, dev);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
|
@ -841,7 +841,7 @@ static int ipoib_hard_header(struct sk_buff *skb,
|
|||
dst = skb_dst(skb);
|
||||
n = NULL;
|
||||
if (dst)
|
||||
n = dst_get_neighbour_raw(dst);
|
||||
n = dst_get_neighbour_noref_raw(dst);
|
||||
if ((!dst || !n) && daddr) {
|
||||
struct ipoib_pseudoheader *phdr =
|
||||
(struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr);
|
||||
|
@ -1222,6 +1222,8 @@ static struct net_device *ipoib_add_port(const char *format,
|
|||
priv->dev->mtu = IPOIB_UD_MTU(priv->max_ib_mtu);
|
||||
priv->mcast_mtu = priv->admin_mtu = priv->dev->mtu;
|
||||
|
||||
priv->dev->neigh_priv_len = sizeof(struct ipoib_neigh);
|
||||
|
||||
result = ib_query_pkey(hca, port, 0, &priv->pkey);
|
||||
if (result) {
|
||||
printk(KERN_WARNING "%s: ib_query_pkey port %d failed (ret = %d)\n",
|
||||
|
|
|
@ -269,7 +269,7 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
|
|||
|
||||
skb->dev = dev;
|
||||
if (dst)
|
||||
n = dst_get_neighbour_raw(dst);
|
||||
n = dst_get_neighbour_noref_raw(dst);
|
||||
if (!dst || !n) {
|
||||
/* put pseudoheader back on for next time */
|
||||
skb_push(skb, sizeof (struct ipoib_pseudoheader));
|
||||
|
@ -728,7 +728,7 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
|
|||
|
||||
rcu_read_lock();
|
||||
if (dst)
|
||||
n = dst_get_neighbour(dst);
|
||||
n = dst_get_neighbour_noref(dst);
|
||||
if (n && !*to_ipoib_neigh(n)) {
|
||||
struct ipoib_neigh *neigh = ipoib_neigh_alloc(n,
|
||||
skb->dev);
|
||||
|
|
|
@ -624,8 +624,6 @@ int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
|
|||
{
|
||||
isdn_if *iif;
|
||||
|
||||
pr_info("ISDN4Linux interface\n");
|
||||
|
||||
iif = kmalloc(sizeof *iif, GFP_KERNEL);
|
||||
if (!iif) {
|
||||
pr_err("out of memory\n");
|
||||
|
@ -684,6 +682,7 @@ void gigaset_isdn_unregdev(struct cardstate *cs)
|
|||
*/
|
||||
void gigaset_isdn_regdrv(void)
|
||||
{
|
||||
pr_info("ISDN4Linux interface\n");
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
|
|
|
@ -381,6 +381,11 @@ static int lg_find_vqs(struct virtio_device *vdev, unsigned nvqs,
|
|||
return PTR_ERR(vqs[i]);
|
||||
}
|
||||
|
||||
static const char *lg_bus_name(struct virtio_device *vdev)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/* The ops structure which hooks everything together. */
|
||||
static struct virtio_config_ops lguest_config_ops = {
|
||||
.get_features = lg_get_features,
|
||||
|
@ -392,6 +397,7 @@ static struct virtio_config_ops lguest_config_ops = {
|
|||
.reset = lg_reset,
|
||||
.find_vqs = lg_find_vqs,
|
||||
.del_vqs = lg_del_vqs,
|
||||
.bus_name = lg_bus_name,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -63,6 +63,7 @@ static void eeprom_93cx6_startup(struct eeprom_93cx6 *eeprom)
|
|||
eeprom->reg_data_out = 0;
|
||||
eeprom->reg_data_clock = 0;
|
||||
eeprom->reg_chip_select = 1;
|
||||
eeprom->drive_data = 1;
|
||||
eeprom->register_write(eeprom);
|
||||
|
||||
/*
|
||||
|
@ -101,6 +102,7 @@ static void eeprom_93cx6_write_bits(struct eeprom_93cx6 *eeprom,
|
|||
*/
|
||||
eeprom->reg_data_in = 0;
|
||||
eeprom->reg_data_out = 0;
|
||||
eeprom->drive_data = 1;
|
||||
|
||||
/*
|
||||
* Start writing all bits.
|
||||
|
@ -140,6 +142,7 @@ static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom,
|
|||
*/
|
||||
eeprom->reg_data_in = 0;
|
||||
eeprom->reg_data_out = 0;
|
||||
eeprom->drive_data = 0;
|
||||
|
||||
/*
|
||||
* Start reading all bits.
|
||||
|
@ -231,3 +234,88 @@ void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread);
|
||||
|
||||
/**
|
||||
* eeprom_93cx6_wren - set the write enable state
|
||||
* @eeprom: Pointer to eeprom structure
|
||||
* @enable: true to enable writes, otherwise disable writes
|
||||
*
|
||||
* Set the EEPROM write enable state to either allow or deny
|
||||
* writes depending on the @enable value.
|
||||
*/
|
||||
void eeprom_93cx6_wren(struct eeprom_93cx6 *eeprom, bool enable)
|
||||
{
|
||||
u16 command;
|
||||
|
||||
/* start the command */
|
||||
eeprom_93cx6_startup(eeprom);
|
||||
|
||||
/* create command to enable/disable */
|
||||
|
||||
command = enable ? PCI_EEPROM_EWEN_OPCODE : PCI_EEPROM_EWDS_OPCODE;
|
||||
command <<= (eeprom->width - 2);
|
||||
|
||||
eeprom_93cx6_write_bits(eeprom, command,
|
||||
PCI_EEPROM_WIDTH_OPCODE + eeprom->width);
|
||||
|
||||
eeprom_93cx6_cleanup(eeprom);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eeprom_93cx6_wren);
|
||||
|
||||
/**
|
||||
* eeprom_93cx6_write - write data to the EEPROM
|
||||
* @eeprom: Pointer to eeprom structure
|
||||
* @addr: Address to write data to.
|
||||
* @data: The data to write to address @addr.
|
||||
*
|
||||
* Write the @data to the specified @addr in the EEPROM and
|
||||
* waiting for the device to finish writing.
|
||||
*
|
||||
* Note, since we do not expect large number of write operations
|
||||
* we delay in between parts of the operation to avoid using excessive
|
||||
* amounts of CPU time busy waiting.
|
||||
*/
|
||||
void eeprom_93cx6_write(struct eeprom_93cx6 *eeprom, u8 addr, u16 data)
|
||||
{
|
||||
int timeout = 100;
|
||||
u16 command;
|
||||
|
||||
/* start the command */
|
||||
eeprom_93cx6_startup(eeprom);
|
||||
|
||||
command = PCI_EEPROM_WRITE_OPCODE << eeprom->width;
|
||||
command |= addr;
|
||||
|
||||
/* send write command */
|
||||
eeprom_93cx6_write_bits(eeprom, command,
|
||||
PCI_EEPROM_WIDTH_OPCODE + eeprom->width);
|
||||
|
||||
/* send data */
|
||||
eeprom_93cx6_write_bits(eeprom, data, 16);
|
||||
|
||||
/* get ready to check for busy */
|
||||
eeprom->drive_data = 0;
|
||||
eeprom->reg_chip_select = 1;
|
||||
eeprom->register_write(eeprom);
|
||||
|
||||
/* wait at-least 250ns to get DO to be the busy signal */
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
/* wait for DO to go high to signify finish */
|
||||
|
||||
while (true) {
|
||||
eeprom->register_read(eeprom);
|
||||
|
||||
if (eeprom->reg_data_out)
|
||||
break;
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
if (--timeout <= 0) {
|
||||
printk(KERN_ERR "%s: timeout\n", __func__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
eeprom_93cx6_cleanup(eeprom);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eeprom_93cx6_write);
|
||||
|
|
|
@ -576,7 +576,7 @@ xpnet_init(void)
|
|||
* report an error if the data is not retrievable and the
|
||||
* packet will be dropped.
|
||||
*/
|
||||
xpnet_device->features = NETIF_F_NO_CSUM;
|
||||
xpnet_device->features = NETIF_F_HW_CSUM;
|
||||
|
||||
result = register_netdev(xpnet_device);
|
||||
if (result != 0) {
|
||||
|
|
|
@ -125,6 +125,8 @@ config IFB
|
|||
'ifb1' etc.
|
||||
Look at the iproute2 documentation directory for usage etc
|
||||
|
||||
source "drivers/net/team/Kconfig"
|
||||
|
||||
config MACVLAN
|
||||
tristate "MAC-VLAN support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
|
@ -241,6 +243,8 @@ source "drivers/atm/Kconfig"
|
|||
|
||||
source "drivers/net/caif/Kconfig"
|
||||
|
||||
source "drivers/net/dsa/Kconfig"
|
||||
|
||||
source "drivers/net/ethernet/Kconfig"
|
||||
|
||||
source "drivers/net/fddi/Kconfig"
|
||||
|
|
|
@ -17,6 +17,7 @@ obj-$(CONFIG_NET) += Space.o loopback.o
|
|||
obj-$(CONFIG_NETCONSOLE) += netconsole.o
|
||||
obj-$(CONFIG_PHYLIB) += phy/
|
||||
obj-$(CONFIG_RIONET) += rionet.o
|
||||
obj-$(CONFIG_NET_TEAM) += team/
|
||||
obj-$(CONFIG_TUN) += tun.o
|
||||
obj-$(CONFIG_VETH) += veth.o
|
||||
obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
|
||||
|
@ -29,6 +30,7 @@ obj-$(CONFIG_DEV_APPLETALK) += appletalk/
|
|||
obj-$(CONFIG_CAIF) += caif/
|
||||
obj-$(CONFIG_CAN) += can/
|
||||
obj-$(CONFIG_ETRAX_ETHERNET) += cris/
|
||||
obj-$(CONFIG_NET_DSA) += dsa/
|
||||
obj-$(CONFIG_ETHERNET) += ethernet/
|
||||
obj-$(CONFIG_FDDI) += fddi/
|
||||
obj-$(CONFIG_HIPPI) += hippi/
|
||||
|
|
|
@ -1,225 +0,0 @@
|
|||
/*
|
||||
* Copyright(c) 2008 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called LICENSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/ndisc.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <net/netns/generic.h>
|
||||
#include "bonding.h"
|
||||
|
||||
/*
|
||||
* Assign bond->master_ipv6 to the next IPv6 address in the list, or
|
||||
* zero it out if there are none.
|
||||
*/
|
||||
static void bond_glean_dev_ipv6(struct net_device *dev, struct in6_addr *addr)
|
||||
{
|
||||
struct inet6_dev *idev;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
idev = in6_dev_get(dev);
|
||||
if (!idev)
|
||||
return;
|
||||
|
||||
read_lock_bh(&idev->lock);
|
||||
if (!list_empty(&idev->addr_list)) {
|
||||
struct inet6_ifaddr *ifa
|
||||
= list_first_entry(&idev->addr_list,
|
||||
struct inet6_ifaddr, if_list);
|
||||
ipv6_addr_copy(addr, &ifa->addr);
|
||||
} else
|
||||
ipv6_addr_set(addr, 0, 0, 0, 0);
|
||||
|
||||
read_unlock_bh(&idev->lock);
|
||||
|
||||
in6_dev_put(idev);
|
||||
}
|
||||
|
||||
static void bond_na_send(struct net_device *slave_dev,
|
||||
struct in6_addr *daddr,
|
||||
int router,
|
||||
unsigned short vlan_id)
|
||||
{
|
||||
struct in6_addr mcaddr;
|
||||
struct icmp6hdr icmp6h = {
|
||||
.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
|
||||
};
|
||||
struct sk_buff *skb;
|
||||
|
||||
icmp6h.icmp6_router = router;
|
||||
icmp6h.icmp6_solicited = 0;
|
||||
icmp6h.icmp6_override = 1;
|
||||
|
||||
addrconf_addr_solict_mult(daddr, &mcaddr);
|
||||
|
||||
pr_debug("ipv6 na on slave %s: dest %pI6, src %pI6\n",
|
||||
slave_dev->name, &mcaddr, daddr);
|
||||
|
||||
skb = ndisc_build_skb(slave_dev, &mcaddr, daddr, &icmp6h, daddr,
|
||||
ND_OPT_TARGET_LL_ADDR);
|
||||
|
||||
if (!skb) {
|
||||
pr_err("NA packet allocation failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (vlan_id) {
|
||||
/* The Ethernet header is not present yet, so it is
|
||||
* too early to insert a VLAN tag. Force use of an
|
||||
* out-of-line tag here and let dev_hard_start_xmit()
|
||||
* insert it if the slave hardware can't.
|
||||
*/
|
||||
skb = __vlan_hwaccel_put_tag(skb, vlan_id);
|
||||
if (!skb) {
|
||||
pr_err("failed to insert VLAN tag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ndisc_send_skb(skb, slave_dev, NULL, &mcaddr, daddr, &icmp6h);
|
||||
}
|
||||
|
||||
/*
|
||||
* Kick out an unsolicited Neighbor Advertisement for an IPv6 address on
|
||||
* the bonding master. This will help the switch learn our address
|
||||
* if in active-backup mode.
|
||||
*
|
||||
* Caller must hold curr_slave_lock for read or better
|
||||
*/
|
||||
void bond_send_unsolicited_na(struct bonding *bond)
|
||||
{
|
||||
struct slave *slave = bond->curr_active_slave;
|
||||
struct vlan_entry *vlan;
|
||||
struct inet6_dev *idev;
|
||||
int is_router;
|
||||
|
||||
pr_debug("%s: bond %s slave %s\n", bond->dev->name,
|
||||
__func__, slave ? slave->dev->name : "NULL");
|
||||
|
||||
if (!slave || !bond->send_unsol_na ||
|
||||
test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
|
||||
return;
|
||||
|
||||
bond->send_unsol_na--;
|
||||
|
||||
idev = in6_dev_get(bond->dev);
|
||||
if (!idev)
|
||||
return;
|
||||
|
||||
is_router = !!idev->cnf.forwarding;
|
||||
|
||||
in6_dev_put(idev);
|
||||
|
||||
if (!ipv6_addr_any(&bond->master_ipv6))
|
||||
bond_na_send(slave->dev, &bond->master_ipv6, is_router, 0);
|
||||
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
||||
if (!ipv6_addr_any(&vlan->vlan_ipv6)) {
|
||||
bond_na_send(slave->dev, &vlan->vlan_ipv6, is_router,
|
||||
vlan->vlan_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* bond_inet6addr_event: handle inet6addr notifier chain events.
|
||||
*
|
||||
* We keep track of device IPv6 addresses primarily to use as source
|
||||
* addresses in NS probes.
|
||||
*
|
||||
* We track one IPv6 for the main device (if it has one).
|
||||
*/
|
||||
static int bond_inet6addr_event(struct notifier_block *this,
|
||||
unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
struct inet6_ifaddr *ifa = ptr;
|
||||
struct net_device *vlan_dev, *event_dev = ifa->idev->dev;
|
||||
struct bonding *bond;
|
||||
struct vlan_entry *vlan;
|
||||
struct bond_net *bn = net_generic(dev_net(event_dev), bond_net_id);
|
||||
|
||||
list_for_each_entry(bond, &bn->dev_list, bond_list) {
|
||||
if (bond->dev == event_dev) {
|
||||
switch (event) {
|
||||
case NETDEV_UP:
|
||||
if (ipv6_addr_any(&bond->master_ipv6))
|
||||
ipv6_addr_copy(&bond->master_ipv6,
|
||||
&ifa->addr);
|
||||
return NOTIFY_OK;
|
||||
case NETDEV_DOWN:
|
||||
if (ipv6_addr_equal(&bond->master_ipv6,
|
||||
&ifa->addr))
|
||||
bond_glean_dev_ipv6(bond->dev,
|
||||
&bond->master_ipv6);
|
||||
return NOTIFY_OK;
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
||||
rcu_read_lock();
|
||||
vlan_dev = __vlan_find_dev_deep(bond->dev,
|
||||
vlan->vlan_id);
|
||||
rcu_read_unlock();
|
||||
if (vlan_dev == event_dev) {
|
||||
switch (event) {
|
||||
case NETDEV_UP:
|
||||
if (ipv6_addr_any(&vlan->vlan_ipv6))
|
||||
ipv6_addr_copy(&vlan->vlan_ipv6,
|
||||
&ifa->addr);
|
||||
return NOTIFY_OK;
|
||||
case NETDEV_DOWN:
|
||||
if (ipv6_addr_equal(&vlan->vlan_ipv6,
|
||||
&ifa->addr))
|
||||
bond_glean_dev_ipv6(vlan_dev,
|
||||
&vlan->vlan_ipv6);
|
||||
return NOTIFY_OK;
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block bond_inet6addr_notifier = {
|
||||
.notifier_call = bond_inet6addr_event,
|
||||
};
|
||||
|
||||
void bond_register_ipv6_notifier(void)
|
||||
{
|
||||
register_inet6addr_notifier(&bond_inet6addr_notifier);
|
||||
}
|
||||
|
||||
void bond_unregister_ipv6_notifier(void)
|
||||
{
|
||||
unregister_inet6addr_notifier(&bond_inet6addr_notifier);
|
||||
}
|
||||
|
|
@ -428,27 +428,34 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
|
|||
* @bond_dev: bonding net device that got called
|
||||
* @vid: vlan id being added
|
||||
*/
|
||||
static void bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
|
||||
static int bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct slave *slave;
|
||||
struct slave *slave, *stop_at;
|
||||
int i, res;
|
||||
|
||||
bond_for_each_slave(bond, slave, i) {
|
||||
struct net_device *slave_dev = slave->dev;
|
||||
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
||||
|
||||
if ((slave_dev->features & NETIF_F_HW_VLAN_FILTER) &&
|
||||
slave_ops->ndo_vlan_rx_add_vid) {
|
||||
slave_ops->ndo_vlan_rx_add_vid(slave_dev, vid);
|
||||
}
|
||||
res = vlan_vid_add(slave->dev, vid);
|
||||
if (res)
|
||||
goto unwind;
|
||||
}
|
||||
|
||||
res = bond_add_vlan(bond, vid);
|
||||
if (res) {
|
||||
pr_err("%s: Error: Failed to add vlan id %d\n",
|
||||
bond_dev->name, vid);
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unwind:
|
||||
/* unwind from head to the slave that failed */
|
||||
stop_at = slave;
|
||||
bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at)
|
||||
vlan_vid_del(slave->dev, vid);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -456,56 +463,48 @@ static void bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
|
|||
* @bond_dev: bonding net device that got called
|
||||
* @vid: vlan id being removed
|
||||
*/
|
||||
static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
|
||||
static int bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct slave *slave;
|
||||
int i, res;
|
||||
|
||||
bond_for_each_slave(bond, slave, i) {
|
||||
struct net_device *slave_dev = slave->dev;
|
||||
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
||||
|
||||
if ((slave_dev->features & NETIF_F_HW_VLAN_FILTER) &&
|
||||
slave_ops->ndo_vlan_rx_kill_vid) {
|
||||
slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vid);
|
||||
}
|
||||
}
|
||||
bond_for_each_slave(bond, slave, i)
|
||||
vlan_vid_del(slave->dev, vid);
|
||||
|
||||
res = bond_del_vlan(bond, vid);
|
||||
if (res) {
|
||||
pr_err("%s: Error: Failed to remove vlan id %d\n",
|
||||
bond_dev->name, vid);
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *slave_dev)
|
||||
{
|
||||
struct vlan_entry *vlan;
|
||||
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
||||
int res;
|
||||
|
||||
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
|
||||
!(slave_ops->ndo_vlan_rx_add_vid))
|
||||
return;
|
||||
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list)
|
||||
slave_ops->ndo_vlan_rx_add_vid(slave_dev, vlan->vlan_id);
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
||||
res = vlan_vid_add(slave_dev, vlan->vlan_id);
|
||||
if (res)
|
||||
pr_warning("%s: Failed to add vlan id %d to device %s\n",
|
||||
bond->dev->name, vlan->vlan_id,
|
||||
slave_dev->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void bond_del_vlans_from_slave(struct bonding *bond,
|
||||
struct net_device *slave_dev)
|
||||
{
|
||||
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
||||
struct vlan_entry *vlan;
|
||||
|
||||
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
|
||||
!(slave_ops->ndo_vlan_rx_kill_vid))
|
||||
return;
|
||||
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
||||
if (!vlan->vlan_id)
|
||||
continue;
|
||||
slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vlan->vlan_id);
|
||||
vlan_vid_del(slave_dev, vlan->vlan_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1325,11 +1324,12 @@ static int bond_sethwaddr(struct net_device *bond_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 bond_fix_features(struct net_device *dev, u32 features)
|
||||
static netdev_features_t bond_fix_features(struct net_device *dev,
|
||||
netdev_features_t features)
|
||||
{
|
||||
struct slave *slave;
|
||||
struct bonding *bond = netdev_priv(dev);
|
||||
u32 mask;
|
||||
netdev_features_t mask;
|
||||
int i;
|
||||
|
||||
read_lock(&bond->lock);
|
||||
|
@ -1363,7 +1363,7 @@ static void bond_compute_features(struct bonding *bond)
|
|||
{
|
||||
struct slave *slave;
|
||||
struct net_device *bond_dev = bond->dev;
|
||||
u32 vlan_features = BOND_VLAN_FEATURES;
|
||||
netdev_features_t vlan_features = BOND_VLAN_FEATURES;
|
||||
unsigned short max_hard_header_len = ETH_HLEN;
|
||||
int i;
|
||||
|
||||
|
@ -1822,7 +1822,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
"but new slave device does not support netpoll.\n",
|
||||
bond_dev->name);
|
||||
res = -EBUSY;
|
||||
goto err_close;
|
||||
goto err_detach;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1831,7 +1831,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
|
||||
res = bond_create_slave_symlinks(bond_dev, slave_dev);
|
||||
if (res)
|
||||
goto err_close;
|
||||
goto err_detach;
|
||||
|
||||
res = netdev_rx_handler_register(slave_dev, bond_handle_frame,
|
||||
new_slave);
|
||||
|
@ -1852,6 +1852,11 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
err_dest_symlinks:
|
||||
bond_destroy_slave_symlinks(bond_dev, slave_dev);
|
||||
|
||||
err_detach:
|
||||
write_lock_bh(&bond->lock);
|
||||
bond_detach_slave(bond, new_slave);
|
||||
write_unlock_bh(&bond->lock);
|
||||
|
||||
err_close:
|
||||
dev_close(slave_dev);
|
||||
|
||||
|
@ -1897,7 +1902,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct slave *slave, *oldcurrent;
|
||||
struct sockaddr addr;
|
||||
u32 old_features = bond_dev->features;
|
||||
netdev_features_t old_features = bond_dev->features;
|
||||
|
||||
/* slave is not a slave or master is not master of this slave */
|
||||
if (!(slave_dev->flags & IFF_SLAVE) ||
|
||||
|
@ -4339,7 +4344,7 @@ static void bond_setup(struct net_device *bond_dev)
|
|||
NETIF_F_HW_VLAN_RX |
|
||||
NETIF_F_HW_VLAN_FILTER;
|
||||
|
||||
bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_NO_CSUM);
|
||||
bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM);
|
||||
bond_dev->features |= bond_dev->hw_features;
|
||||
}
|
||||
|
||||
|
|
|
@ -117,15 +117,6 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi)
|
|||
dev_dbg(&cfhsi->ndev->dev, "%s.\n",
|
||||
__func__);
|
||||
|
||||
|
||||
ret = cfhsi->dev->cfhsi_wake_up(cfhsi->dev);
|
||||
if (ret) {
|
||||
dev_warn(&cfhsi->ndev->dev,
|
||||
"%s: can't wake up HSI interface: %d.\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
|
||||
&fifo_occupancy);
|
||||
|
@ -168,8 +159,6 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi)
|
|||
}
|
||||
} while (1);
|
||||
|
||||
cfhsi->dev->cfhsi_wake_down(cfhsi->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -944,7 +933,7 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
|
||||
/* Create HSI frame. */
|
||||
len = cfhsi_tx_frm(desc, cfhsi);
|
||||
BUG_ON(!len);
|
||||
WARN_ON(!len);
|
||||
|
||||
/* Set up new transfer. */
|
||||
res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev);
|
||||
|
|
|
@ -38,15 +38,15 @@ MODULE_ALIAS_LDISC(N_CAIF);
|
|||
/*This list is protected by the rtnl lock. */
|
||||
static LIST_HEAD(ser_list);
|
||||
|
||||
static int ser_loop;
|
||||
static bool ser_loop;
|
||||
module_param(ser_loop, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode.");
|
||||
|
||||
static int ser_use_stx = 1;
|
||||
static bool ser_use_stx = true;
|
||||
module_param(ser_use_stx, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(ser_use_stx, "STX enabled or not.");
|
||||
|
||||
static int ser_use_fcs = 1;
|
||||
static bool ser_use_fcs = true;
|
||||
|
||||
module_param(ser_use_fcs, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not.");
|
||||
|
@ -261,7 +261,7 @@ static int handle_tx(struct ser_device *ser)
|
|||
skb_pull(skb, tty_wr);
|
||||
if (skb->len == 0) {
|
||||
struct sk_buff *tmp = skb_dequeue(&ser->head);
|
||||
BUG_ON(tmp != skb);
|
||||
WARN_ON(tmp != skb);
|
||||
if (in_interrupt())
|
||||
dev_kfree_skb_irq(skb);
|
||||
else
|
||||
|
@ -305,7 +305,7 @@ static void ldisc_tx_wakeup(struct tty_struct *tty)
|
|||
|
||||
ser = tty->disc_data;
|
||||
BUG_ON(ser == NULL);
|
||||
BUG_ON(ser->tty != tty);
|
||||
WARN_ON(ser->tty != tty);
|
||||
handle_tx(ser);
|
||||
}
|
||||
|
||||
|
|
|
@ -238,11 +238,11 @@ int caif_shmdrv_rx_cb(u32 mbx_msg, void *priv)
|
|||
if ((avail_emptybuff > HIGH_WATERMARK) &&
|
||||
(!pshm_drv->tx_empty_available)) {
|
||||
pshm_drv->tx_empty_available = 1;
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
pshm_drv->cfdev.flowctrl
|
||||
(pshm_drv->pshm_dev->pshm_netdev,
|
||||
CAIF_FLOW_ON);
|
||||
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
/* Schedule the work queue. if required */
|
||||
if (!work_pending(&pshm_drv->shm_tx_work))
|
||||
|
@ -285,6 +285,7 @@ static void shm_rx_work_func(struct work_struct *rx_work)
|
|||
list_entry(pshm_drv->rx_full_list.next, struct buf_list,
|
||||
list);
|
||||
list_del_init(&pbuf->list);
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
/* Retrieve pointer to start of the packet descriptor area. */
|
||||
pck_desc = (struct shm_pck_desc *) pbuf->desc_vptr;
|
||||
|
@ -336,7 +337,11 @@ static void shm_rx_work_func(struct work_struct *rx_work)
|
|||
/* Get a suitable CAIF packet and copy in data. */
|
||||
skb = netdev_alloc_skb(pshm_drv->pshm_dev->pshm_netdev,
|
||||
frm_pck_len + 1);
|
||||
BUG_ON(skb == NULL);
|
||||
|
||||
if (skb == NULL) {
|
||||
pr_info("OOM: Try next frame in descriptor\n");
|
||||
break;
|
||||
}
|
||||
|
||||
p = skb_put(skb, frm_pck_len);
|
||||
memcpy(p, pbuf->desc_vptr + frm_pck_ofs, frm_pck_len);
|
||||
|
@ -360,6 +365,7 @@ static void shm_rx_work_func(struct work_struct *rx_work)
|
|||
pck_desc++;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&pshm_drv->lock, flags);
|
||||
list_add_tail(&pbuf->list, &pshm_drv->rx_pend_list);
|
||||
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
@ -412,7 +418,6 @@ static void shm_tx_work_func(struct work_struct *tx_work)
|
|||
|
||||
if (skb == NULL)
|
||||
goto send_msg;
|
||||
|
||||
/* Check the available no. of buffers in the empty list */
|
||||
list_for_each(pos, &pshm_drv->tx_empty_list)
|
||||
avail_emptybuff++;
|
||||
|
@ -421,9 +426,11 @@ static void shm_tx_work_func(struct work_struct *tx_work)
|
|||
pshm_drv->tx_empty_available) {
|
||||
/* Update blocking condition. */
|
||||
pshm_drv->tx_empty_available = 0;
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
pshm_drv->cfdev.flowctrl
|
||||
(pshm_drv->pshm_dev->pshm_netdev,
|
||||
CAIF_FLOW_OFF);
|
||||
spin_lock_irqsave(&pshm_drv->lock, flags);
|
||||
}
|
||||
/*
|
||||
* We simply return back to the caller if we do not have space
|
||||
|
@ -469,6 +476,8 @@ static void shm_tx_work_func(struct work_struct *tx_work)
|
|||
}
|
||||
|
||||
skb = skb_dequeue(&pshm_drv->sk_qhead);
|
||||
if (skb == NULL)
|
||||
break;
|
||||
/* Copy in CAIF frame. */
|
||||
skb_copy_bits(skb, 0, pbuf->desc_vptr +
|
||||
pbuf->frm_ofs + SHM_HDR_LEN +
|
||||
|
@ -477,7 +486,7 @@ static void shm_tx_work_func(struct work_struct *tx_work)
|
|||
pshm_drv->pshm_dev->pshm_netdev->stats.tx_packets++;
|
||||
pshm_drv->pshm_dev->pshm_netdev->stats.tx_bytes +=
|
||||
frmlen;
|
||||
dev_kfree_skb(skb);
|
||||
dev_kfree_skb_irq(skb);
|
||||
|
||||
/* Fill in the shared memory packet descriptor area. */
|
||||
pck_desc = (struct shm_pck_desc *) (pbuf->desc_vptr);
|
||||
|
@ -512,16 +521,11 @@ static void shm_tx_work_func(struct work_struct *tx_work)
|
|||
static int shm_netdev_tx(struct sk_buff *skb, struct net_device *shm_netdev)
|
||||
{
|
||||
struct shmdrv_layer *pshm_drv;
|
||||
unsigned long flags = 0;
|
||||
|
||||
pshm_drv = netdev_priv(shm_netdev);
|
||||
|
||||
spin_lock_irqsave(&pshm_drv->lock, flags);
|
||||
|
||||
skb_queue_tail(&pshm_drv->sk_qhead, skb);
|
||||
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
/* Schedule Tx work queue. for deferred processing of skbs*/
|
||||
if (!work_pending(&pshm_drv->shm_tx_work))
|
||||
queue_work(pshm_drv->pshm_tx_workqueue, &pshm_drv->shm_tx_work);
|
||||
|
@ -606,6 +610,7 @@ int caif_shmcore_probe(struct shmdev_layer *pshm_dev)
|
|||
pshm_drv->shm_rx_addr = pshm_dev->shm_base_addr +
|
||||
(NR_TX_BUF * TX_BUF_SZ);
|
||||
|
||||
spin_lock_init(&pshm_drv->lock);
|
||||
INIT_LIST_HEAD(&pshm_drv->tx_empty_list);
|
||||
INIT_LIST_HEAD(&pshm_drv->tx_pend_list);
|
||||
INIT_LIST_HEAD(&pshm_drv->tx_full_list);
|
||||
|
@ -640,7 +645,7 @@ int caif_shmcore_probe(struct shmdev_layer *pshm_dev)
|
|||
tx_buf->frm_ofs = SHM_CAIF_FRM_OFS;
|
||||
|
||||
if (pshm_dev->shm_loopback)
|
||||
tx_buf->desc_vptr = (char *)tx_buf->phy_addr;
|
||||
tx_buf->desc_vptr = (unsigned char *)tx_buf->phy_addr;
|
||||
else
|
||||
tx_buf->desc_vptr =
|
||||
ioremap(tx_buf->phy_addr, TX_BUF_SZ);
|
||||
|
@ -664,7 +669,7 @@ int caif_shmcore_probe(struct shmdev_layer *pshm_dev)
|
|||
rx_buf->len = RX_BUF_SZ;
|
||||
|
||||
if (pshm_dev->shm_loopback)
|
||||
rx_buf->desc_vptr = (char *)rx_buf->phy_addr;
|
||||
rx_buf->desc_vptr = (unsigned char *)rx_buf->phy_addr;
|
||||
else
|
||||
rx_buf->desc_vptr =
|
||||
ioremap(rx_buf->phy_addr, RX_BUF_SZ);
|
||||
|
|
|
@ -35,7 +35,7 @@ MODULE_DESCRIPTION("CAIF SPI driver");
|
|||
/* Returns the number of padding bytes for alignment. */
|
||||
#define PAD_POW2(x, pow) ((((x)&((pow)-1))==0) ? 0 : (((pow)-((x)&((pow)-1)))))
|
||||
|
||||
static int spi_loop;
|
||||
static bool spi_loop;
|
||||
module_param(spi_loop, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(spi_loop, "SPI running in loopback mode.");
|
||||
|
||||
|
@ -226,7 +226,7 @@ static ssize_t dbgfs_frame(struct file *file, char __user *user_buf,
|
|||
"Tx data (Len: %d):\n", cfspi->tx_cpck_len);
|
||||
|
||||
len += print_frame((buf + len), (DEBUGFS_BUF_SIZE - len),
|
||||
cfspi->xfer.va_tx,
|
||||
cfspi->xfer.va_tx[0],
|
||||
(cfspi->tx_cpck_len + SPI_CMD_SZ), 100);
|
||||
|
||||
len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
|
||||
|
@ -599,48 +599,11 @@ static int cfspi_close(struct net_device *dev)
|
|||
netif_stop_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
static const struct net_device_ops cfspi_ops = {
|
||||
.ndo_open = cfspi_open,
|
||||
.ndo_stop = cfspi_close,
|
||||
.ndo_start_xmit = cfspi_xmit
|
||||
};
|
||||
|
||||
static void cfspi_setup(struct net_device *dev)
|
||||
static int cfspi_init(struct net_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
struct cfspi *cfspi = netdev_priv(dev);
|
||||
dev->features = 0;
|
||||
dev->netdev_ops = &cfspi_ops;
|
||||
dev->type = ARPHRD_CAIF;
|
||||
dev->flags = IFF_NOARP | IFF_POINTOPOINT;
|
||||
dev->tx_queue_len = 0;
|
||||
dev->mtu = SPI_MAX_PAYLOAD_SIZE;
|
||||
dev->destructor = free_netdev;
|
||||
skb_queue_head_init(&cfspi->qhead);
|
||||
skb_queue_head_init(&cfspi->chead);
|
||||
cfspi->cfdev.link_select = CAIF_LINK_HIGH_BANDW;
|
||||
cfspi->cfdev.use_frag = false;
|
||||
cfspi->cfdev.use_stx = false;
|
||||
cfspi->cfdev.use_fcs = false;
|
||||
cfspi->ndev = dev;
|
||||
}
|
||||
|
||||
int cfspi_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cfspi *cfspi = NULL;
|
||||
struct net_device *ndev;
|
||||
struct cfspi_dev *dev;
|
||||
int res;
|
||||
dev = (struct cfspi_dev *)pdev->dev.platform_data;
|
||||
|
||||
ndev = alloc_netdev(sizeof(struct cfspi),
|
||||
"cfspi%d", cfspi_setup);
|
||||
if (!ndev)
|
||||
return -ENOMEM;
|
||||
|
||||
cfspi = netdev_priv(ndev);
|
||||
netif_stop_queue(ndev);
|
||||
cfspi->ndev = ndev;
|
||||
cfspi->pdev = pdev;
|
||||
|
||||
/* Set flow info. */
|
||||
cfspi->flow_off_sent = 0;
|
||||
|
@ -656,16 +619,11 @@ int cfspi_spi_probe(struct platform_device *pdev)
|
|||
cfspi->slave_talked = false;
|
||||
}
|
||||
|
||||
/* Assign the SPI device. */
|
||||
cfspi->dev = dev;
|
||||
/* Assign the device ifc to this SPI interface. */
|
||||
dev->ifc = &cfspi->ifc;
|
||||
|
||||
/* Allocate DMA buffers. */
|
||||
cfspi->xfer.va_tx = dma_alloc(&cfspi->xfer.pa_tx);
|
||||
if (!cfspi->xfer.va_tx) {
|
||||
cfspi->xfer.va_tx[0] = dma_alloc(&cfspi->xfer.pa_tx[0]);
|
||||
if (!cfspi->xfer.va_tx[0]) {
|
||||
res = -ENODEV;
|
||||
goto err_dma_alloc_tx;
|
||||
goto err_dma_alloc_tx_0;
|
||||
}
|
||||
|
||||
cfspi->xfer.va_rx = dma_alloc(&cfspi->xfer.pa_rx);
|
||||
|
@ -714,6 +672,87 @@ int cfspi_spi_probe(struct platform_device *pdev)
|
|||
/* Schedule the work queue. */
|
||||
queue_work(cfspi->wq, &cfspi->work);
|
||||
|
||||
return 0;
|
||||
|
||||
err_create_wq:
|
||||
dma_free(cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
|
||||
err_dma_alloc_rx:
|
||||
dma_free(cfspi->xfer.va_tx[0], cfspi->xfer.pa_tx[0]);
|
||||
err_dma_alloc_tx_0:
|
||||
return res;
|
||||
}
|
||||
|
||||
static void cfspi_uninit(struct net_device *dev)
|
||||
{
|
||||
struct cfspi *cfspi = netdev_priv(dev);
|
||||
|
||||
/* Remove from list. */
|
||||
spin_lock(&cfspi_list_lock);
|
||||
list_del(&cfspi->list);
|
||||
spin_unlock(&cfspi_list_lock);
|
||||
|
||||
cfspi->ndev = NULL;
|
||||
/* Free DMA buffers. */
|
||||
dma_free(cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
|
||||
dma_free(cfspi->xfer.va_tx[0], cfspi->xfer.pa_tx[0]);
|
||||
set_bit(SPI_TERMINATE, &cfspi->state);
|
||||
wake_up_interruptible(&cfspi->wait);
|
||||
destroy_workqueue(cfspi->wq);
|
||||
/* Destroy debugfs directory and files. */
|
||||
dev_debugfs_rem(cfspi);
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct net_device_ops cfspi_ops = {
|
||||
.ndo_open = cfspi_open,
|
||||
.ndo_stop = cfspi_close,
|
||||
.ndo_init = cfspi_init,
|
||||
.ndo_uninit = cfspi_uninit,
|
||||
.ndo_start_xmit = cfspi_xmit
|
||||
};
|
||||
|
||||
static void cfspi_setup(struct net_device *dev)
|
||||
{
|
||||
struct cfspi *cfspi = netdev_priv(dev);
|
||||
dev->features = 0;
|
||||
dev->netdev_ops = &cfspi_ops;
|
||||
dev->type = ARPHRD_CAIF;
|
||||
dev->flags = IFF_NOARP | IFF_POINTOPOINT;
|
||||
dev->tx_queue_len = 0;
|
||||
dev->mtu = SPI_MAX_PAYLOAD_SIZE;
|
||||
dev->destructor = free_netdev;
|
||||
skb_queue_head_init(&cfspi->qhead);
|
||||
skb_queue_head_init(&cfspi->chead);
|
||||
cfspi->cfdev.link_select = CAIF_LINK_HIGH_BANDW;
|
||||
cfspi->cfdev.use_frag = false;
|
||||
cfspi->cfdev.use_stx = false;
|
||||
cfspi->cfdev.use_fcs = false;
|
||||
cfspi->ndev = dev;
|
||||
}
|
||||
|
||||
int cfspi_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cfspi *cfspi = NULL;
|
||||
struct net_device *ndev;
|
||||
struct cfspi_dev *dev;
|
||||
int res;
|
||||
dev = (struct cfspi_dev *)pdev->dev.platform_data;
|
||||
|
||||
ndev = alloc_netdev(sizeof(struct cfspi),
|
||||
"cfspi%d", cfspi_setup);
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
cfspi = netdev_priv(ndev);
|
||||
netif_stop_queue(ndev);
|
||||
cfspi->ndev = ndev;
|
||||
cfspi->pdev = pdev;
|
||||
|
||||
/* Assign the SPI device. */
|
||||
cfspi->dev = dev;
|
||||
/* Assign the device ifc to this SPI interface. */
|
||||
dev->ifc = &cfspi->ifc;
|
||||
|
||||
/* Register network device. */
|
||||
res = register_netdev(ndev);
|
||||
if (res) {
|
||||
|
@ -723,15 +762,6 @@ int cfspi_spi_probe(struct platform_device *pdev)
|
|||
return res;
|
||||
|
||||
err_net_reg:
|
||||
dev_debugfs_rem(cfspi);
|
||||
set_bit(SPI_TERMINATE, &cfspi->state);
|
||||
wake_up_interruptible(&cfspi->wait);
|
||||
destroy_workqueue(cfspi->wq);
|
||||
err_create_wq:
|
||||
dma_free(cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
|
||||
err_dma_alloc_rx:
|
||||
dma_free(cfspi->xfer.va_tx, cfspi->xfer.pa_tx);
|
||||
err_dma_alloc_tx:
|
||||
free_netdev(ndev);
|
||||
|
||||
return res;
|
||||
|
@ -739,34 +769,8 @@ int cfspi_spi_probe(struct platform_device *pdev)
|
|||
|
||||
int cfspi_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct list_head *list_node;
|
||||
struct list_head *n;
|
||||
struct cfspi *cfspi = NULL;
|
||||
struct cfspi_dev *dev;
|
||||
|
||||
dev = (struct cfspi_dev *)pdev->dev.platform_data;
|
||||
spin_lock(&cfspi_list_lock);
|
||||
list_for_each_safe(list_node, n, &cfspi_list) {
|
||||
cfspi = list_entry(list_node, struct cfspi, list);
|
||||
/* Find the corresponding device. */
|
||||
if (cfspi->dev == dev) {
|
||||
/* Remove from list. */
|
||||
list_del(list_node);
|
||||
/* Free DMA buffers. */
|
||||
dma_free(cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
|
||||
dma_free(cfspi->xfer.va_tx, cfspi->xfer.pa_tx);
|
||||
set_bit(SPI_TERMINATE, &cfspi->state);
|
||||
wake_up_interruptible(&cfspi->wait);
|
||||
destroy_workqueue(cfspi->wq);
|
||||
/* Destroy debugfs directory and files. */
|
||||
dev_debugfs_rem(cfspi);
|
||||
unregister_netdev(cfspi->ndev);
|
||||
spin_unlock(&cfspi_list_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
spin_unlock(&cfspi_list_lock);
|
||||
return -ENODEV;
|
||||
/* Everything is done in cfspi_uninit(). */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cfspi_exit_module(void)
|
||||
|
@ -777,7 +781,7 @@ static void __exit cfspi_exit_module(void)
|
|||
|
||||
list_for_each_safe(list_node, n, &cfspi_list) {
|
||||
cfspi = list_entry(list_node, struct cfspi, list);
|
||||
platform_device_unregister(cfspi->pdev);
|
||||
unregister_netdev(cfspi->ndev);
|
||||
}
|
||||
|
||||
/* Destroy sysfs files. */
|
||||
|
|
|
@ -116,6 +116,8 @@ source "drivers/net/can/sja1000/Kconfig"
|
|||
|
||||
source "drivers/net/can/c_can/Kconfig"
|
||||
|
||||
source "drivers/net/can/cc770/Kconfig"
|
||||
|
||||
source "drivers/net/can/usb/Kconfig"
|
||||
|
||||
source "drivers/net/can/softing/Kconfig"
|
||||
|
|
|
@ -14,6 +14,7 @@ obj-y += softing/
|
|||
obj-$(CONFIG_CAN_SJA1000) += sja1000/
|
||||
obj-$(CONFIG_CAN_MSCAN) += mscan/
|
||||
obj-$(CONFIG_CAN_C_CAN) += c_can/
|
||||
obj-$(CONFIG_CAN_CC770) += cc770/
|
||||
obj-$(CONFIG_CAN_AT91) += at91_can.o
|
||||
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
|
||||
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
|
||||
|
|
|
@ -1383,18 +1383,7 @@ static struct platform_driver at91_can_driver = {
|
|||
.id_table = at91_can_id_table,
|
||||
};
|
||||
|
||||
static int __init at91_can_module_init(void)
|
||||
{
|
||||
return platform_driver_register(&at91_can_driver);
|
||||
}
|
||||
|
||||
static void __exit at91_can_module_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&at91_can_driver);
|
||||
}
|
||||
|
||||
module_init(at91_can_module_init);
|
||||
module_exit(at91_can_module_exit);
|
||||
module_platform_driver(at91_can_driver);
|
||||
|
||||
MODULE_AUTHOR("Marc Kleine-Budde <mkl@pengutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -676,17 +676,7 @@ static struct platform_driver bfin_can_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init bfin_can_init(void)
|
||||
{
|
||||
return platform_driver_register(&bfin_can_driver);
|
||||
}
|
||||
module_init(bfin_can_init);
|
||||
|
||||
static void __exit bfin_can_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bfin_can_driver);
|
||||
}
|
||||
module_exit(bfin_can_exit);
|
||||
module_platform_driver(bfin_can_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -197,17 +197,7 @@ static struct platform_driver c_can_plat_driver = {
|
|||
.remove = __devexit_p(c_can_plat_remove),
|
||||
};
|
||||
|
||||
static int __init c_can_plat_init(void)
|
||||
{
|
||||
return platform_driver_register(&c_can_plat_driver);
|
||||
}
|
||||
module_init(c_can_plat_init);
|
||||
|
||||
static void __exit c_can_plat_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&c_can_plat_driver);
|
||||
}
|
||||
module_exit(c_can_plat_exit);
|
||||
module_platform_driver(c_can_plat_driver);
|
||||
|
||||
MODULE_AUTHOR("Bhupesh Sharma <bhupesh.sharma@st.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
menuconfig CAN_CC770
|
||||
tristate "Bosch CC770 and Intel AN82527 devices"
|
||||
depends on CAN_DEV && HAS_IOMEM
|
||||
|
||||
if CAN_CC770
|
||||
|
||||
config CAN_CC770_ISA
|
||||
tristate "ISA Bus based legacy CC770 driver"
|
||||
---help---
|
||||
This driver adds legacy support for CC770 and AN82527 chips
|
||||
connected to the ISA bus using I/O port, memory mapped or
|
||||
indirect access.
|
||||
|
||||
config CAN_CC770_PLATFORM
|
||||
tristate "Generic Platform Bus based CC770 driver"
|
||||
---help---
|
||||
This driver adds support for the CC770 and AN82527 chips
|
||||
connected to the "platform bus" (Linux abstraction for directly
|
||||
to the processor attached devices).
|
||||
|
||||
endif
|
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# Makefile for the Bosch CC770 CAN controller drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CAN_CC770) += cc770.o
|
||||
obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o
|
||||
obj-$(CONFIG_CAN_CC770_PLATFORM) += cc770_platform.o
|
||||
|
||||
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
|
@ -0,0 +1,881 @@
|
|||
/*
|
||||
* Core driver for the CC770 and AN82527 CAN controllers
|
||||
*
|
||||
* Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/error.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/platform/cc770.h>
|
||||
|
||||
#include "cc770.h"
|
||||
|
||||
MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION(KBUILD_MODNAME "CAN netdevice driver");
|
||||
|
||||
/*
|
||||
* The CC770 is a CAN controller from Bosch, which is 100% compatible
|
||||
* with the AN82527 from Intel, but with "bugs" being fixed and some
|
||||
* additional functionality, mainly:
|
||||
*
|
||||
* 1. RX and TX error counters are readable.
|
||||
* 2. Support of silent (listen-only) mode.
|
||||
* 3. Message object 15 can receive all types of frames, also RTR and EFF.
|
||||
*
|
||||
* Details are available from Bosch's "CC770_Product_Info_2007-01.pdf",
|
||||
* which explains in detail the compatibility between the CC770 and the
|
||||
* 82527. This driver use the additional functionality 3. on real CC770
|
||||
* devices. Unfortunately, the CC770 does still not store the message
|
||||
* identifier of received remote transmission request frames and
|
||||
* therefore it's set to 0.
|
||||
*
|
||||
* The message objects 1..14 can be used for TX and RX while the message
|
||||
* objects 15 is optimized for RX. It has a shadow register for reliable
|
||||
* data receiption under heavy bus load. Therefore it makes sense to use
|
||||
* this message object for the needed use case. The frame type (EFF/SFF)
|
||||
* for the message object 15 can be defined via kernel module parameter
|
||||
* "msgobj15_eff". If not equal 0, it will receive 29-bit EFF frames,
|
||||
* otherwise 11 bit SFF messages.
|
||||
*/
|
||||
static int msgobj15_eff;
|
||||
module_param(msgobj15_eff, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(msgobj15_eff, "Extended 29-bit frames for message object 15 "
|
||||
"(default: 11-bit standard frames)");
|
||||
|
||||
static int i82527_compat;
|
||||
module_param(i82527_compat, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 comptibility mode "
|
||||
"without using additional functions");
|
||||
|
||||
/*
|
||||
* This driver uses the last 5 message objects 11..15. The definitions
|
||||
* and structure below allows to configure and assign them to the real
|
||||
* message object.
|
||||
*/
|
||||
static unsigned char cc770_obj_flags[CC770_OBJ_MAX] = {
|
||||
[CC770_OBJ_RX0] = CC770_OBJ_FLAG_RX,
|
||||
[CC770_OBJ_RX1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_EFF,
|
||||
[CC770_OBJ_RX_RTR0] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR,
|
||||
[CC770_OBJ_RX_RTR1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR |
|
||||
CC770_OBJ_FLAG_EFF,
|
||||
[CC770_OBJ_TX] = 0,
|
||||
};
|
||||
|
||||
static struct can_bittiming_const cc770_bittiming_const = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.tseg1_min = 1,
|
||||
.tseg1_max = 16,
|
||||
.tseg2_min = 1,
|
||||
.tseg2_max = 8,
|
||||
.sjw_max = 4,
|
||||
.brp_min = 1,
|
||||
.brp_max = 64,
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
static inline int intid2obj(unsigned int intid)
|
||||
{
|
||||
if (intid == 2)
|
||||
return 0;
|
||||
else
|
||||
return MSGOBJ_LAST + 2 - intid;
|
||||
}
|
||||
|
||||
static void enable_all_objs(const struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
u8 msgcfg;
|
||||
unsigned char obj_flags;
|
||||
unsigned int o, mo;
|
||||
|
||||
for (o = 0; o < ARRAY_SIZE(priv->obj_flags); o++) {
|
||||
obj_flags = priv->obj_flags[o];
|
||||
mo = obj2msgobj(o);
|
||||
|
||||
if (obj_flags & CC770_OBJ_FLAG_RX) {
|
||||
/*
|
||||
* We don't need extra objects for RTR and EFF if
|
||||
* the additional CC770 functions are enabled.
|
||||
*/
|
||||
if (priv->control_normal_mode & CTRL_EAF) {
|
||||
if (o > 0)
|
||||
continue;
|
||||
netdev_dbg(dev, "Message object %d for "
|
||||
"RX data, RTR, SFF and EFF\n", mo);
|
||||
} else {
|
||||
netdev_dbg(dev,
|
||||
"Message object %d for RX %s %s\n",
|
||||
mo, obj_flags & CC770_OBJ_FLAG_RTR ?
|
||||
"RTR" : "data",
|
||||
obj_flags & CC770_OBJ_FLAG_EFF ?
|
||||
"EFF" : "SFF");
|
||||
}
|
||||
|
||||
if (obj_flags & CC770_OBJ_FLAG_EFF)
|
||||
msgcfg = MSGCFG_XTD;
|
||||
else
|
||||
msgcfg = 0;
|
||||
if (obj_flags & CC770_OBJ_FLAG_RTR)
|
||||
msgcfg |= MSGCFG_DIR;
|
||||
|
||||
cc770_write_reg(priv, msgobj[mo].config, msgcfg);
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_SET | TXIE_RES |
|
||||
RXIE_SET | INTPND_RES);
|
||||
|
||||
if (obj_flags & CC770_OBJ_FLAG_RTR)
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
NEWDAT_RES | CPUUPD_SET |
|
||||
TXRQST_RES | RMTPND_RES);
|
||||
else
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
NEWDAT_RES | MSGLST_RES |
|
||||
TXRQST_RES | RMTPND_RES);
|
||||
} else {
|
||||
netdev_dbg(dev, "Message object %d for "
|
||||
"TX data, RTR, SFF and EFF\n", mo);
|
||||
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
RMTPND_RES | TXRQST_RES |
|
||||
CPUUPD_RES | NEWDAT_RES);
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_RES | TXIE_RES |
|
||||
RXIE_RES | INTPND_RES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void disable_all_objs(const struct cc770_priv *priv)
|
||||
{
|
||||
int o, mo;
|
||||
|
||||
for (o = 0; o < ARRAY_SIZE(priv->obj_flags); o++) {
|
||||
mo = obj2msgobj(o);
|
||||
|
||||
if (priv->obj_flags[o] & CC770_OBJ_FLAG_RX) {
|
||||
if (o > 0 && priv->control_normal_mode & CTRL_EAF)
|
||||
continue;
|
||||
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
NEWDAT_RES | MSGLST_RES |
|
||||
TXRQST_RES | RMTPND_RES);
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_RES | TXIE_RES |
|
||||
RXIE_RES | INTPND_RES);
|
||||
} else {
|
||||
/* Clear message object for send */
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
RMTPND_RES | TXRQST_RES |
|
||||
CPUUPD_RES | NEWDAT_RES);
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_RES | TXIE_RES |
|
||||
RXIE_RES | INTPND_RES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void set_reset_mode(struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* Enable configuration and puts chip in bus-off, disable interrupts */
|
||||
cc770_write_reg(priv, control, CTRL_CCE | CTRL_INI);
|
||||
|
||||
priv->can.state = CAN_STATE_STOPPED;
|
||||
|
||||
/* Clear interrupts */
|
||||
cc770_read_reg(priv, interrupt);
|
||||
|
||||
/* Clear status register */
|
||||
cc770_write_reg(priv, status, 0);
|
||||
|
||||
/* Disable all used message objects */
|
||||
disable_all_objs(priv);
|
||||
}
|
||||
|
||||
static void set_normal_mode(struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* Clear interrupts */
|
||||
cc770_read_reg(priv, interrupt);
|
||||
|
||||
/* Clear status register and pre-set last error code */
|
||||
cc770_write_reg(priv, status, STAT_LEC_MASK);
|
||||
|
||||
/* Enable all used message objects*/
|
||||
enable_all_objs(dev);
|
||||
|
||||
/*
|
||||
* Clear bus-off, interrupts only for errors,
|
||||
* not for status change
|
||||
*/
|
||||
cc770_write_reg(priv, control, priv->control_normal_mode);
|
||||
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
|
||||
static void chipset_init(struct cc770_priv *priv)
|
||||
{
|
||||
int mo, id, data;
|
||||
|
||||
/* Enable configuration and put chip in bus-off, disable interrupts */
|
||||
cc770_write_reg(priv, control, (CTRL_CCE | CTRL_INI));
|
||||
|
||||
/* Set CLKOUT divider and slew rates */
|
||||
cc770_write_reg(priv, clkout, priv->clkout);
|
||||
|
||||
/* Configure CPU interface / CLKOUT enable */
|
||||
cc770_write_reg(priv, cpu_interface, priv->cpu_interface);
|
||||
|
||||
/* Set bus configuration */
|
||||
cc770_write_reg(priv, bus_config, priv->bus_config);
|
||||
|
||||
/* Clear interrupts */
|
||||
cc770_read_reg(priv, interrupt);
|
||||
|
||||
/* Clear status register */
|
||||
cc770_write_reg(priv, status, 0);
|
||||
|
||||
/* Clear and invalidate message objects */
|
||||
for (mo = MSGOBJ_FIRST; mo <= MSGOBJ_LAST; mo++) {
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
INTPND_UNC | RXIE_RES |
|
||||
TXIE_RES | MSGVAL_RES);
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
INTPND_RES | RXIE_RES |
|
||||
TXIE_RES | MSGVAL_RES);
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
NEWDAT_RES | MSGLST_RES |
|
||||
TXRQST_RES | RMTPND_RES);
|
||||
for (data = 0; data < 8; data++)
|
||||
cc770_write_reg(priv, msgobj[mo].data[data], 0);
|
||||
for (id = 0; id < 4; id++)
|
||||
cc770_write_reg(priv, msgobj[mo].id[id], 0);
|
||||
cc770_write_reg(priv, msgobj[mo].config, 0);
|
||||
}
|
||||
|
||||
/* Set all global ID masks to "don't care" */
|
||||
cc770_write_reg(priv, global_mask_std[0], 0);
|
||||
cc770_write_reg(priv, global_mask_std[1], 0);
|
||||
cc770_write_reg(priv, global_mask_ext[0], 0);
|
||||
cc770_write_reg(priv, global_mask_ext[1], 0);
|
||||
cc770_write_reg(priv, global_mask_ext[2], 0);
|
||||
cc770_write_reg(priv, global_mask_ext[3], 0);
|
||||
|
||||
}
|
||||
|
||||
static int cc770_probe_chip(struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* Enable configuration, put chip in bus-off, disable ints */
|
||||
cc770_write_reg(priv, control, CTRL_CCE | CTRL_EAF | CTRL_INI);
|
||||
/* Configure cpu interface / CLKOUT disable */
|
||||
cc770_write_reg(priv, cpu_interface, priv->cpu_interface);
|
||||
|
||||
/*
|
||||
* Check if hardware reset is still inactive or maybe there
|
||||
* is no chip in this address space
|
||||
*/
|
||||
if (cc770_read_reg(priv, cpu_interface) & CPUIF_RST) {
|
||||
netdev_info(dev, "probing @0x%p failed (reset)\n",
|
||||
priv->reg_base);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Write and read back test pattern (some arbitrary values) */
|
||||
cc770_write_reg(priv, msgobj[1].data[1], 0x25);
|
||||
cc770_write_reg(priv, msgobj[2].data[3], 0x52);
|
||||
cc770_write_reg(priv, msgobj[10].data[6], 0xc3);
|
||||
if ((cc770_read_reg(priv, msgobj[1].data[1]) != 0x25) ||
|
||||
(cc770_read_reg(priv, msgobj[2].data[3]) != 0x52) ||
|
||||
(cc770_read_reg(priv, msgobj[10].data[6]) != 0xc3)) {
|
||||
netdev_info(dev, "probing @0x%p failed (pattern)\n",
|
||||
priv->reg_base);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Check if this chip is a CC770 supporting additional functions */
|
||||
if (cc770_read_reg(priv, control) & CTRL_EAF)
|
||||
priv->control_normal_mode |= CTRL_EAF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cc770_start(struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* leave reset mode */
|
||||
if (priv->can.state != CAN_STATE_STOPPED)
|
||||
set_reset_mode(dev);
|
||||
|
||||
/* leave reset mode */
|
||||
set_normal_mode(dev);
|
||||
}
|
||||
|
||||
static int cc770_set_mode(struct net_device *dev, enum can_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case CAN_MODE_START:
|
||||
cc770_start(dev);
|
||||
netif_wake_queue(dev);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc770_set_bittiming(struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
struct can_bittiming *bt = &priv->can.bittiming;
|
||||
u8 btr0, btr1;
|
||||
|
||||
btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
|
||||
btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
|
||||
(((bt->phase_seg2 - 1) & 0x7) << 4);
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
|
||||
btr1 |= 0x80;
|
||||
|
||||
netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
|
||||
|
||||
cc770_write_reg(priv, bit_timing_0, btr0);
|
||||
cc770_write_reg(priv, bit_timing_1, btr1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc770_get_berr_counter(const struct net_device *dev,
|
||||
struct can_berr_counter *bec)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
|
||||
bec->txerr = cc770_read_reg(priv, tx_error_counter);
|
||||
bec->rxerr = cc770_read_reg(priv, rx_error_counter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||
unsigned int mo = obj2msgobj(CC770_OBJ_TX);
|
||||
u8 dlc, rtr;
|
||||
u32 id;
|
||||
int i;
|
||||
|
||||
if (can_dropped_invalid_skb(dev, skb))
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
if ((cc770_read_reg(priv,
|
||||
msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) {
|
||||
netdev_err(dev, "TX register is still occupied!\n");
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
dlc = cf->can_dlc;
|
||||
id = cf->can_id;
|
||||
if (cf->can_id & CAN_RTR_FLAG)
|
||||
rtr = 0;
|
||||
else
|
||||
rtr = MSGCFG_DIR;
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES);
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_SET | TXIE_SET | RXIE_RES | INTPND_RES);
|
||||
if (id & CAN_EFF_FLAG) {
|
||||
id &= CAN_EFF_MASK;
|
||||
cc770_write_reg(priv, msgobj[mo].config,
|
||||
(dlc << 4) | rtr | MSGCFG_XTD);
|
||||
cc770_write_reg(priv, msgobj[mo].id[3], id << 3);
|
||||
cc770_write_reg(priv, msgobj[mo].id[2], id >> 5);
|
||||
cc770_write_reg(priv, msgobj[mo].id[1], id >> 13);
|
||||
cc770_write_reg(priv, msgobj[mo].id[0], id >> 21);
|
||||
} else {
|
||||
id &= CAN_SFF_MASK;
|
||||
cc770_write_reg(priv, msgobj[mo].config, (dlc << 4) | rtr);
|
||||
cc770_write_reg(priv, msgobj[mo].id[0], id >> 3);
|
||||
cc770_write_reg(priv, msgobj[mo].id[1], id << 5);
|
||||
}
|
||||
|
||||
for (i = 0; i < dlc; i++)
|
||||
cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]);
|
||||
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC);
|
||||
|
||||
stats->tx_bytes += dlc;
|
||||
|
||||
can_put_echo_skb(skb, dev, 0);
|
||||
|
||||
/*
|
||||
* HM: We had some cases of repeated IRQs so make sure the
|
||||
* INT is acknowledged I know it's already further up, but
|
||||
* doing again fixed the issue
|
||||
*/
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static void cc770_rx(struct net_device *dev, unsigned int mo, u8 ctrl1)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
u8 config;
|
||||
u32 id;
|
||||
int i;
|
||||
|
||||
skb = alloc_can_skb(dev, &cf);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
config = cc770_read_reg(priv, msgobj[mo].config);
|
||||
|
||||
if (ctrl1 & RMTPND_SET) {
|
||||
/*
|
||||
* Unfortunately, the chip does not store the real message
|
||||
* identifier of the received remote transmission request
|
||||
* frame. Therefore we set it to 0.
|
||||
*/
|
||||
cf->can_id = CAN_RTR_FLAG;
|
||||
if (config & MSGCFG_XTD)
|
||||
cf->can_id |= CAN_EFF_FLAG;
|
||||
cf->can_dlc = 0;
|
||||
} else {
|
||||
if (config & MSGCFG_XTD) {
|
||||
id = cc770_read_reg(priv, msgobj[mo].id[3]);
|
||||
id |= cc770_read_reg(priv, msgobj[mo].id[2]) << 8;
|
||||
id |= cc770_read_reg(priv, msgobj[mo].id[1]) << 16;
|
||||
id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 24;
|
||||
id >>= 3;
|
||||
id |= CAN_EFF_FLAG;
|
||||
} else {
|
||||
id = cc770_read_reg(priv, msgobj[mo].id[1]);
|
||||
id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 8;
|
||||
id >>= 5;
|
||||
}
|
||||
|
||||
cf->can_id = id;
|
||||
cf->can_dlc = get_can_dlc((config & 0xf0) >> 4);
|
||||
for (i = 0; i < cf->can_dlc; i++)
|
||||
cf->data[i] = cc770_read_reg(priv, msgobj[mo].data[i]);
|
||||
}
|
||||
netif_rx(skb);
|
||||
|
||||
stats->rx_packets++;
|
||||
stats->rx_bytes += cf->can_dlc;
|
||||
}
|
||||
|
||||
static int cc770_err(struct net_device *dev, u8 status)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
u8 lec;
|
||||
|
||||
netdev_dbg(dev, "status interrupt (%#x)\n", status);
|
||||
|
||||
skb = alloc_can_err_skb(dev, &cf);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Use extended functions of the CC770 */
|
||||
if (priv->control_normal_mode & CTRL_EAF) {
|
||||
cf->data[6] = cc770_read_reg(priv, tx_error_counter);
|
||||
cf->data[7] = cc770_read_reg(priv, rx_error_counter);
|
||||
}
|
||||
|
||||
if (status & STAT_BOFF) {
|
||||
/* Disable interrupts */
|
||||
cc770_write_reg(priv, control, CTRL_INI);
|
||||
cf->can_id |= CAN_ERR_BUSOFF;
|
||||
priv->can.state = CAN_STATE_BUS_OFF;
|
||||
can_bus_off(dev);
|
||||
} else if (status & STAT_WARN) {
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
/* Only the CC770 does show error passive */
|
||||
if (cf->data[7] > 127) {
|
||||
cf->data[1] = CAN_ERR_CRTL_RX_PASSIVE |
|
||||
CAN_ERR_CRTL_TX_PASSIVE;
|
||||
priv->can.state = CAN_STATE_ERROR_PASSIVE;
|
||||
priv->can.can_stats.error_passive++;
|
||||
} else {
|
||||
cf->data[1] = CAN_ERR_CRTL_RX_WARNING |
|
||||
CAN_ERR_CRTL_TX_WARNING;
|
||||
priv->can.state = CAN_STATE_ERROR_WARNING;
|
||||
priv->can.can_stats.error_warning++;
|
||||
}
|
||||
} else {
|
||||
/* Back to error avtive */
|
||||
cf->can_id |= CAN_ERR_PROT;
|
||||
cf->data[2] = CAN_ERR_PROT_ACTIVE;
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
|
||||
lec = status & STAT_LEC_MASK;
|
||||
if (lec < 7 && lec > 0) {
|
||||
if (lec == STAT_LEC_ACK) {
|
||||
cf->can_id |= CAN_ERR_ACK;
|
||||
} else {
|
||||
cf->can_id |= CAN_ERR_PROT;
|
||||
switch (lec) {
|
||||
case STAT_LEC_STUFF:
|
||||
cf->data[2] |= CAN_ERR_PROT_STUFF;
|
||||
break;
|
||||
case STAT_LEC_FORM:
|
||||
cf->data[2] |= CAN_ERR_PROT_FORM;
|
||||
break;
|
||||
case STAT_LEC_BIT1:
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT1;
|
||||
break;
|
||||
case STAT_LEC_BIT0:
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT0;
|
||||
break;
|
||||
case STAT_LEC_CRC:
|
||||
cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
netif_rx(skb);
|
||||
|
||||
stats->rx_packets++;
|
||||
stats->rx_bytes += cf->can_dlc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc770_status_interrupt(struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
u8 status;
|
||||
|
||||
status = cc770_read_reg(priv, status);
|
||||
/* Reset the status register including RXOK and TXOK */
|
||||
cc770_write_reg(priv, status, STAT_LEC_MASK);
|
||||
|
||||
if (status & (STAT_WARN | STAT_BOFF) ||
|
||||
(status & STAT_LEC_MASK) != STAT_LEC_MASK) {
|
||||
cc770_err(dev, status);
|
||||
return status & STAT_BOFF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cc770_rx_interrupt(struct net_device *dev, unsigned int o)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
unsigned int mo = obj2msgobj(o);
|
||||
u8 ctrl1;
|
||||
int n = CC770_MAX_MSG;
|
||||
|
||||
while (n--) {
|
||||
ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1);
|
||||
|
||||
if (!(ctrl1 & NEWDAT_SET)) {
|
||||
/* Check for RTR if additional functions are enabled */
|
||||
if (priv->control_normal_mode & CTRL_EAF) {
|
||||
if (!(cc770_read_reg(priv, msgobj[mo].ctrl0) &
|
||||
INTPND_SET))
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctrl1 & MSGLST_SET) {
|
||||
stats->rx_over_errors++;
|
||||
stats->rx_errors++;
|
||||
}
|
||||
if (mo < MSGOBJ_LAST)
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
NEWDAT_RES | MSGLST_RES |
|
||||
TXRQST_UNC | RMTPND_UNC);
|
||||
cc770_rx(dev, mo, ctrl1);
|
||||
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_SET | TXIE_RES |
|
||||
RXIE_SET | INTPND_RES);
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
NEWDAT_RES | MSGLST_RES |
|
||||
TXRQST_RES | RMTPND_RES);
|
||||
}
|
||||
}
|
||||
|
||||
static void cc770_rtr_interrupt(struct net_device *dev, unsigned int o)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
unsigned int mo = obj2msgobj(o);
|
||||
u8 ctrl0, ctrl1;
|
||||
int n = CC770_MAX_MSG;
|
||||
|
||||
while (n--) {
|
||||
ctrl0 = cc770_read_reg(priv, msgobj[mo].ctrl0);
|
||||
if (!(ctrl0 & INTPND_SET))
|
||||
break;
|
||||
|
||||
ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1);
|
||||
cc770_rx(dev, mo, ctrl1);
|
||||
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_SET | TXIE_RES |
|
||||
RXIE_SET | INTPND_RES);
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
||||
NEWDAT_RES | CPUUPD_SET |
|
||||
TXRQST_RES | RMTPND_RES);
|
||||
}
|
||||
}
|
||||
|
||||
static void cc770_tx_interrupt(struct net_device *dev, unsigned int o)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
unsigned int mo = obj2msgobj(o);
|
||||
|
||||
/* Nothing more to send, switch off interrupts */
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES);
|
||||
/*
|
||||
* We had some cases of repeated IRQ so make sure the
|
||||
* INT is acknowledged
|
||||
*/
|
||||
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
||||
MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
|
||||
|
||||
stats->tx_packets++;
|
||||
can_get_echo_skb(dev, 0);
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
irqreturn_t cc770_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)dev_id;
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
u8 intid;
|
||||
int o, n = 0;
|
||||
|
||||
/* Shared interrupts and IRQ off? */
|
||||
if (priv->can.state == CAN_STATE_STOPPED)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (priv->pre_irq)
|
||||
priv->pre_irq(priv);
|
||||
|
||||
while (n < CC770_MAX_IRQ) {
|
||||
/* Read the highest pending interrupt request */
|
||||
intid = cc770_read_reg(priv, interrupt);
|
||||
if (!intid)
|
||||
break;
|
||||
n++;
|
||||
|
||||
if (intid == 1) {
|
||||
/* Exit in case of bus-off */
|
||||
if (cc770_status_interrupt(dev))
|
||||
break;
|
||||
} else {
|
||||
o = intid2obj(intid);
|
||||
|
||||
if (o >= CC770_OBJ_MAX) {
|
||||
netdev_err(dev, "Unexpected interrupt id %d\n",
|
||||
intid);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (priv->obj_flags[o] & CC770_OBJ_FLAG_RTR)
|
||||
cc770_rtr_interrupt(dev, o);
|
||||
else if (priv->obj_flags[o] & CC770_OBJ_FLAG_RX)
|
||||
cc770_rx_interrupt(dev, o);
|
||||
else
|
||||
cc770_tx_interrupt(dev, o);
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->post_irq)
|
||||
priv->post_irq(priv);
|
||||
|
||||
if (n >= CC770_MAX_IRQ)
|
||||
netdev_dbg(dev, "%d messages handled in ISR", n);
|
||||
|
||||
return (n) ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static int cc770_open(struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
/* set chip into reset mode */
|
||||
set_reset_mode(dev);
|
||||
|
||||
/* common open */
|
||||
err = open_candev(dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = request_irq(dev->irq, &cc770_interrupt, priv->irq_flags,
|
||||
dev->name, dev);
|
||||
if (err) {
|
||||
close_candev(dev);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* init and start chip */
|
||||
cc770_start(dev);
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc770_close(struct net_device *dev)
|
||||
{
|
||||
netif_stop_queue(dev);
|
||||
set_reset_mode(dev);
|
||||
|
||||
free_irq(dev->irq, dev);
|
||||
close_candev(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct net_device *alloc_cc770dev(int sizeof_priv)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct cc770_priv *priv;
|
||||
|
||||
dev = alloc_candev(sizeof(struct cc770_priv) + sizeof_priv,
|
||||
CC770_ECHO_SKB_MAX);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
|
||||
priv->dev = dev;
|
||||
priv->can.bittiming_const = &cc770_bittiming_const;
|
||||
priv->can.do_set_bittiming = cc770_set_bittiming;
|
||||
priv->can.do_set_mode = cc770_set_mode;
|
||||
priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
|
||||
|
||||
memcpy(priv->obj_flags, cc770_obj_flags, sizeof(cc770_obj_flags));
|
||||
|
||||
if (sizeof_priv)
|
||||
priv->priv = (void *)priv + sizeof(struct cc770_priv);
|
||||
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(alloc_cc770dev);
|
||||
|
||||
void free_cc770dev(struct net_device *dev)
|
||||
{
|
||||
free_candev(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(free_cc770dev);
|
||||
|
||||
static const struct net_device_ops cc770_netdev_ops = {
|
||||
.ndo_open = cc770_open,
|
||||
.ndo_stop = cc770_close,
|
||||
.ndo_start_xmit = cc770_start_xmit,
|
||||
};
|
||||
|
||||
int register_cc770dev(struct net_device *dev)
|
||||
{
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
err = cc770_probe_chip(dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dev->netdev_ops = &cc770_netdev_ops;
|
||||
|
||||
dev->flags |= IFF_ECHO; /* we support local echo */
|
||||
|
||||
/* Should we use additional functions? */
|
||||
if (!i82527_compat && priv->control_normal_mode & CTRL_EAF) {
|
||||
priv->can.do_get_berr_counter = cc770_get_berr_counter;
|
||||
priv->control_normal_mode = CTRL_IE | CTRL_EAF | CTRL_EIE;
|
||||
netdev_dbg(dev, "i82527 mode with additional functions\n");
|
||||
} else {
|
||||
priv->control_normal_mode = CTRL_IE | CTRL_EIE;
|
||||
netdev_dbg(dev, "strict i82527 compatibility mode\n");
|
||||
}
|
||||
|
||||
chipset_init(priv);
|
||||
set_reset_mode(dev);
|
||||
|
||||
return register_candev(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_cc770dev);
|
||||
|
||||
void unregister_cc770dev(struct net_device *dev)
|
||||
{
|
||||
set_reset_mode(dev);
|
||||
unregister_candev(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_cc770dev);
|
||||
|
||||
static __init int cc770_init(void)
|
||||
{
|
||||
if (msgobj15_eff) {
|
||||
cc770_obj_flags[CC770_OBJ_RX0] |= CC770_OBJ_FLAG_EFF;
|
||||
cc770_obj_flags[CC770_OBJ_RX1] &= ~CC770_OBJ_FLAG_EFF;
|
||||
}
|
||||
|
||||
pr_info("CAN netdevice driver\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(cc770_init);
|
||||
|
||||
static __exit void cc770_exit(void)
|
||||
{
|
||||
pr_info("driver removed\n");
|
||||
}
|
||||
module_exit(cc770_exit);
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Core driver for the CC770 and AN82527 CAN controllers
|
||||
*
|
||||
* Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef CC770_DEV_H
|
||||
#define CC770_DEV_H
|
||||
|
||||
#include <linux/can/dev.h>
|
||||
|
||||
struct cc770_msgobj {
|
||||
u8 ctrl0;
|
||||
u8 ctrl1;
|
||||
u8 id[4];
|
||||
u8 config;
|
||||
u8 data[8];
|
||||
u8 dontuse; /* padding */
|
||||
} __packed;
|
||||
|
||||
struct cc770_regs {
|
||||
union {
|
||||
struct cc770_msgobj msgobj[16]; /* Message object 1..15 */
|
||||
struct {
|
||||
u8 control; /* Control Register */
|
||||
u8 status; /* Status Register */
|
||||
u8 cpu_interface; /* CPU Interface Register */
|
||||
u8 dontuse1;
|
||||
u8 high_speed_read[2]; /* High Speed Read */
|
||||
u8 global_mask_std[2]; /* Standard Global Mask */
|
||||
u8 global_mask_ext[4]; /* Extended Global Mask */
|
||||
u8 msg15_mask[4]; /* Message 15 Mask */
|
||||
u8 dontuse2[15];
|
||||
u8 clkout; /* Clock Out Register */
|
||||
u8 dontuse3[15];
|
||||
u8 bus_config; /* Bus Configuration Register */
|
||||
u8 dontuse4[15];
|
||||
u8 bit_timing_0; /* Bit Timing Register byte 0 */
|
||||
u8 dontuse5[15];
|
||||
u8 bit_timing_1; /* Bit Timing Register byte 1 */
|
||||
u8 dontuse6[15];
|
||||
u8 interrupt; /* Interrupt Register */
|
||||
u8 dontuse7[15];
|
||||
u8 rx_error_counter; /* Receive Error Counter */
|
||||
u8 dontuse8[15];
|
||||
u8 tx_error_counter; /* Transmit Error Counter */
|
||||
u8 dontuse9[31];
|
||||
u8 p1_conf;
|
||||
u8 dontuse10[15];
|
||||
u8 p2_conf;
|
||||
u8 dontuse11[15];
|
||||
u8 p1_in;
|
||||
u8 dontuse12[15];
|
||||
u8 p2_in;
|
||||
u8 dontuse13[15];
|
||||
u8 p1_out;
|
||||
u8 dontuse14[15];
|
||||
u8 p2_out;
|
||||
u8 dontuse15[15];
|
||||
u8 serial_reset_addr;
|
||||
};
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* Control Register (0x00) */
|
||||
#define CTRL_INI 0x01 /* Initialization */
|
||||
#define CTRL_IE 0x02 /* Interrupt Enable */
|
||||
#define CTRL_SIE 0x04 /* Status Interrupt Enable */
|
||||
#define CTRL_EIE 0x08 /* Error Interrupt Enable */
|
||||
#define CTRL_EAF 0x20 /* Enable additional functions */
|
||||
#define CTRL_CCE 0x40 /* Change Configuration Enable */
|
||||
|
||||
/* Status Register (0x01) */
|
||||
#define STAT_LEC_STUFF 0x01 /* Stuff error */
|
||||
#define STAT_LEC_FORM 0x02 /* Form error */
|
||||
#define STAT_LEC_ACK 0x03 /* Acknowledgement error */
|
||||
#define STAT_LEC_BIT1 0x04 /* Bit1 error */
|
||||
#define STAT_LEC_BIT0 0x05 /* Bit0 error */
|
||||
#define STAT_LEC_CRC 0x06 /* CRC error */
|
||||
#define STAT_LEC_MASK 0x07 /* Last Error Code mask */
|
||||
#define STAT_TXOK 0x08 /* Transmit Message Successfully */
|
||||
#define STAT_RXOK 0x10 /* Receive Message Successfully */
|
||||
#define STAT_WAKE 0x20 /* Wake Up Status */
|
||||
#define STAT_WARN 0x40 /* Warning Status */
|
||||
#define STAT_BOFF 0x80 /* Bus Off Status */
|
||||
|
||||
/*
|
||||
* CPU Interface Register (0x02)
|
||||
* Clock Out Register (0x1f)
|
||||
* Bus Configuration Register (0x2f)
|
||||
*
|
||||
* see include/linux/can/platform/cc770.h
|
||||
*/
|
||||
|
||||
/* Message Control Register 0 (Base Address + 0x0) */
|
||||
#define INTPND_RES 0x01 /* No Interrupt pending */
|
||||
#define INTPND_SET 0x02 /* Interrupt pending */
|
||||
#define INTPND_UNC 0x03
|
||||
#define RXIE_RES 0x04 /* Receive Interrupt Disable */
|
||||
#define RXIE_SET 0x08 /* Receive Interrupt Enable */
|
||||
#define RXIE_UNC 0x0c
|
||||
#define TXIE_RES 0x10 /* Transmit Interrupt Disable */
|
||||
#define TXIE_SET 0x20 /* Transmit Interrupt Enable */
|
||||
#define TXIE_UNC 0x30
|
||||
#define MSGVAL_RES 0x40 /* Message Invalid */
|
||||
#define MSGVAL_SET 0x80 /* Message Valid */
|
||||
#define MSGVAL_UNC 0xc0
|
||||
|
||||
/* Message Control Register 1 (Base Address + 0x01) */
|
||||
#define NEWDAT_RES 0x01 /* No New Data */
|
||||
#define NEWDAT_SET 0x02 /* New Data */
|
||||
#define NEWDAT_UNC 0x03
|
||||
#define MSGLST_RES 0x04 /* No Message Lost */
|
||||
#define MSGLST_SET 0x08 /* Message Lost */
|
||||
#define MSGLST_UNC 0x0c
|
||||
#define CPUUPD_RES 0x04 /* No CPU Updating */
|
||||
#define CPUUPD_SET 0x08 /* CPU Updating */
|
||||
#define CPUUPD_UNC 0x0c
|
||||
#define TXRQST_RES 0x10 /* No Transmission Request */
|
||||
#define TXRQST_SET 0x20 /* Transmission Request */
|
||||
#define TXRQST_UNC 0x30
|
||||
#define RMTPND_RES 0x40 /* No Remote Request Pending */
|
||||
#define RMTPND_SET 0x80 /* Remote Request Pending */
|
||||
#define RMTPND_UNC 0xc0
|
||||
|
||||
/* Message Configuration Register (Base Address + 0x06) */
|
||||
#define MSGCFG_XTD 0x04 /* Extended Identifier */
|
||||
#define MSGCFG_DIR 0x08 /* Direction is Transmit */
|
||||
|
||||
#define MSGOBJ_FIRST 1
|
||||
#define MSGOBJ_LAST 15
|
||||
|
||||
#define CC770_IO_SIZE 0x100
|
||||
#define CC770_MAX_IRQ 20 /* max. number of interrupts handled in ISR */
|
||||
#define CC770_MAX_MSG 4 /* max. number of messages handled in ISR */
|
||||
|
||||
#define CC770_ECHO_SKB_MAX 1
|
||||
|
||||
#define cc770_read_reg(priv, member) \
|
||||
priv->read_reg(priv, offsetof(struct cc770_regs, member))
|
||||
|
||||
#define cc770_write_reg(priv, member, value) \
|
||||
priv->write_reg(priv, offsetof(struct cc770_regs, member), value)
|
||||
|
||||
/*
|
||||
* Message objects and flags used by this driver
|
||||
*/
|
||||
#define CC770_OBJ_FLAG_RX 0x01
|
||||
#define CC770_OBJ_FLAG_RTR 0x02
|
||||
#define CC770_OBJ_FLAG_EFF 0x04
|
||||
|
||||
enum {
|
||||
CC770_OBJ_RX0 = 0, /* for receiving normal messages */
|
||||
CC770_OBJ_RX1, /* for receiving normal messages */
|
||||
CC770_OBJ_RX_RTR0, /* for receiving remote transmission requests */
|
||||
CC770_OBJ_RX_RTR1, /* for receiving remote transmission requests */
|
||||
CC770_OBJ_TX, /* for sending messages */
|
||||
CC770_OBJ_MAX
|
||||
};
|
||||
|
||||
#define obj2msgobj(o) (MSGOBJ_LAST - (o)) /* message object 11..15 */
|
||||
|
||||
/*
|
||||
* CC770 private data structure
|
||||
*/
|
||||
struct cc770_priv {
|
||||
struct can_priv can; /* must be the first member */
|
||||
struct sk_buff *echo_skb;
|
||||
|
||||
/* the lower-layer is responsible for appropriate locking */
|
||||
u8 (*read_reg)(const struct cc770_priv *priv, int reg);
|
||||
void (*write_reg)(const struct cc770_priv *priv, int reg, u8 val);
|
||||
void (*pre_irq)(const struct cc770_priv *priv);
|
||||
void (*post_irq)(const struct cc770_priv *priv);
|
||||
|
||||
void *priv; /* for board-specific data */
|
||||
struct net_device *dev;
|
||||
|
||||
void __iomem *reg_base; /* ioremap'ed address to registers */
|
||||
unsigned long irq_flags; /* for request_irq() */
|
||||
|
||||
unsigned char obj_flags[CC770_OBJ_MAX];
|
||||
u8 control_normal_mode; /* Control register for normal mode */
|
||||
u8 cpu_interface; /* CPU interface register */
|
||||
u8 clkout; /* Clock out register */
|
||||
u8 bus_config; /* Bus conffiguration register */
|
||||
};
|
||||
|
||||
struct net_device *alloc_cc770dev(int sizeof_priv);
|
||||
void free_cc770dev(struct net_device *dev);
|
||||
int register_cc770dev(struct net_device *dev);
|
||||
void unregister_cc770dev(struct net_device *dev);
|
||||
|
||||
#endif /* CC770_DEV_H */
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* Driver for CC770 and AN82527 CAN controllers on the legacy ISA bus
|
||||
*
|
||||
* Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Bosch CC770 and Intel AN82527 CAN controllers on the ISA or PC-104 bus.
|
||||
* The I/O port or memory address and the IRQ number must be specified via
|
||||
* module parameters:
|
||||
*
|
||||
* insmod cc770_isa.ko port=0x310,0x380 irq=7,11
|
||||
*
|
||||
* for ISA devices using I/O ports or:
|
||||
*
|
||||
* insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11
|
||||
*
|
||||
* for memory mapped ISA devices.
|
||||
*
|
||||
* Indirect access via address and data port is supported as well:
|
||||
*
|
||||
* insmod cc770_isa.ko port=0x310,0x380 indirect=1 irq=7,11
|
||||
*
|
||||
* Furthermore, the following mode parameter can be defined:
|
||||
*
|
||||
* clk: External oscillator clock frequency (default=16000000 [16 MHz])
|
||||
* cir: CPU interface register (default=0x40 [DSC])
|
||||
* bcr: Bus configuration register (default=0x40 [CBY])
|
||||
* cor: Clockout register (default=0x00)
|
||||
*
|
||||
* Note: for clk, cir, bcr and cor, the first argument re-defines the
|
||||
* default for all other devices, e.g.:
|
||||
*
|
||||
* insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000
|
||||
*
|
||||
* is equivalent to
|
||||
*
|
||||
* insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000,24000000
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/platform/cc770.h>
|
||||
|
||||
#include "cc770.h"
|
||||
|
||||
#define MAXDEV 8
|
||||
|
||||
MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
|
||||
MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
#define CLK_DEFAULT 16000000 /* 16 MHz */
|
||||
#define COR_DEFAULT 0x00
|
||||
#define BCR_DEFAULT BUSCFG_CBY
|
||||
|
||||
static unsigned long port[MAXDEV];
|
||||
static unsigned long mem[MAXDEV];
|
||||
static int __devinitdata irq[MAXDEV];
|
||||
static int __devinitdata clk[MAXDEV];
|
||||
static u8 __devinitdata cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
||||
static u8 __devinitdata cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
||||
static u8 __devinitdata bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
||||
static int __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
|
||||
|
||||
module_param_array(port, ulong, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(port, "I/O port number");
|
||||
|
||||
module_param_array(mem, ulong, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(mem, "I/O memory address");
|
||||
|
||||
module_param_array(indirect, int, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
|
||||
|
||||
module_param_array(irq, int, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(irq, "IRQ number");
|
||||
|
||||
module_param_array(clk, int, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(clk, "External oscillator clock frequency "
|
||||
"(default=16000000 [16 MHz])");
|
||||
|
||||
module_param_array(cir, byte, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])");
|
||||
|
||||
module_param_array(cor, byte, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
|
||||
|
||||
module_param_array(bcr, byte, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])");
|
||||
|
||||
#define CC770_IOSIZE 0x20
|
||||
#define CC770_IOSIZE_INDIRECT 0x02
|
||||
|
||||
static struct platform_device *cc770_isa_devs[MAXDEV];
|
||||
|
||||
static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg)
|
||||
{
|
||||
return readb(priv->reg_base + reg);
|
||||
}
|
||||
|
||||
static void cc770_isa_mem_write_reg(const struct cc770_priv *priv,
|
||||
int reg, u8 val)
|
||||
{
|
||||
writeb(val, priv->reg_base + reg);
|
||||
}
|
||||
|
||||
static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg)
|
||||
{
|
||||
return inb((unsigned long)priv->reg_base + reg);
|
||||
}
|
||||
|
||||
static void cc770_isa_port_write_reg(const struct cc770_priv *priv,
|
||||
int reg, u8 val)
|
||||
{
|
||||
outb(val, (unsigned long)priv->reg_base + reg);
|
||||
}
|
||||
|
||||
static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv,
|
||||
int reg)
|
||||
{
|
||||
unsigned long base = (unsigned long)priv->reg_base;
|
||||
|
||||
outb(reg, base);
|
||||
return inb(base + 1);
|
||||
}
|
||||
|
||||
static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv,
|
||||
int reg, u8 val)
|
||||
{
|
||||
unsigned long base = (unsigned long)priv->reg_base;
|
||||
|
||||
outb(reg, base);
|
||||
outb(val, base + 1);
|
||||
}
|
||||
|
||||
static int __devinit cc770_isa_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct cc770_priv *priv;
|
||||
void __iomem *base = NULL;
|
||||
int iosize = CC770_IOSIZE;
|
||||
int idx = pdev->id;
|
||||
int err;
|
||||
u32 clktmp;
|
||||
|
||||
dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",
|
||||
idx, port[idx], mem[idx], irq[idx]);
|
||||
if (mem[idx]) {
|
||||
if (!request_mem_region(mem[idx], iosize, KBUILD_MODNAME)) {
|
||||
err = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
base = ioremap_nocache(mem[idx], iosize);
|
||||
if (!base) {
|
||||
err = -ENOMEM;
|
||||
goto exit_release;
|
||||
}
|
||||
} else {
|
||||
if (indirect[idx] > 0 ||
|
||||
(indirect[idx] == -1 && indirect[0] > 0))
|
||||
iosize = CC770_IOSIZE_INDIRECT;
|
||||
if (!request_region(port[idx], iosize, KBUILD_MODNAME)) {
|
||||
err = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
dev = alloc_cc770dev(0);
|
||||
if (!dev) {
|
||||
err = -ENOMEM;
|
||||
goto exit_unmap;
|
||||
}
|
||||
priv = netdev_priv(dev);
|
||||
|
||||
dev->irq = irq[idx];
|
||||
priv->irq_flags = IRQF_SHARED;
|
||||
if (mem[idx]) {
|
||||
priv->reg_base = base;
|
||||
dev->base_addr = mem[idx];
|
||||
priv->read_reg = cc770_isa_mem_read_reg;
|
||||
priv->write_reg = cc770_isa_mem_write_reg;
|
||||
} else {
|
||||
priv->reg_base = (void __iomem *)port[idx];
|
||||
dev->base_addr = port[idx];
|
||||
|
||||
if (iosize == CC770_IOSIZE_INDIRECT) {
|
||||
priv->read_reg = cc770_isa_port_read_reg_indirect;
|
||||
priv->write_reg = cc770_isa_port_write_reg_indirect;
|
||||
} else {
|
||||
priv->read_reg = cc770_isa_port_read_reg;
|
||||
priv->write_reg = cc770_isa_port_write_reg;
|
||||
}
|
||||
}
|
||||
|
||||
if (clk[idx])
|
||||
clktmp = clk[idx];
|
||||
else if (clk[0])
|
||||
clktmp = clk[0];
|
||||
else
|
||||
clktmp = CLK_DEFAULT;
|
||||
priv->can.clock.freq = clktmp;
|
||||
|
||||
if (cir[idx] != 0xff) {
|
||||
priv->cpu_interface = cir[idx];
|
||||
} else if (cir[0] != 0xff) {
|
||||
priv->cpu_interface = cir[0];
|
||||
} else {
|
||||
/* The system clock may not exceed 10 MHz */
|
||||
if (clktmp > 10000000) {
|
||||
priv->cpu_interface |= CPUIF_DSC;
|
||||
clktmp /= 2;
|
||||
}
|
||||
/* The memory clock may not exceed 8 MHz */
|
||||
if (clktmp > 8000000)
|
||||
priv->cpu_interface |= CPUIF_DMC;
|
||||
}
|
||||
|
||||
if (priv->cpu_interface & CPUIF_DSC)
|
||||
priv->can.clock.freq /= 2;
|
||||
|
||||
if (bcr[idx] != 0xff)
|
||||
priv->bus_config = bcr[idx];
|
||||
else if (bcr[0] != 0xff)
|
||||
priv->bus_config = bcr[0];
|
||||
else
|
||||
priv->bus_config = BCR_DEFAULT;
|
||||
|
||||
if (cor[idx] != 0xff)
|
||||
priv->clkout = cor[idx];
|
||||
else if (cor[0] != 0xff)
|
||||
priv->clkout = cor[0];
|
||||
else
|
||||
priv->clkout = COR_DEFAULT;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, dev);
|
||||
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||
|
||||
err = register_cc770dev(dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"couldn't register device (err=%d)\n", err);
|
||||
goto exit_unmap;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "device registered (reg_base=0x%p, irq=%d)\n",
|
||||
priv->reg_base, dev->irq);
|
||||
return 0;
|
||||
|
||||
exit_unmap:
|
||||
if (mem[idx])
|
||||
iounmap(base);
|
||||
exit_release:
|
||||
if (mem[idx])
|
||||
release_mem_region(mem[idx], iosize);
|
||||
else
|
||||
release_region(port[idx], iosize);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit cc770_isa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = dev_get_drvdata(&pdev->dev);
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
int idx = pdev->id;
|
||||
|
||||
unregister_cc770dev(dev);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
if (mem[idx]) {
|
||||
iounmap(priv->reg_base);
|
||||
release_mem_region(mem[idx], CC770_IOSIZE);
|
||||
} else {
|
||||
if (priv->read_reg == cc770_isa_port_read_reg_indirect)
|
||||
release_region(port[idx], CC770_IOSIZE_INDIRECT);
|
||||
else
|
||||
release_region(port[idx], CC770_IOSIZE);
|
||||
}
|
||||
free_cc770dev(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver cc770_isa_driver = {
|
||||
.probe = cc770_isa_probe,
|
||||
.remove = __devexit_p(cc770_isa_remove),
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init cc770_isa_init(void)
|
||||
{
|
||||
int idx, err;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {
|
||||
if ((port[idx] || mem[idx]) && irq[idx]) {
|
||||
cc770_isa_devs[idx] =
|
||||
platform_device_alloc(KBUILD_MODNAME, idx);
|
||||
if (!cc770_isa_devs[idx]) {
|
||||
err = -ENOMEM;
|
||||
goto exit_free_devices;
|
||||
}
|
||||
err = platform_device_add(cc770_isa_devs[idx]);
|
||||
if (err) {
|
||||
platform_device_put(cc770_isa_devs[idx]);
|
||||
goto exit_free_devices;
|
||||
}
|
||||
pr_debug("platform device %d: port=%#lx, mem=%#lx, "
|
||||
"irq=%d\n",
|
||||
idx, port[idx], mem[idx], irq[idx]);
|
||||
} else if (idx == 0 || port[idx] || mem[idx]) {
|
||||
pr_err("insufficient parameters supplied\n");
|
||||
err = -EINVAL;
|
||||
goto exit_free_devices;
|
||||
}
|
||||
}
|
||||
|
||||
err = platform_driver_register(&cc770_isa_driver);
|
||||
if (err)
|
||||
goto exit_free_devices;
|
||||
|
||||
pr_info("driver for max. %d devices registered\n", MAXDEV);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_free_devices:
|
||||
while (--idx >= 0) {
|
||||
if (cc770_isa_devs[idx])
|
||||
platform_device_unregister(cc770_isa_devs[idx]);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
module_init(cc770_isa_init);
|
||||
|
||||
static void __exit cc770_isa_exit(void)
|
||||
{
|
||||
int idx;
|
||||
|
||||
platform_driver_unregister(&cc770_isa_driver);
|
||||
for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {
|
||||
if (cc770_isa_devs[idx])
|
||||
platform_device_unregister(cc770_isa_devs[idx]);
|
||||
}
|
||||
}
|
||||
module_exit(cc770_isa_exit);
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Driver for CC770 and AN82527 CAN controllers on the platform bus
|
||||
*
|
||||
* Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If platform data are used you should have similar definitions
|
||||
* in your board-specific code:
|
||||
*
|
||||
* static struct cc770_platform_data myboard_cc770_pdata = {
|
||||
* .osc_freq = 16000000,
|
||||
* .cir = 0x41,
|
||||
* .cor = 0x20,
|
||||
* .bcr = 0x40,
|
||||
* };
|
||||
*
|
||||
* Please see include/linux/can/platform/cc770.h for description of
|
||||
* above fields.
|
||||
*
|
||||
* If the device tree is used, you need a CAN node definition in your
|
||||
* DTS file similar to:
|
||||
*
|
||||
* can@3,100 {
|
||||
* compatible = "bosch,cc770";
|
||||
* reg = <3 0x100 0x80>;
|
||||
* interrupts = <2 0>;
|
||||
* interrupt-parent = <&mpic>;
|
||||
* bosch,external-clock-frequency = <16000000>;
|
||||
* };
|
||||
*
|
||||
* See "Documentation/devicetree/bindings/net/can/cc770.txt" for further
|
||||
* information.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/platform/cc770.h>
|
||||
|
||||
#include "cc770.h"
|
||||
|
||||
#define DRV_NAME "cc770_platform"
|
||||
|
||||
MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
|
||||
MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
#define CC770_PLATFORM_CAN_CLOCK 16000000
|
||||
|
||||
static u8 cc770_platform_read_reg(const struct cc770_priv *priv, int reg)
|
||||
{
|
||||
return ioread8(priv->reg_base + reg);
|
||||
}
|
||||
|
||||
static void cc770_platform_write_reg(const struct cc770_priv *priv, int reg,
|
||||
u8 val)
|
||||
{
|
||||
iowrite8(val, priv->reg_base + reg);
|
||||
}
|
||||
|
||||
static int __devinit cc770_get_of_node_data(struct platform_device *pdev,
|
||||
struct cc770_priv *priv)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const u32 *prop;
|
||||
int prop_size;
|
||||
u32 clkext;
|
||||
|
||||
prop = of_get_property(np, "bosch,external-clock-frequency",
|
||||
&prop_size);
|
||||
if (prop && (prop_size == sizeof(u32)))
|
||||
clkext = *prop;
|
||||
else
|
||||
clkext = CC770_PLATFORM_CAN_CLOCK; /* default */
|
||||
priv->can.clock.freq = clkext;
|
||||
|
||||
/* The system clock may not exceed 10 MHz */
|
||||
if (priv->can.clock.freq > 10000000) {
|
||||
priv->cpu_interface |= CPUIF_DSC;
|
||||
priv->can.clock.freq /= 2;
|
||||
}
|
||||
|
||||
/* The memory clock may not exceed 8 MHz */
|
||||
if (priv->can.clock.freq > 8000000)
|
||||
priv->cpu_interface |= CPUIF_DMC;
|
||||
|
||||
if (of_get_property(np, "bosch,divide-memory-clock", NULL))
|
||||
priv->cpu_interface |= CPUIF_DMC;
|
||||
if (of_get_property(np, "bosch,iso-low-speed-mux", NULL))
|
||||
priv->cpu_interface |= CPUIF_MUX;
|
||||
|
||||
if (!of_get_property(np, "bosch,no-comperator-bypass", NULL))
|
||||
priv->bus_config |= BUSCFG_CBY;
|
||||
if (of_get_property(np, "bosch,disconnect-rx0-input", NULL))
|
||||
priv->bus_config |= BUSCFG_DR0;
|
||||
if (of_get_property(np, "bosch,disconnect-rx1-input", NULL))
|
||||
priv->bus_config |= BUSCFG_DR1;
|
||||
if (of_get_property(np, "bosch,disconnect-tx1-output", NULL))
|
||||
priv->bus_config |= BUSCFG_DT1;
|
||||
if (of_get_property(np, "bosch,polarity-dominant", NULL))
|
||||
priv->bus_config |= BUSCFG_POL;
|
||||
|
||||
prop = of_get_property(np, "bosch,clock-out-frequency", &prop_size);
|
||||
if (prop && (prop_size == sizeof(u32)) && *prop > 0) {
|
||||
u32 cdv = clkext / *prop;
|
||||
int slew;
|
||||
|
||||
if (cdv > 0 && cdv < 16) {
|
||||
priv->cpu_interface |= CPUIF_CEN;
|
||||
priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK;
|
||||
|
||||
prop = of_get_property(np, "bosch,slew-rate",
|
||||
&prop_size);
|
||||
if (prop && (prop_size == sizeof(u32))) {
|
||||
slew = *prop;
|
||||
} else {
|
||||
/* Determine default slew rate */
|
||||
slew = (CLKOUT_SL_MASK >>
|
||||
CLKOUT_SL_SHIFT) -
|
||||
((cdv * clkext - 1) / 8000000);
|
||||
if (slew < 0)
|
||||
slew = 0;
|
||||
}
|
||||
priv->clkout |= (slew << CLKOUT_SL_SHIFT) &
|
||||
CLKOUT_SL_MASK;
|
||||
} else {
|
||||
dev_dbg(&pdev->dev, "invalid clock-out-frequency\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit cc770_get_platform_data(struct platform_device *pdev,
|
||||
struct cc770_priv *priv)
|
||||
{
|
||||
|
||||
struct cc770_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
priv->can.clock.freq = pdata->osc_freq;
|
||||
if (priv->cpu_interface | CPUIF_DSC)
|
||||
priv->can.clock.freq /= 2;
|
||||
priv->clkout = pdata->cor;
|
||||
priv->bus_config = pdata->bcr;
|
||||
priv->cpu_interface = pdata->cir;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit cc770_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct cc770_priv *priv;
|
||||
struct resource *mem;
|
||||
resource_size_t mem_size;
|
||||
void __iomem *base;
|
||||
int err, irq;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!mem || irq <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
mem_size = resource_size(mem);
|
||||
if (!request_mem_region(mem->start, mem_size, pdev->name))
|
||||
return -EBUSY;
|
||||
|
||||
base = ioremap(mem->start, mem_size);
|
||||
if (!base) {
|
||||
err = -ENOMEM;
|
||||
goto exit_release_mem;
|
||||
}
|
||||
|
||||
dev = alloc_cc770dev(0);
|
||||
if (!dev) {
|
||||
err = -ENOMEM;
|
||||
goto exit_unmap_mem;
|
||||
}
|
||||
|
||||
dev->irq = irq;
|
||||
priv = netdev_priv(dev);
|
||||
priv->read_reg = cc770_platform_read_reg;
|
||||
priv->write_reg = cc770_platform_write_reg;
|
||||
priv->irq_flags = IRQF_SHARED;
|
||||
priv->reg_base = base;
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
err = cc770_get_of_node_data(pdev, priv);
|
||||
else if (pdev->dev.platform_data)
|
||||
err = cc770_get_platform_data(pdev, priv);
|
||||
else
|
||||
err = -ENODEV;
|
||||
if (err)
|
||||
goto exit_free_cc770;
|
||||
|
||||
dev_dbg(&pdev->dev,
|
||||
"reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x "
|
||||
"bus_config=0x%02x clkout=0x%02x\n",
|
||||
priv->reg_base, dev->irq, priv->can.clock.freq,
|
||||
priv->cpu_interface, priv->bus_config, priv->clkout);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, dev);
|
||||
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||
|
||||
err = register_cc770dev(dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"couldn't register CC700 device (err=%d)\n", err);
|
||||
goto exit_free_cc770;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_free_cc770:
|
||||
free_cc770dev(dev);
|
||||
exit_unmap_mem:
|
||||
iounmap(base);
|
||||
exit_release_mem:
|
||||
release_mem_region(mem->start, mem_size);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit cc770_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = dev_get_drvdata(&pdev->dev);
|
||||
struct cc770_priv *priv = netdev_priv(dev);
|
||||
struct resource *mem;
|
||||
|
||||
unregister_cc770dev(dev);
|
||||
iounmap(priv->reg_base);
|
||||
free_cc770dev(dev);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id __devinitdata cc770_platform_table[] = {
|
||||
{.compatible = "bosch,cc770"}, /* CC770 from Bosch */
|
||||
{.compatible = "intc,82527"}, /* AN82527 from Intel CP */
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver cc770_platform_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = cc770_platform_table,
|
||||
},
|
||||
.probe = cc770_platform_probe,
|
||||
.remove = __devexit_p(cc770_platform_remove),
|
||||
};
|
||||
|
||||
module_platform_driver(cc770_platform_driver);
|
|
@ -454,7 +454,7 @@ static void can_setup(struct net_device *dev)
|
|||
|
||||
/* New-style flags. */
|
||||
dev->flags = IFF_NOARP;
|
||||
dev->features = NETIF_F_NO_CSUM;
|
||||
dev->features = NETIF_F_HW_CSUM;
|
||||
}
|
||||
|
||||
struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
|
||||
|
|
|
@ -1060,20 +1060,7 @@ static struct platform_driver flexcan_driver = {
|
|||
.remove = __devexit_p(flexcan_remove),
|
||||
};
|
||||
|
||||
static int __init flexcan_init(void)
|
||||
{
|
||||
pr_info("%s netdevice driver\n", DRV_NAME);
|
||||
return platform_driver_register(&flexcan_driver);
|
||||
}
|
||||
|
||||
static void __exit flexcan_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&flexcan_driver);
|
||||
pr_info("%s: driver removed\n", DRV_NAME);
|
||||
}
|
||||
|
||||
module_init(flexcan_init);
|
||||
module_exit(flexcan_exit);
|
||||
module_platform_driver(flexcan_driver);
|
||||
|
||||
MODULE_AUTHOR("Sascha Hauer <kernel@pengutronix.de>, "
|
||||
"Marc Kleine-Budde <kernel@pengutronix.de>");
|
||||
|
|
|
@ -1803,20 +1803,9 @@ static struct platform_driver ican3_driver = {
|
|||
.remove = __devexit_p(ican3_remove),
|
||||
};
|
||||
|
||||
static int __init ican3_init(void)
|
||||
{
|
||||
return platform_driver_register(&ican3_driver);
|
||||
}
|
||||
|
||||
static void __exit ican3_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ican3_driver);
|
||||
}
|
||||
module_platform_driver(ican3_driver);
|
||||
|
||||
MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
|
||||
MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:janz-ican3");
|
||||
|
||||
module_init(ican3_init);
|
||||
module_exit(ican3_exit);
|
||||
|
|
|
@ -411,17 +411,7 @@ static struct platform_driver mpc5xxx_can_driver = {
|
|||
#endif
|
||||
};
|
||||
|
||||
static int __init mpc5xxx_can_init(void)
|
||||
{
|
||||
return platform_driver_register(&mpc5xxx_can_driver);
|
||||
}
|
||||
module_init(mpc5xxx_can_init);
|
||||
|
||||
static void __exit mpc5xxx_can_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mpc5xxx_can_driver);
|
||||
};
|
||||
module_exit(mpc5xxx_can_exit);
|
||||
module_platform_driver(mpc5xxx_can_driver);
|
||||
|
||||
MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
|
||||
MODULE_DESCRIPTION("Freescale MPC5xxx CAN driver");
|
||||
|
|
|
@ -581,7 +581,10 @@ static int mscan_open(struct net_device *dev)
|
|||
|
||||
priv->open_time = jiffies;
|
||||
|
||||
clrbits8(®s->canctl1, MSCAN_LISTEN);
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
||||
setbits8(®s->canctl1, MSCAN_LISTEN);
|
||||
else
|
||||
clrbits8(®s->canctl1, MSCAN_LISTEN);
|
||||
|
||||
ret = mscan_start(dev);
|
||||
if (ret)
|
||||
|
@ -690,7 +693,8 @@ struct net_device *alloc_mscandev(void)
|
|||
priv->can.bittiming_const = &mscan_bittiming_const;
|
||||
priv->can.do_set_bittiming = mscan_do_set_bittiming;
|
||||
priv->can.do_set_mode = mscan_do_set_mode;
|
||||
priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
|
||||
priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
|
||||
CAN_CTRLMODE_LISTENONLY;
|
||||
|
||||
for (i = 0; i < TX_QUEUE_SIZE; i++) {
|
||||
priv->tx_queue[i].id = i;
|
||||
|
|
|
@ -6,7 +6,6 @@ if CAN_SJA1000
|
|||
|
||||
config CAN_SJA1000_ISA
|
||||
tristate "ISA Bus based legacy SJA1000 driver"
|
||||
depends on ISA
|
||||
---help---
|
||||
This driver adds legacy support for SJA1000 chips connected to
|
||||
the ISA bus using I/O port, memory mapped or indirect access.
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -44,9 +44,9 @@ static unsigned long port[MAXDEV];
|
|||
static unsigned long mem[MAXDEV];
|
||||
static int __devinitdata irq[MAXDEV];
|
||||
static int __devinitdata clk[MAXDEV];
|
||||
static char __devinitdata cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
|
||||
static char __devinitdata ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
|
||||
static char __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
|
||||
static unsigned char __devinitdata cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
||||
static unsigned char __devinitdata ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
||||
static int __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
|
||||
|
||||
module_param_array(port, ulong, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(port, "I/O port number");
|
||||
|
@ -54,7 +54,7 @@ MODULE_PARM_DESC(port, "I/O port number");
|
|||
module_param_array(mem, ulong, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(mem, "I/O memory address");
|
||||
|
||||
module_param_array(indirect, byte, NULL, S_IRUGO);
|
||||
module_param_array(indirect, int, NULL, S_IRUGO);
|
||||
MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
|
||||
|
||||
module_param_array(irq, int, NULL, S_IRUGO);
|
||||
|
@ -75,6 +75,8 @@ MODULE_PARM_DESC(ocr, "Output control register "
|
|||
#define SJA1000_IOSIZE 0x20
|
||||
#define SJA1000_IOSIZE_INDIRECT 0x02
|
||||
|
||||
static struct platform_device *sja1000_isa_devs[MAXDEV];
|
||||
|
||||
static u8 sja1000_isa_mem_read_reg(const struct sja1000_priv *priv, int reg)
|
||||
{
|
||||
return readb(priv->reg_base + reg);
|
||||
|
@ -115,26 +117,18 @@ static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv,
|
|||
outb(val, base + 1);
|
||||
}
|
||||
|
||||
static int __devinit sja1000_isa_match(struct device *pdev, unsigned int idx)
|
||||
{
|
||||
if (port[idx] || mem[idx]) {
|
||||
if (irq[idx])
|
||||
return 1;
|
||||
} else if (idx)
|
||||
return 0;
|
||||
|
||||
dev_err(pdev, "insufficient parameters supplied\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit sja1000_isa_probe(struct device *pdev, unsigned int idx)
|
||||
static int __devinit sja1000_isa_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct sja1000_priv *priv;
|
||||
void __iomem *base = NULL;
|
||||
int iosize = SJA1000_IOSIZE;
|
||||
int idx = pdev->id;
|
||||
int err;
|
||||
|
||||
dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",
|
||||
idx, port[idx], mem[idx], irq[idx]);
|
||||
|
||||
if (mem[idx]) {
|
||||
if (!request_mem_region(mem[idx], iosize, DRV_NAME)) {
|
||||
err = -EBUSY;
|
||||
|
@ -189,31 +183,31 @@ static int __devinit sja1000_isa_probe(struct device *pdev, unsigned int idx)
|
|||
else
|
||||
priv->can.clock.freq = CLK_DEFAULT / 2;
|
||||
|
||||
if (ocr[idx] != -1)
|
||||
priv->ocr = ocr[idx] & 0xff;
|
||||
else if (ocr[0] != -1)
|
||||
priv->ocr = ocr[0] & 0xff;
|
||||
if (ocr[idx] != 0xff)
|
||||
priv->ocr = ocr[idx];
|
||||
else if (ocr[0] != 0xff)
|
||||
priv->ocr = ocr[0];
|
||||
else
|
||||
priv->ocr = OCR_DEFAULT;
|
||||
|
||||
if (cdr[idx] != -1)
|
||||
priv->cdr = cdr[idx] & 0xff;
|
||||
else if (cdr[0] != -1)
|
||||
priv->cdr = cdr[0] & 0xff;
|
||||
if (cdr[idx] != 0xff)
|
||||
priv->cdr = cdr[idx];
|
||||
else if (cdr[0] != 0xff)
|
||||
priv->cdr = cdr[0];
|
||||
else
|
||||
priv->cdr = CDR_DEFAULT;
|
||||
|
||||
dev_set_drvdata(pdev, dev);
|
||||
SET_NETDEV_DEV(dev, pdev);
|
||||
dev_set_drvdata(&pdev->dev, dev);
|
||||
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||
|
||||
err = register_sja1000dev(dev);
|
||||
if (err) {
|
||||
dev_err(pdev, "registering %s failed (err=%d)\n",
|
||||
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
|
||||
DRV_NAME, err);
|
||||
goto exit_unmap;
|
||||
}
|
||||
|
||||
dev_info(pdev, "%s device registered (reg_base=0x%p, irq=%d)\n",
|
||||
dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n",
|
||||
DRV_NAME, priv->reg_base, dev->irq);
|
||||
return 0;
|
||||
|
||||
|
@ -229,13 +223,14 @@ static int __devinit sja1000_isa_probe(struct device *pdev, unsigned int idx)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int __devexit sja1000_isa_remove(struct device *pdev, unsigned int idx)
|
||||
static int __devexit sja1000_isa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = dev_get_drvdata(pdev);
|
||||
struct net_device *dev = dev_get_drvdata(&pdev->dev);
|
||||
struct sja1000_priv *priv = netdev_priv(dev);
|
||||
int idx = pdev->id;
|
||||
|
||||
unregister_sja1000dev(dev);
|
||||
dev_set_drvdata(pdev, NULL);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
if (mem[idx]) {
|
||||
iounmap(priv->reg_base);
|
||||
|
@ -251,29 +246,70 @@ static int __devexit sja1000_isa_remove(struct device *pdev, unsigned int idx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct isa_driver sja1000_isa_driver = {
|
||||
.match = sja1000_isa_match,
|
||||
static struct platform_driver sja1000_isa_driver = {
|
||||
.probe = sja1000_isa_probe,
|
||||
.remove = __devexit_p(sja1000_isa_remove),
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init sja1000_isa_init(void)
|
||||
{
|
||||
int err = isa_register_driver(&sja1000_isa_driver, MAXDEV);
|
||||
int idx, err;
|
||||
|
||||
for (idx = 0; idx < MAXDEV; idx++) {
|
||||
if ((port[idx] || mem[idx]) && irq[idx]) {
|
||||
sja1000_isa_devs[idx] =
|
||||
platform_device_alloc(DRV_NAME, idx);
|
||||
if (!sja1000_isa_devs[idx]) {
|
||||
err = -ENOMEM;
|
||||
goto exit_free_devices;
|
||||
}
|
||||
err = platform_device_add(sja1000_isa_devs[idx]);
|
||||
if (err) {
|
||||
platform_device_put(sja1000_isa_devs[idx]);
|
||||
goto exit_free_devices;
|
||||
}
|
||||
pr_debug("%s: platform device %d: port=%#lx, mem=%#lx, "
|
||||
"irq=%d\n",
|
||||
DRV_NAME, idx, port[idx], mem[idx], irq[idx]);
|
||||
} else if (idx == 0 || port[idx] || mem[idx]) {
|
||||
pr_err("%s: insufficient parameters supplied\n",
|
||||
DRV_NAME);
|
||||
err = -EINVAL;
|
||||
goto exit_free_devices;
|
||||
}
|
||||
}
|
||||
|
||||
err = platform_driver_register(&sja1000_isa_driver);
|
||||
if (err)
|
||||
goto exit_free_devices;
|
||||
|
||||
pr_info("Legacy %s driver for max. %d devices registered\n",
|
||||
DRV_NAME, MAXDEV);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_free_devices:
|
||||
while (--idx >= 0) {
|
||||
if (sja1000_isa_devs[idx])
|
||||
platform_device_unregister(sja1000_isa_devs[idx]);
|
||||
}
|
||||
|
||||
if (!err)
|
||||
printk(KERN_INFO
|
||||
"Legacy %s driver for max. %d devices registered\n",
|
||||
DRV_NAME, MAXDEV);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit sja1000_isa_exit(void)
|
||||
{
|
||||
isa_unregister_driver(&sja1000_isa_driver);
|
||||
int idx;
|
||||
|
||||
platform_driver_unregister(&sja1000_isa_driver);
|
||||
for (idx = 0; idx < MAXDEV; idx++) {
|
||||
if (sja1000_isa_devs[idx])
|
||||
platform_device_unregister(sja1000_isa_devs[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(sja1000_isa_init);
|
||||
|
|
|
@ -220,14 +220,4 @@ static struct platform_driver sja1000_ofp_driver = {
|
|||
.remove = __devexit_p(sja1000_ofp_remove),
|
||||
};
|
||||
|
||||
static int __init sja1000_ofp_init(void)
|
||||
{
|
||||
return platform_driver_register(&sja1000_ofp_driver);
|
||||
}
|
||||
module_init(sja1000_ofp_init);
|
||||
|
||||
static void __exit sja1000_ofp_exit(void)
|
||||
{
|
||||
return platform_driver_unregister(&sja1000_ofp_driver);
|
||||
};
|
||||
module_exit(sja1000_ofp_exit);
|
||||
module_platform_driver(sja1000_ofp_driver);
|
||||
|
|
|
@ -185,15 +185,4 @@ static struct platform_driver sp_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init sp_init(void)
|
||||
{
|
||||
return platform_driver_register(&sp_driver);
|
||||
}
|
||||
|
||||
static void __exit sp_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sp_driver);
|
||||
}
|
||||
|
||||
module_init(sp_init);
|
||||
module_exit(sp_exit);
|
||||
module_platform_driver(sp_driver);
|
||||
|
|
|
@ -387,7 +387,7 @@ static void slc_setup(struct net_device *dev)
|
|||
|
||||
/* New-style flags. */
|
||||
dev->flags = IFF_NOARP;
|
||||
dev->features = NETIF_F_NO_CSUM;
|
||||
dev->features = NETIF_F_HW_CSUM;
|
||||
}
|
||||
|
||||
/******************************************
|
||||
|
|
|
@ -874,21 +874,9 @@ static struct platform_driver softing_driver = {
|
|||
.remove = __devexit_p(softing_pdev_remove),
|
||||
};
|
||||
|
||||
module_platform_driver(softing_driver);
|
||||
|
||||
MODULE_ALIAS("platform:softing");
|
||||
|
||||
static int __init softing_start(void)
|
||||
{
|
||||
return platform_driver_register(&softing_driver);
|
||||
}
|
||||
|
||||
static void __exit softing_stop(void)
|
||||
{
|
||||
platform_driver_unregister(&softing_driver);
|
||||
}
|
||||
|
||||
module_init(softing_start);
|
||||
module_exit(softing_stop);
|
||||
|
||||
MODULE_DESCRIPTION("Softing DPRAM CAN driver");
|
||||
MODULE_AUTHOR("Kurt Van Dijck <kurt.van.dijck@eia.be>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -1037,20 +1037,7 @@ static struct platform_driver ti_hecc_driver = {
|
|||
.resume = ti_hecc_resume,
|
||||
};
|
||||
|
||||
static int __init ti_hecc_init_driver(void)
|
||||
{
|
||||
printk(KERN_INFO DRV_DESC "\n");
|
||||
return platform_driver_register(&ti_hecc_driver);
|
||||
}
|
||||
|
||||
static void __exit ti_hecc_exit_driver(void)
|
||||
{
|
||||
printk(KERN_INFO DRV_DESC " unloaded\n");
|
||||
platform_driver_unregister(&ti_hecc_driver);
|
||||
}
|
||||
|
||||
module_exit(ti_hecc_exit_driver);
|
||||
module_init(ti_hecc_init_driver);
|
||||
module_platform_driver(ti_hecc_driver);
|
||||
|
||||
MODULE_AUTHOR("Anant Gole <anantgole@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -63,7 +63,7 @@ MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
|
|||
* See Documentation/networking/can.txt for details.
|
||||
*/
|
||||
|
||||
static int echo; /* echo testing. Default: 0 (Off) */
|
||||
static bool echo; /* echo testing. Default: 0 (Off) */
|
||||
module_param(echo, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)");
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
menu "Distributed Switch Architecture drivers"
|
||||
depends on NET_DSA
|
||||
|
||||
config NET_DSA_MV88E6XXX
|
||||
tristate
|
||||
default n
|
||||
|
||||
config NET_DSA_MV88E6060
|
||||
tristate "Marvell 88E6060 ethernet switch chip support"
|
||||
select NET_DSA_TAG_TRAILER
|
||||
---help---
|
||||
This enables support for the Marvell 88E6060 ethernet switch
|
||||
chip.
|
||||
|
||||
config NET_DSA_MV88E6XXX_NEED_PPU
|
||||
bool
|
||||
default n
|
||||
|
||||
config NET_DSA_MV88E6131
|
||||
tristate "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support"
|
||||
select NET_DSA_MV88E6XXX
|
||||
select NET_DSA_MV88E6XXX_NEED_PPU
|
||||
select NET_DSA_TAG_DSA
|
||||
---help---
|
||||
This enables support for the Marvell 88E6085/6095/6095F/6131
|
||||
ethernet switch chips.
|
||||
|
||||
config NET_DSA_MV88E6123_61_65
|
||||
tristate "Marvell 88E6123/6161/6165 ethernet switch chip support"
|
||||
select NET_DSA_MV88E6XXX
|
||||
select NET_DSA_TAG_EDSA
|
||||
---help---
|
||||
This enables support for the Marvell 88E6123/6161/6165
|
||||
ethernet switch chips.
|
||||
|
||||
endmenu
|
|
@ -0,0 +1,9 @@
|
|||
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
|
||||
obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx_drv.o
|
||||
mv88e6xxx_drv-y += mv88e6xxx.o
|
||||
ifdef CONFIG_NET_DSA_MV88E6123_61_65
|
||||
mv88e6xxx_drv-y += mv88e6123_61_65.o
|
||||
endif
|
||||
ifdef CONFIG_NET_DSA_MV88E6131
|
||||
mv88e6xxx_drv-y += mv88e6131.o
|
||||
endif
|
|
@ -11,7 +11,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/phy.h>
|
||||
#include "dsa_priv.h"
|
||||
#include <net/dsa.h>
|
||||
|
||||
#define REG_PORT(p) (8 + (p))
|
||||
#define REG_GLOBAL 0x0f
|
||||
|
@ -286,3 +286,8 @@ static void __exit mv88e6060_cleanup(void)
|
|||
unregister_switch_driver(&mv88e6060_switch_driver);
|
||||
}
|
||||
module_exit(mv88e6060_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
|
||||
MODULE_DESCRIPTION("Driver for Marvell 88E6060 ethernet switch chip");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mv88e6060");
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue