mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-15 08:44:14 +08:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: Changes of note: 1) Allow to schedule ICMP packets in IPVS, from Alex Gartrell. 2) Provide FIB table ID in ipv4 route dumps just as ipv6 does, from David Ahern. 3) Allow the user to ask for the statistics to be filtered out of ipv4/ipv6 address netlink dumps. From Sowmini Varadhan. 4) More work to pass the network namespace context around deep into various packet path APIs, starting with the netfilter hooks. From Eric W Biederman. 5) Add layer 2 TX/RX checksum offloading to qeth driver, from Thomas Richter. 6) Use usec resolution for SYN/ACK RTTs in TCP, from Yuchung Cheng. 7) Support Very High Throughput in wireless MESH code, from Bob Copeland. 8) Allow setting the ageing_time in switchdev/rocker. From Scott Feldman. 9) Properly autoload L2TP type modules, from Stephen Hemminger. 10) Fix and enable offload features by default in 8139cp driver, from David Woodhouse. 11) Support both ipv4 and ipv6 sockets in a single vxlan device, from Jiri Benc. 12) Fix CWND limiting of thin streams in TCP, from Bendik Rønning Opstad. 13) Fix IPSEC flowcache overflows on large systems, from Steffen Klassert. 14) Convert bridging to track VLANs using rhashtable entries rather than a bitmap. From Nikolay Aleksandrov. 15) Make TCP listener handling completely lockless, this is a major accomplishment. Incoming request sockets now live in the established hash table just like any other socket too. From Eric Dumazet. 15) Provide more bridging attributes to netlink, from Nikolay Aleksandrov. 16) Use hash based algorithm for ipv4 multipath routing, this was very long overdue. From Peter Nørlund. 17) Several y2038 cures, mostly avoiding timespec. From Arnd Bergmann. 18) Allow non-root execution of EBPF programs, from Alexei Starovoitov. 19) Support SO_INCOMING_CPU as setsockopt, from Eric Dumazet. This influences the port binding selection logic used by SO_REUSEPORT. 20) Add ipv6 support to VRF, from David Ahern. 21) Add support for Mellanox Spectrum switch ASIC, from Jiri Pirko. 22) Add rtl8xxxu Realtek wireless driver, from Jes Sorensen. 23) Implement RACK loss recovery in TCP, from Yuchung Cheng. 24) Support multipath routes in MPLS, from Roopa Prabhu. 25) Fix POLLOUT notification for listening sockets in AF_UNIX, from Eric Dumazet. 26) Add new QED Qlogic river, from Yuval Mintz, Manish Chopra, and Sudarsana Kalluru. 27) Don't fetch timestamps on AF_UNIX sockets, from Hannes Frederic Sowa. 28) Support ipv6 geneve tunnels, from John W Linville. 29) Add flood control support to switchdev layer, from Ido Schimmel. 30) Fix CHECKSUM_PARTIAL handling of potentially fragmented frames, from Hannes Frederic Sowa. 31) Support persistent maps and progs in bpf, from Daniel Borkmann. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1790 commits) sh_eth: use DMA barriers switchdev: respect SKIP_EOPNOTSUPP flag in case there is no recursion net: sched: kill dead code in sch_choke.c irda: Delete an unnecessary check before the function call "irlmp_unregister_service" net: dsa: mv88e6xxx: include DSA ports in VLANs net: dsa: mv88e6xxx: disable SA learning for DSA and CPU ports net/core: fix for_each_netdev_feature vlan: Invoke driver vlan hooks only if device is present arcnet/com20020: add LEDS_CLASS dependency bpf, verifier: annotate verbose printer with __printf dp83640: Only wait for timestamps for packets with timestamping enabled. ptp: Change ptp_class to a proper bitmask dp83640: Prune rx timestamp list before reading from it dp83640: Delay scheduled work. dp83640: Include hash in timestamp/packet matching ipv6: fix tunnel error handling net/mlx5e: Fix LSO vlan insertion net/mlx5e: Re-eanble client vlan TX acceleration net/mlx5e: Return error in case mlx5e_set_features() fails net/mlx5e: Don't allow more than max supported channels ...
This commit is contained in:
commit
b0f85fa11a
@ -154,8 +154,9 @@
|
|||||||
!Finclude/net/cfg80211.h cfg80211_scan_request
|
!Finclude/net/cfg80211.h cfg80211_scan_request
|
||||||
!Finclude/net/cfg80211.h cfg80211_scan_done
|
!Finclude/net/cfg80211.h cfg80211_scan_done
|
||||||
!Finclude/net/cfg80211.h cfg80211_bss
|
!Finclude/net/cfg80211.h cfg80211_bss
|
||||||
!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame
|
!Finclude/net/cfg80211.h cfg80211_inform_bss
|
||||||
!Finclude/net/cfg80211.h cfg80211_inform_bss_width
|
!Finclude/net/cfg80211.h cfg80211_inform_bss_frame_data
|
||||||
|
!Finclude/net/cfg80211.h cfg80211_inform_bss_data
|
||||||
!Finclude/net/cfg80211.h cfg80211_unlink_bss
|
!Finclude/net/cfg80211.h cfg80211_unlink_bss
|
||||||
!Finclude/net/cfg80211.h cfg80211_find_ie
|
!Finclude/net/cfg80211.h cfg80211_find_ie
|
||||||
!Finclude/net/cfg80211.h ieee80211_bss_get_ie
|
!Finclude/net/cfg80211.h ieee80211_bss_get_ie
|
||||||
|
@ -37,6 +37,14 @@ Required properties for ethernet interfaces that have external PHY:
|
|||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- status: Should be "ok" or "disabled" for enabled/disabled. Default is "ok".
|
- status: Should be "ok" or "disabled" for enabled/disabled. Default is "ok".
|
||||||
|
- tx-delay: Delay value for RGMII bridge TX clock.
|
||||||
|
Valid values are between 0 to 7, that maps to
|
||||||
|
417, 717, 1020, 1321, 1611, 1913, 2215, 2514 ps
|
||||||
|
Default value is 4, which corresponds to 1611 ps
|
||||||
|
- rx-delay: Delay value for RGMII bridge RX clock.
|
||||||
|
Valid values are between 0 to 7, that maps to
|
||||||
|
273, 589, 899, 1222, 1480, 1806, 2147, 2464 ps
|
||||||
|
Default value is 2, which corresponds to 899 ps
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
menetclk: menetclk {
|
menetclk: menetclk {
|
||||||
@ -72,5 +80,7 @@ Example:
|
|||||||
|
|
||||||
/* Board-specific peripheral configurations */
|
/* Board-specific peripheral configurations */
|
||||||
&menet {
|
&menet {
|
||||||
|
tx-delay = <4>;
|
||||||
|
rx-delay = <2>;
|
||||||
status = "ok";
|
status = "ok";
|
||||||
};
|
};
|
||||||
|
23
Documentation/devicetree/bindings/net/brcm,iproc-mdio.txt
Normal file
23
Documentation/devicetree/bindings/net/brcm,iproc-mdio.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
* Broadcom iProc MDIO bus controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be "brcm,iproc-mdio"
|
||||||
|
- reg: address and length of the register set for the MDIO interface
|
||||||
|
- #size-cells: must be 1
|
||||||
|
- #address-cells: must be 0
|
||||||
|
|
||||||
|
Child nodes of this MDIO bus controller node are standard Ethernet PHY device
|
||||||
|
nodes as described in Documentation/devicetree/bindings/net/phy.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
mdio@18002000 {
|
||||||
|
compatible = "brcm,iproc-mdio";
|
||||||
|
reg = <0x18002000 0x8>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
#address-cells = <0>;
|
||||||
|
|
||||||
|
enet-gphy@0 {
|
||||||
|
reg = <0>;
|
||||||
|
};
|
||||||
|
};
|
36
Documentation/devicetree/bindings/net/can/sun4i_can.txt
Normal file
36
Documentation/devicetree/bindings/net/can/sun4i_can.txt
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
Allwinner A10/A20 CAN controller Device Tree Bindings
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "allwinner,sun4i-a10-can"
|
||||||
|
- reg: physical base address and size of the Allwinner A10/A20 CAN register map.
|
||||||
|
- interrupts: interrupt specifier for the sole interrupt.
|
||||||
|
- clock: phandle and clock specifier.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
SoC common .dtsi file:
|
||||||
|
|
||||||
|
can0_pins_a: can0@0 {
|
||||||
|
allwinner,pins = "PH20","PH21";
|
||||||
|
allwinner,function = "can";
|
||||||
|
allwinner,drive = <0>;
|
||||||
|
allwinner,pull = <0>;
|
||||||
|
};
|
||||||
|
...
|
||||||
|
can0: can@01c2bc00 {
|
||||||
|
compatible = "allwinner,sun4i-a10-can";
|
||||||
|
reg = <0x01c2bc00 0x400>;
|
||||||
|
interrupts = <0 26 4>;
|
||||||
|
clocks = <&apb1_gates 4>;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
Board specific .dts file:
|
||||||
|
|
||||||
|
can0: can@01c2bc00 {
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&can0_pins_a>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
@ -30,6 +30,13 @@ Optional properties:
|
|||||||
- dual_emac : Specifies Switch to act as Dual EMAC
|
- dual_emac : Specifies Switch to act as Dual EMAC
|
||||||
- syscon : Phandle to the system control device node, which is
|
- syscon : Phandle to the system control device node, which is
|
||||||
the control module device of the am33x
|
the control module device of the am33x
|
||||||
|
- mode-gpios : Should be added if one/multiple gpio lines are
|
||||||
|
required to be driven so that cpsw data lines
|
||||||
|
can be connected to the phy via selective mux.
|
||||||
|
For example in dra72x-evm, pcf gpio has to be
|
||||||
|
driven low so that cpsw slave 0 and phy data
|
||||||
|
lines are connected via mux.
|
||||||
|
|
||||||
|
|
||||||
Slave Properties:
|
Slave Properties:
|
||||||
Required properties:
|
Required properties:
|
||||||
|
@ -57,6 +57,10 @@ Properties:
|
|||||||
"rgmii-id", as all other connection types are detected by hardware.
|
"rgmii-id", as all other connection types are detected by hardware.
|
||||||
- fsl,magic-packet : If present, indicates that the hardware supports
|
- fsl,magic-packet : If present, indicates that the hardware supports
|
||||||
waking up via magic packet.
|
waking up via magic packet.
|
||||||
|
- fsl,wake-on-filer : If present, indicates that the hardware supports
|
||||||
|
waking up by Filer General Purpose Interrupt (FGPI) asserted on the
|
||||||
|
Rx int line. This is an advanced power management capability allowing
|
||||||
|
certain packet types (user) defined by filer rules to wake up the system.
|
||||||
- bd-stash : If present, indicates that the hardware supports stashing
|
- bd-stash : If present, indicates that the hardware supports stashing
|
||||||
buffer descriptors in the L2.
|
buffer descriptors in the L2.
|
||||||
- rx-stash-len : Denotes the number of bytes of a received buffer to stash
|
- rx-stash-len : Denotes the number of bytes of a received buffer to stash
|
||||||
|
@ -32,13 +32,13 @@ Required properties:
|
|||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
|
|
||||||
- compatible: should be "hisilicon,hip04-mdio".
|
- compatible: should be "hisilicon,mdio".
|
||||||
- Inherits from MDIO bus node binding [2]
|
- Inherits from MDIO bus node binding [2]
|
||||||
[2] Documentation/devicetree/bindings/net/phy.txt
|
[2] Documentation/devicetree/bindings/net/phy.txt
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
mdio {
|
mdio {
|
||||||
compatible = "hisilicon,hip04-mdio";
|
compatible = "hisilicon,mdio";
|
||||||
reg = <0x28f1000 0x1000>;
|
reg = <0x28f1000 0x1000>;
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
|
49
Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt
Normal file
49
Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
Hisilicon DSA Fabric device controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be "hisilicon,hns-dsaf-v1" or "hisilicon,hns-dsaf-v2".
|
||||||
|
"hisilicon,hns-dsaf-v1" is for hip05.
|
||||||
|
"hisilicon,hns-dsaf-v2" is for Hi1610 and Hi1612.
|
||||||
|
- dsa-name: dsa fabric name who provide this interface.
|
||||||
|
should be "dsafX", X is the dsaf id.
|
||||||
|
- mode: dsa fabric mode string. only support one of dsaf modes like these:
|
||||||
|
"2port-64vf",
|
||||||
|
"6port-16rss",
|
||||||
|
"6port-16vf".
|
||||||
|
- interrupt-parent: the interrupt parent of this device.
|
||||||
|
- interrupts: should contain the DSA Fabric and rcb interrupt.
|
||||||
|
- reg: specifies base physical address(es) and size of the device registers.
|
||||||
|
The first region is external interface control register base and size.
|
||||||
|
The second region is SerDes base register and size.
|
||||||
|
The third region is the PPE register base and size.
|
||||||
|
The fourth region is dsa fabric base register and size.
|
||||||
|
The fifth region is cpld base register and size, it is not required if do not use cpld.
|
||||||
|
- phy-handle: phy handle of physicl port, 0 if not any phy device. see ethernet.txt [1].
|
||||||
|
- buf-size: rx buffer size, should be 16-1024.
|
||||||
|
- desc-num: number of description in TX and RX queue, should be 512, 1024, 2048 or 4096.
|
||||||
|
|
||||||
|
[1] Documentation/devicetree/bindings/net/phy.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
dsa: dsa@c7000000 {
|
||||||
|
compatible = "hisilicon,hns-dsaf-v1";
|
||||||
|
dsa_name = "dsaf0";
|
||||||
|
mode = "6port-16rss";
|
||||||
|
interrupt-parent = <&mbigen_dsa>;
|
||||||
|
reg = <0x0 0xC0000000 0x0 0x420000
|
||||||
|
0x0 0xC2000000 0x0 0x300000
|
||||||
|
0x0 0xc5000000 0x0 0x890000
|
||||||
|
0x0 0xc7000000 0x0 0x60000>;
|
||||||
|
phy-handle = <0 0 0 0 &soc0_phy4 &soc0_phy5 0 0>;
|
||||||
|
interrupts = <131 4>,<132 4>, <133 4>,<134 4>,
|
||||||
|
<135 4>,<136 4>, <137 4>,<138 4>,
|
||||||
|
<139 4>,<140 4>, <141 4>,<142 4>,
|
||||||
|
<143 4>,<144 4>, <145 4>,<146 4>,
|
||||||
|
<147 4>,<148 4>, <384 1>,<385 1>,
|
||||||
|
<386 1>,<387 1>, <388 1>,<389 1>,
|
||||||
|
<390 1>,<391 1>,
|
||||||
|
buf-size = <4096>;
|
||||||
|
desc-num = <1024>;
|
||||||
|
dma-coherent;
|
||||||
|
};
|
22
Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt
Normal file
22
Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Hisilicon MDIO bus controller
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
- compatible: "hisilicon,mdio","hisilicon,hns-mdio".
|
||||||
|
- reg: The base address of the MDIO bus controller register bank.
|
||||||
|
- #address-cells: Must be <1>.
|
||||||
|
- #size-cells: Must be <0>. MDIO addresses have no size component.
|
||||||
|
|
||||||
|
Typically an MDIO bus might have several children.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
mdio@803c0000 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
compatible = "hisilicon,hns-mdio","hisilicon,mdio";
|
||||||
|
reg = <0x0 0x803c0000 0x0 0x10000>;
|
||||||
|
|
||||||
|
ethernet-phy@0 {
|
||||||
|
...
|
||||||
|
reg = <0>;
|
||||||
|
};
|
||||||
|
};
|
47
Documentation/devicetree/bindings/net/hisilicon-hns-nic.txt
Normal file
47
Documentation/devicetree/bindings/net/hisilicon-hns-nic.txt
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
Hisilicon Network Subsystem NIC controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "hisilicon,hns-nic-v1" or "hisilicon,hns-nic-v2".
|
||||||
|
"hisilicon,hns-nic-v1" is for hip05.
|
||||||
|
"hisilicon,hns-nic-v2" is for Hi1610 and Hi1612.
|
||||||
|
- ae-name: accelerator name who provides this interface,
|
||||||
|
is simply a name referring to the name of name in the accelerator node.
|
||||||
|
- port-id: is the index of port provided by DSAF (the accelerator). DSAF can
|
||||||
|
connect to 8 PHYs. Port 0 to 1 are both used for adminstration purpose. They
|
||||||
|
are called debug ports.
|
||||||
|
|
||||||
|
The remaining 6 PHYs are taken according to the mode of DSAF.
|
||||||
|
|
||||||
|
In NIC mode of DSAF, all 6 PHYs are taken as ethernet ports to the CPU. The
|
||||||
|
port-id can be 2 to 7. Here is the diagram:
|
||||||
|
+-----+---------------+
|
||||||
|
| CPU |
|
||||||
|
+-+-+-+---+-+-+-+-+-+-+
|
||||||
|
| | | | | | | |
|
||||||
|
debug service
|
||||||
|
port port
|
||||||
|
(0,1) (2-7)
|
||||||
|
|
||||||
|
In Switch mode of DSAF, all 6 PHYs are taken as physical ports connect to a
|
||||||
|
LAN Switch while the CPU side assume itself have one single NIC connect to
|
||||||
|
this switch. In this case, the port-id will be 2 only.
|
||||||
|
+-----+---------------+
|
||||||
|
| CPU |
|
||||||
|
+-+-+-+---+-+-+-+-+-+-+
|
||||||
|
| | service| port(2)
|
||||||
|
debug +------------+
|
||||||
|
port | switch |
|
||||||
|
(0,1) +-+-+-+-+-+-++
|
||||||
|
| | | | | |
|
||||||
|
external port
|
||||||
|
|
||||||
|
- local-mac-address: mac addr of the ethernet interface
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
ethernet@0{
|
||||||
|
compatible = "hisilicon,hns-nic-v1";
|
||||||
|
ae-name = "dsaf0";
|
||||||
|
port-id = <0>;
|
||||||
|
local-mac-address = [a2 14 e4 4b 56 76];
|
||||||
|
};
|
@ -0,0 +1,20 @@
|
|||||||
|
* MRF24J40 IEEE 802.15.4 *
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be "microchip,mrf24j40", "microchip,mrf24j40ma",
|
||||||
|
or "microchip,mrf24j40mc" depends on your transceiver
|
||||||
|
board
|
||||||
|
- spi-max-frequency: maximal bus speed, should be set something under or equal
|
||||||
|
10000000
|
||||||
|
- reg: the chipselect index
|
||||||
|
- interrupts: the interrupt generated by the device.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
mrf24j40ma@0 {
|
||||||
|
compatible = "microchip,mrf24j40ma";
|
||||||
|
spi-max-frequency = <8500000>;
|
||||||
|
reg = <0>;
|
||||||
|
interrupts = <19 8>;
|
||||||
|
interrupt-parent = <&gpio3>;
|
||||||
|
};
|
@ -1,7 +1,10 @@
|
|||||||
* Marvell International Ltd. NCI NFC Controller
|
* Marvell International Ltd. NCI NFC Controller
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should be "mrvl,nfc-uart".
|
- compatible: Should be:
|
||||||
|
- "marvell,nfc-uart" or "mrvl,nfc-uart" for UART devices
|
||||||
|
- "marvell,nfc-i2c" for I2C devices
|
||||||
|
- "marvell,nfc-spi" for SPI devices
|
||||||
|
|
||||||
Optional SoC specific properties:
|
Optional SoC specific properties:
|
||||||
- pinctrl-names: Contains only one value - "default".
|
- pinctrl-names: Contains only one value - "default".
|
||||||
@ -13,13 +16,19 @@ Optional UART-based chip specific properties:
|
|||||||
- flow-control: Specifies that the chip is using RTS/CTS.
|
- flow-control: Specifies that the chip is using RTS/CTS.
|
||||||
- break-control: Specifies that the chip needs specific break management.
|
- break-control: Specifies that the chip needs specific break management.
|
||||||
|
|
||||||
|
Optional I2C-based chip specific properties:
|
||||||
|
- i2c-int-falling: Specifies that the chip read event shall be trigged on
|
||||||
|
falling edge.
|
||||||
|
- i2c-int-rising: Specifies that the chip read event shall be trigged on
|
||||||
|
rising edge.
|
||||||
|
|
||||||
Example (for ARM-based BeagleBoard Black with 88W8887 on UART5):
|
Example (for ARM-based BeagleBoard Black with 88W8887 on UART5):
|
||||||
|
|
||||||
&uart5 {
|
&uart5 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|
||||||
nfcmrvluart: nfcmrvluart@5 {
|
nfcmrvluart: nfcmrvluart@5 {
|
||||||
compatible = "mrvl,nfc-uart";
|
compatible = "marvell,nfc-uart";
|
||||||
|
|
||||||
reset-n-io = <&gpio3 16 0>;
|
reset-n-io = <&gpio3 16 0>;
|
||||||
|
|
||||||
@ -27,3 +36,51 @@ Example (for ARM-based BeagleBoard Black with 88W8887 on UART5):
|
|||||||
flow-control;
|
flow-control;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Example (for ARM-based BeagleBoard Black with 88W8887 on I2C1):
|
||||||
|
|
||||||
|
&i2c1 {
|
||||||
|
status = "okay";
|
||||||
|
clock-frequency = <400000>;
|
||||||
|
|
||||||
|
nfcmrvli2c0: i2c@1 {
|
||||||
|
compatible = "marvell,nfc-i2c";
|
||||||
|
|
||||||
|
reg = <0x8>;
|
||||||
|
|
||||||
|
/* I2C INT configuration */
|
||||||
|
interrupt-parent = <&gpio3>;
|
||||||
|
interrupts = <21 0>;
|
||||||
|
|
||||||
|
/* I2C INT trigger configuration */
|
||||||
|
i2c-int-rising;
|
||||||
|
|
||||||
|
/* Reset IO */
|
||||||
|
reset-n-io = <&gpio3 19 0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Example (for ARM-based BeagleBoard Black on SPI0):
|
||||||
|
|
||||||
|
&spi0 {
|
||||||
|
|
||||||
|
mrvlnfcspi0: spi@0 {
|
||||||
|
compatible = "marvell,nfc-spi";
|
||||||
|
|
||||||
|
reg = <0>;
|
||||||
|
|
||||||
|
/* SPI Bus configuration */
|
||||||
|
spi-max-frequency = <3000000>;
|
||||||
|
spi-cpha;
|
||||||
|
spi-cpol;
|
||||||
|
|
||||||
|
/* SPI INT configuration */
|
||||||
|
interrupt-parent = <&gpio1>;
|
||||||
|
interrupts = <17 0>;
|
||||||
|
|
||||||
|
/* Reset IO */
|
||||||
|
reset-n-io = <&gpio3 19 0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -11,6 +11,10 @@ Required properties:
|
|||||||
Optional SoC Specific Properties:
|
Optional SoC Specific Properties:
|
||||||
- pinctrl-names: Contains only one value - "default".
|
- pinctrl-names: Contains only one value - "default".
|
||||||
- pintctrl-0: Specifies the pin control groups used for this controller.
|
- pintctrl-0: Specifies the pin control groups used for this controller.
|
||||||
|
- ese-present: Specifies that an ese is physically connected to the nfc
|
||||||
|
controller.
|
||||||
|
- uicc-present: Specifies that the uicc swp signal can be physically
|
||||||
|
connected to the nfc controller.
|
||||||
|
|
||||||
Example (for ARM-based BeagleBoard xM with ST21NFCB on I2C2):
|
Example (for ARM-based BeagleBoard xM with ST21NFCB on I2C2):
|
||||||
|
|
||||||
@ -29,5 +33,8 @@ Example (for ARM-based BeagleBoard xM with ST21NFCB on I2C2):
|
|||||||
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
|
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
|
||||||
reset-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
|
reset-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
|
||||||
|
|
||||||
|
ese-present;
|
||||||
|
uicc-present;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should be "st,st21nfcb-spi"
|
- compatible: Should be "st,st21nfcb-spi"
|
||||||
- spi-max-frequency: Maximum SPI frequency (<= 10000000).
|
- spi-max-frequency: Maximum SPI frequency (<= 4000000).
|
||||||
- interrupt-parent: phandle for the interrupt gpio controller
|
- interrupt-parent: phandle for the interrupt gpio controller
|
||||||
- interrupts: GPIO interrupt to which the chip is connected
|
- interrupts: GPIO interrupt to which the chip is connected
|
||||||
- reset-gpios: Output GPIO pin used to reset the ST21NFCB
|
- reset-gpios: Output GPIO pin used to reset the ST21NFCB
|
||||||
@ -10,6 +10,10 @@ Required properties:
|
|||||||
Optional SoC Specific Properties:
|
Optional SoC Specific Properties:
|
||||||
- pinctrl-names: Contains only one value - "default".
|
- pinctrl-names: Contains only one value - "default".
|
||||||
- pintctrl-0: Specifies the pin control groups used for this controller.
|
- pintctrl-0: Specifies the pin control groups used for this controller.
|
||||||
|
- ese-present: Specifies that an ese is physically connected to the nfc
|
||||||
|
controller.
|
||||||
|
- uicc-present: Specifies that the uicc swp signal can be physically
|
||||||
|
connected to the nfc controller.
|
||||||
|
|
||||||
Example (for ARM-based BeagleBoard xM with ST21NFCB on SPI4):
|
Example (for ARM-based BeagleBoard xM with ST21NFCB on SPI4):
|
||||||
|
|
||||||
@ -27,5 +31,8 @@ Example (for ARM-based BeagleBoard xM with ST21NFCB on SPI4):
|
|||||||
interrupts = <2 IRQ_TYPE_EDGE_RISING>;
|
interrupts = <2 IRQ_TYPE_EDGE_RISING>;
|
||||||
|
|
||||||
reset-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
|
reset-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
|
||||||
|
|
||||||
|
ese-present;
|
||||||
|
uicc-present;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -6,8 +6,12 @@ interface contains.
|
|||||||
Required properties:
|
Required properties:
|
||||||
- compatible: "renesas,etheravb-r8a7790" if the device is a part of R8A7790 SoC.
|
- compatible: "renesas,etheravb-r8a7790" if the device is a part of R8A7790 SoC.
|
||||||
"renesas,etheravb-r8a7794" if the device is a part of R8A7794 SoC.
|
"renesas,etheravb-r8a7794" if the device is a part of R8A7794 SoC.
|
||||||
|
"renesas,etheravb-r8a7795" if the device is a part of R8A7795 SoC.
|
||||||
- reg: offset and length of (1) the register block and (2) the stream buffer.
|
- reg: offset and length of (1) the register block and (2) the stream buffer.
|
||||||
- interrupts: interrupt specifier for the sole interrupt.
|
- interrupts: A list of interrupt-specifiers, one for each entry in
|
||||||
|
interrupt-names.
|
||||||
|
If interrupt-names is not present, an interrupt specifier
|
||||||
|
for a single muxed interrupt.
|
||||||
- phy-mode: see ethernet.txt file in the same directory.
|
- phy-mode: see ethernet.txt file in the same directory.
|
||||||
- phy-handle: see ethernet.txt file in the same directory.
|
- phy-handle: see ethernet.txt file in the same directory.
|
||||||
- #address-cells: number of address cells for the MDIO bus, must be equal to 1.
|
- #address-cells: number of address cells for the MDIO bus, must be equal to 1.
|
||||||
@ -18,6 +22,12 @@ Required properties:
|
|||||||
Optional properties:
|
Optional properties:
|
||||||
- interrupt-parent: the phandle for the interrupt controller that services
|
- interrupt-parent: the phandle for the interrupt controller that services
|
||||||
interrupts for this device.
|
interrupts for this device.
|
||||||
|
- interrupt-names: A list of interrupt names.
|
||||||
|
For the R8A7795 SoC this property is mandatory;
|
||||||
|
it should include one entry per channel, named "ch%u",
|
||||||
|
where %u is the channel number ranging from 0 to 24.
|
||||||
|
For other SoCs this property is optional; if present
|
||||||
|
it should contain "mux" for a single muxed interrupt.
|
||||||
- pinctrl-names: pin configuration state name ("default").
|
- pinctrl-names: pin configuration state name ("default").
|
||||||
- renesas,no-ether-link: boolean, specify when a board does not provide a proper
|
- renesas,no-ether-link: boolean, specify when a board does not provide a proper
|
||||||
AVB_LINK signal.
|
AVB_LINK signal.
|
||||||
@ -27,13 +37,46 @@ Optional properties:
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
ethernet@e6800000 {
|
ethernet@e6800000 {
|
||||||
compatible = "renesas,etheravb-r8a7790";
|
compatible = "renesas,etheravb-r8a7795";
|
||||||
reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>;
|
reg = <0 0xe6800000 0 0x800>, <0 0xe6a00000 0 0x10000>;
|
||||||
interrupt-parent = <&gic>;
|
interrupt-parent = <&gic>;
|
||||||
interrupts = <0 163 IRQ_TYPE_LEVEL_HIGH>;
|
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
clocks = <&mstp8_clks R8A7790_CLK_ETHERAVB>;
|
<GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
phy-mode = "rmii";
|
<GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
interrupt-names = "ch0", "ch1", "ch2", "ch3",
|
||||||
|
"ch4", "ch5", "ch6", "ch7",
|
||||||
|
"ch8", "ch9", "ch10", "ch11",
|
||||||
|
"ch12", "ch13", "ch14", "ch15",
|
||||||
|
"ch16", "ch17", "ch18", "ch19",
|
||||||
|
"ch20", "ch21", "ch22", "ch23",
|
||||||
|
"ch24";
|
||||||
|
clocks = <&mstp8_clks R8A7795_CLK_ETHERAVB>;
|
||||||
|
power-domains = <&cpg_clocks>;
|
||||||
|
phy-mode = "rgmii-id";
|
||||||
phy-handle = <&phy0>;
|
phy-handle = <&phy0>;
|
||||||
|
|
||||||
pinctrl-0 = <ðer_pins>;
|
pinctrl-0 = <ðer_pins>;
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
renesas,no-ether-link;
|
renesas,no-ether-link;
|
||||||
@ -41,8 +84,20 @@ Example:
|
|||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
|
|
||||||
phy0: ethernet-phy@0 {
|
phy0: ethernet-phy@0 {
|
||||||
|
rxc-skew-ps = <900>;
|
||||||
|
rxdv-skew-ps = <0>;
|
||||||
|
rxd0-skew-ps = <0>;
|
||||||
|
rxd1-skew-ps = <0>;
|
||||||
|
rxd2-skew-ps = <0>;
|
||||||
|
rxd3-skew-ps = <0>;
|
||||||
|
txc-skew-ps = <900>;
|
||||||
|
txen-skew-ps = <0>;
|
||||||
|
txd0-skew-ps = <0>;
|
||||||
|
txd1-skew-ps = <0>;
|
||||||
|
txd2-skew-ps = <0>;
|
||||||
|
txd3-skew-ps = <0>;
|
||||||
reg = <0>;
|
reg = <0>;
|
||||||
interrupt-parent = <&gpio2>;
|
interrupt-parent = <&gpio2>;
|
||||||
interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
|
interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -157,6 +157,9 @@ ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:
|
|||||||
both: use both BOOTP and RARP but not DHCP
|
both: use both BOOTP and RARP but not DHCP
|
||||||
(old option kept for backwards compatibility)
|
(old option kept for backwards compatibility)
|
||||||
|
|
||||||
|
if dhcp is used, the client identifier can be used by following
|
||||||
|
format "ip=dhcp,client-id-type,client-id-value"
|
||||||
|
|
||||||
Default: any
|
Default: any
|
||||||
|
|
||||||
<dns0-ip> IP address of first nameserver.
|
<dns0-ip> IP address of first nameserver.
|
||||||
|
@ -7,11 +7,11 @@ Introduction
|
|||||||
The IEEE 802.15.4 working group focuses on standardization of bottom
|
The IEEE 802.15.4 working group focuses on standardization of bottom
|
||||||
two layers: Medium Access Control (MAC) and Physical (PHY). And there
|
two layers: Medium Access Control (MAC) and Physical (PHY). And there
|
||||||
are mainly two options available for upper layers:
|
are mainly two options available for upper layers:
|
||||||
- ZigBee - proprietary protocol from ZigBee Alliance
|
- ZigBee - proprietary protocol from the ZigBee Alliance
|
||||||
- 6LowPAN - IPv6 networking over low rate personal area networks
|
- 6LoWPAN - IPv6 networking over low rate personal area networks
|
||||||
|
|
||||||
The Linux-ZigBee project goal is to provide complete implementation
|
The linux-wpan project goal is to provide a complete implementation
|
||||||
of IEEE 802.15.4 and 6LoWPAN protocols. IEEE 802.15.4 is a stack
|
of the IEEE 802.15.4 and 6LoWPAN protocols. IEEE 802.15.4 is a stack
|
||||||
of protocols for organizing Low-Rate Wireless Personal Area Networks.
|
of protocols for organizing Low-Rate Wireless Personal Area Networks.
|
||||||
|
|
||||||
The stack is composed of three main parts:
|
The stack is composed of three main parts:
|
||||||
|
@ -384,6 +384,14 @@ tcp_mem - vector of 3 INTEGERs: min, pressure, max
|
|||||||
Defaults are calculated at boot time from amount of available
|
Defaults are calculated at boot time from amount of available
|
||||||
memory.
|
memory.
|
||||||
|
|
||||||
|
tcp_min_rtt_wlen - INTEGER
|
||||||
|
The window length of the windowed min filter to track the minimum RTT.
|
||||||
|
A shorter window lets a flow more quickly pick up new (higher)
|
||||||
|
minimum RTT when it is moved to a longer path (e.g., due to traffic
|
||||||
|
engineering). A longer window makes the filter more resistant to RTT
|
||||||
|
inflations such as transient congestion. The unit is seconds.
|
||||||
|
Default: 300
|
||||||
|
|
||||||
tcp_moderate_rcvbuf - BOOLEAN
|
tcp_moderate_rcvbuf - BOOLEAN
|
||||||
If set, TCP performs receive buffer auto-tuning, attempting to
|
If set, TCP performs receive buffer auto-tuning, attempting to
|
||||||
automatically size the buffer (no greater than tcp_rmem[2]) to
|
automatically size the buffer (no greater than tcp_rmem[2]) to
|
||||||
@ -425,6 +433,15 @@ tcp_orphan_retries - INTEGER
|
|||||||
you should think about lowering this value, such sockets
|
you should think about lowering this value, such sockets
|
||||||
may consume significant resources. Cf. tcp_max_orphans.
|
may consume significant resources. Cf. tcp_max_orphans.
|
||||||
|
|
||||||
|
tcp_recovery - INTEGER
|
||||||
|
This value is a bitmap to enable various experimental loss recovery
|
||||||
|
features.
|
||||||
|
|
||||||
|
RACK: 0x1 enables the RACK loss detection for fast detection of lost
|
||||||
|
retransmissions and tail drops.
|
||||||
|
|
||||||
|
Default: 0x1
|
||||||
|
|
||||||
tcp_reordering - INTEGER
|
tcp_reordering - INTEGER
|
||||||
Initial reordering level of packets in a TCP stream.
|
Initial reordering level of packets in a TCP stream.
|
||||||
TCP stack can then dynamically adjust flow reordering level
|
TCP stack can then dynamically adjust flow reordering level
|
||||||
@ -1199,7 +1216,8 @@ tag - INTEGER
|
|||||||
xfrm4_gc_thresh - INTEGER
|
xfrm4_gc_thresh - INTEGER
|
||||||
The threshold at which we will start garbage collecting for IPv4
|
The threshold at which we will start garbage collecting for IPv4
|
||||||
destination cache entries. At twice this value the system will
|
destination cache entries. At twice this value the system will
|
||||||
refuse new allocations.
|
refuse new allocations. The value must be set below the flowcache
|
||||||
|
limit (4096 * number of online cpus) to take effect.
|
||||||
|
|
||||||
igmp_link_local_mcast_reports - BOOLEAN
|
igmp_link_local_mcast_reports - BOOLEAN
|
||||||
Enable IGMP reports for link local multicast groups in the
|
Enable IGMP reports for link local multicast groups in the
|
||||||
@ -1645,7 +1663,8 @@ ratelimit - INTEGER
|
|||||||
xfrm6_gc_thresh - INTEGER
|
xfrm6_gc_thresh - INTEGER
|
||||||
The threshold at which we will start garbage collecting for IPv6
|
The threshold at which we will start garbage collecting for IPv6
|
||||||
destination cache entries. At twice this value the system will
|
destination cache entries. At twice this value the system will
|
||||||
refuse new allocations.
|
refuse new allocations. The value must be set below the flowcache
|
||||||
|
limit (4096 * number of online cpus) to take effect.
|
||||||
|
|
||||||
|
|
||||||
IPv6 Update by:
|
IPv6 Update by:
|
||||||
|
@ -157,6 +157,16 @@ expire_quiescent_template - BOOLEAN
|
|||||||
persistence template if it is to be used to schedule a new
|
persistence template if it is to be used to schedule a new
|
||||||
connection and the destination server is quiescent.
|
connection and the destination server is quiescent.
|
||||||
|
|
||||||
|
ignore_tunneled - BOOLEAN
|
||||||
|
0 - disabled (default)
|
||||||
|
not 0 - enabled
|
||||||
|
|
||||||
|
If set, ipvs will set the ipvs_property on all packets which are of
|
||||||
|
unrecognized protocols. This prevents us from routing tunneled
|
||||||
|
protocols like ipip, which is useful to prevent rescheduling
|
||||||
|
packets that have been tunneled to the ipvs host (i.e. to prevent
|
||||||
|
ipvs routing loops when ipvs is also acting as a real server).
|
||||||
|
|
||||||
nat_icmp_send - BOOLEAN
|
nat_icmp_send - BOOLEAN
|
||||||
0 - disabled (default)
|
0 - disabled (default)
|
||||||
not 0 - enabled
|
not 0 - enabled
|
||||||
|
@ -213,15 +213,12 @@ To create an L2TPv3 ethernet pseudowire between local host 192.168.1.1
|
|||||||
and peer 192.168.1.2, using IP addresses 10.5.1.1 and 10.5.1.2 for the
|
and peer 192.168.1.2, using IP addresses 10.5.1.1 and 10.5.1.2 for the
|
||||||
tunnel endpoints:-
|
tunnel endpoints:-
|
||||||
|
|
||||||
# modprobe l2tp_eth
|
|
||||||
# modprobe l2tp_netlink
|
|
||||||
|
|
||||||
# ip l2tp add tunnel tunnel_id 1 peer_tunnel_id 1 udp_sport 5000 \
|
# ip l2tp add tunnel tunnel_id 1 peer_tunnel_id 1 udp_sport 5000 \
|
||||||
udp_dport 5000 encap udp local 192.168.1.1 remote 192.168.1.2
|
udp_dport 5000 encap udp local 192.168.1.1 remote 192.168.1.2
|
||||||
# ip l2tp add session tunnel_id 1 session_id 1 peer_session_id 1
|
# ip l2tp add session tunnel_id 1 session_id 1 peer_session_id 1
|
||||||
# ifconfig -a
|
# ip -s -d show dev l2tpeth0
|
||||||
# ip addr add 10.5.1.2/32 peer 10.5.1.1/32 dev l2tpeth0
|
# ip addr add 10.5.1.2/32 peer 10.5.1.1/32 dev l2tpeth0
|
||||||
# ifconfig l2tpeth0 up
|
# ip li set dev l2tpeth0 up
|
||||||
|
|
||||||
Choose IP addresses to be the address of a local IP interface and that
|
Choose IP addresses to be the address of a local IP interface and that
|
||||||
of the remote system. The IP addresses of the l2tpeth0 interface can be
|
of the remote system. The IP addresses of the l2tpeth0 interface can be
|
||||||
|
@ -115,7 +115,7 @@ Switch ID
|
|||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
||||||
The switchdev driver must implement the switchdev op switchdev_port_attr_get
|
The switchdev driver must implement the switchdev op switchdev_port_attr_get
|
||||||
for SWITCHDEV_ATTR_PORT_PARENT_ID for each port netdev, returning the same
|
for SWITCHDEV_ATTR_ID_PORT_PARENT_ID for each port netdev, returning the same
|
||||||
physical ID for each port of a switch. The ID must be unique between switches
|
physical ID for each port of a switch. The ID must be unique between switches
|
||||||
on the same system. The ID does not need to be unique between switches on
|
on the same system. The ID does not need to be unique between switches on
|
||||||
different systems.
|
different systems.
|
||||||
@ -178,7 +178,7 @@ entries are installed, for example, using iproute2 bridge cmd:
|
|||||||
bridge fdb add ADDR dev DEV [vlan VID] [self]
|
bridge fdb add ADDR dev DEV [vlan VID] [self]
|
||||||
|
|
||||||
The driver should use the helper switchdev_port_fdb_xxx ops for ndo_fdb_xxx
|
The driver should use the helper switchdev_port_fdb_xxx ops for ndo_fdb_xxx
|
||||||
ops, and handle add/delete/dump of SWITCHDEV_OBJ_PORT_FDB object using
|
ops, and handle add/delete/dump of SWITCHDEV_OBJ_ID_PORT_FDB object using
|
||||||
switchdev_port_obj_xxx ops.
|
switchdev_port_obj_xxx ops.
|
||||||
|
|
||||||
XXX: what should be done if offloading this rule to hardware fails (for
|
XXX: what should be done if offloading this rule to hardware fails (for
|
||||||
@ -233,26 +233,27 @@ the bridge's FDB. It's possible, but not optimal, to enable learning on the
|
|||||||
device port and on the bridge port, and disable learning_sync.
|
device port and on the bridge port, and disable learning_sync.
|
||||||
|
|
||||||
To support learning and learning_sync port attributes, the driver implements
|
To support learning and learning_sync port attributes, the driver implements
|
||||||
switchdev op switchdev_port_attr_get/set for SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS.
|
switchdev op switchdev_port_attr_get/set for
|
||||||
The driver should initialize the attributes to the hardware defaults.
|
SWITCHDEV_ATTR_PORT_ID_BRIDGE_FLAGS. The driver should initialize the attributes
|
||||||
|
to the hardware defaults.
|
||||||
|
|
||||||
FDB Ageing
|
FDB Ageing
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
There are two FDB ageing models supported: 1) ageing by the device, and 2)
|
The bridge will skip ageing FDB entries marked with NTF_EXT_LEARNED and it is
|
||||||
ageing by the kernel. Ageing by the device is preferred if many FDB entries
|
the responsibility of the port driver/device to age out these entries. If the
|
||||||
are supported. The driver calls call_switchdev_notifiers(SWITCHDEV_FDB_DEL,
|
port device supports ageing, when the FDB entry expires, it will notify the
|
||||||
...) to age out the FDB entry. In this model, ageing by the kernel should be
|
driver which in turn will notify the bridge with SWITCHDEV_FDB_DEL. If the
|
||||||
turned off. XXX: how to turn off ageing in kernel on a per-port basis or
|
device does not support ageing, the driver can simulate ageing using a
|
||||||
otherwise prevent the kernel from ageing out the FDB entry?
|
garbage collection timer to monitor FBD entries. Expired entries will be
|
||||||
|
notified to the bridge using SWITCHDEV_FDB_DEL. See rocker driver for
|
||||||
|
example of driver running ageing timer.
|
||||||
|
|
||||||
In the kernel ageing model, the standard bridge ageing mechanism is used to age
|
To keep an NTF_EXT_LEARNED entry "alive", the driver should refresh the FDB
|
||||||
out stale FDB entries. To keep an FDB entry "alive", the driver should refresh
|
entry by calling call_switchdev_notifiers(SWITCHDEV_FDB_ADD, ...). The
|
||||||
the FDB entry by calling call_switchdev_notifiers(SWITCHDEV_FDB_ADD, ...). The
|
|
||||||
notification will reset the FDB entry's last-used time to now. The driver
|
notification will reset the FDB entry's last-used time to now. The driver
|
||||||
should rate limit refresh notifications, for example, no more than once a
|
should rate limit refresh notifications, for example, no more than once a
|
||||||
second. If the FDB entry expires, fdb_delete is called to remove entry from
|
second. (The last-used time is visible using the bridge -s fdb option).
|
||||||
the device.
|
|
||||||
|
|
||||||
STP State Change on Port
|
STP State Change on Port
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -260,7 +261,7 @@ STP State Change on Port
|
|||||||
Internally or with a third-party STP protocol implementation (e.g. mstpd), the
|
Internally or with a third-party STP protocol implementation (e.g. mstpd), the
|
||||||
bridge driver maintains the STP state for ports, and will notify the switch
|
bridge driver maintains the STP state for ports, and will notify the switch
|
||||||
driver of STP state change on a port using the switchdev op
|
driver of STP state change on a port using the switchdev op
|
||||||
switchdev_attr_port_set for SWITCHDEV_ATTR_PORT_STP_UPDATE.
|
switchdev_attr_port_set for SWITCHDEV_ATTR_PORT_ID_STP_UPDATE.
|
||||||
|
|
||||||
State is one of BR_STATE_*. The switch driver can use STP state updates to
|
State is one of BR_STATE_*. The switch driver can use STP state updates to
|
||||||
update ingress packet filter list for the port. For example, if port is
|
update ingress packet filter list for the port. For example, if port is
|
||||||
@ -277,8 +278,8 @@ Flooding L2 domain
|
|||||||
For a given L2 VLAN domain, the switch device should flood multicast/broadcast
|
For a given L2 VLAN domain, the switch device should flood multicast/broadcast
|
||||||
and unknown unicast packets to all ports in domain, if allowed by port's
|
and unknown unicast packets to all ports in domain, if allowed by port's
|
||||||
current STP state. The switch driver, knowing which ports are within which
|
current STP state. The switch driver, knowing which ports are within which
|
||||||
vlan L2 domain, can program the switch device for flooding. The packet should
|
vlan L2 domain, can program the switch device for flooding. The packet may
|
||||||
also be sent to the port netdev for processing by the bridge driver. The
|
be sent to the port netdev for processing by the bridge driver. The
|
||||||
bridge should not reflood the packet to the same ports the device flooded,
|
bridge should not reflood the packet to the same ports the device flooded,
|
||||||
otherwise there will be duplicate packets on the wire.
|
otherwise there will be duplicate packets on the wire.
|
||||||
|
|
||||||
@ -297,6 +298,9 @@ packets up to the bridge driver for flooding. This is not ideal as the number
|
|||||||
of ports scale in the L2 domain as the device is much more efficient at
|
of ports scale in the L2 domain as the device is much more efficient at
|
||||||
flooding packets that software.
|
flooding packets that software.
|
||||||
|
|
||||||
|
If supported by the device, flood control can be offloaded to it, preventing
|
||||||
|
certain netdevs from flooding unicast traffic for which there is no FDB entry.
|
||||||
|
|
||||||
IGMP Snooping
|
IGMP Snooping
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -316,9 +320,9 @@ SWITCHDEV_OBJ_IPV[4|6]_FIB object using switchdev_port_obj_xxx ops.
|
|||||||
switchdev_port_obj_add is used for both adding a new FIB entry to the device,
|
switchdev_port_obj_add is used for both adding a new FIB entry to the device,
|
||||||
or modifying an existing entry on the device.
|
or modifying an existing entry on the device.
|
||||||
|
|
||||||
XXX: Currently, only SWITCHDEV_OBJ_IPV4_FIB objects are supported.
|
XXX: Currently, only SWITCHDEV_OBJ_ID_IPV4_FIB objects are supported.
|
||||||
|
|
||||||
SWITCHDEV_OBJ_IPV4_FIB object passes:
|
SWITCHDEV_OBJ_ID_IPV4_FIB object passes:
|
||||||
|
|
||||||
struct switchdev_obj_ipv4_fib { /* IPV4_FIB */
|
struct switchdev_obj_ipv4_fib { /* IPV4_FIB */
|
||||||
u32 dst;
|
u32 dst;
|
||||||
@ -369,3 +373,22 @@ The driver can monitor for updates to arp_tbl using the netevent notifier
|
|||||||
NETEVENT_NEIGH_UPDATE. The device can be programmed with resolved nexthops
|
NETEVENT_NEIGH_UPDATE. The device can be programmed with resolved nexthops
|
||||||
for the routes as arp_tbl updates. The driver implements ndo_neigh_destroy
|
for the routes as arp_tbl updates. The driver implements ndo_neigh_destroy
|
||||||
to know when arp_tbl neighbor entries are purged from the port.
|
to know when arp_tbl neighbor entries are purged from the port.
|
||||||
|
|
||||||
|
Transaction item queue
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
For switchdev ops attr_set and obj_add, there is a 2 phase transaction model
|
||||||
|
used. First phase is to "prepare" anything needed, including various checks,
|
||||||
|
memory allocation, etc. The goal is to handle the stuff that is not unlikely
|
||||||
|
to fail here. The second phase is to "commit" the actual changes.
|
||||||
|
|
||||||
|
Switchdev provides an inftrastructure for sharing items (for example memory
|
||||||
|
allocations) between the two phases.
|
||||||
|
|
||||||
|
The object created by a driver in "prepare" phase and it is queued up by:
|
||||||
|
switchdev_trans_item_enqueue()
|
||||||
|
During the "commit" phase, the driver gets the object by:
|
||||||
|
switchdev_trans_item_dequeue()
|
||||||
|
|
||||||
|
If a transaction is aborted during "prepare" phase, switchdev code will handle
|
||||||
|
cleanup of the queued-up objects.
|
||||||
|
@ -90,7 +90,304 @@ or to specify the output device using cmsg and IP_PKTINFO.
|
|||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
-----------
|
-----------
|
||||||
VRF device currently only works for IPv4. Support for IPv6 is under development.
|
|
||||||
|
|
||||||
Index of original ingress interface is not available via cmsg. Will address
|
Index of original ingress interface is not available via cmsg. Will address
|
||||||
soon.
|
soon.
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
Using iproute2 for VRFs
|
||||||
|
=======================
|
||||||
|
VRF devices do *not* have to start with 'vrf-'. That is a convention used here
|
||||||
|
for emphasis of the device type, similar to use of 'br' in bridge names.
|
||||||
|
|
||||||
|
1. Create a VRF
|
||||||
|
|
||||||
|
To instantiate a VRF device and associate it with a table:
|
||||||
|
$ ip link add dev NAME type vrf table ID
|
||||||
|
|
||||||
|
Remember to add the ip rules as well:
|
||||||
|
$ ip ru add oif NAME table 10
|
||||||
|
$ ip ru add iif NAME table 10
|
||||||
|
$ ip -6 ru add oif NAME table 10
|
||||||
|
$ ip -6 ru add iif NAME table 10
|
||||||
|
|
||||||
|
Without the rules route lookups are not directed to the table.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
$ ip link add dev vrf-blue type vrf table 10
|
||||||
|
$ ip ru add pref 200 oif vrf-blue table 10
|
||||||
|
$ ip ru add pref 200 iif vrf-blue table 10
|
||||||
|
$ ip -6 ru add pref 200 oif vrf-blue table 10
|
||||||
|
$ ip -6 ru add pref 200 iif vrf-blue table 10
|
||||||
|
|
||||||
|
|
||||||
|
2. List VRFs
|
||||||
|
|
||||||
|
To list VRFs that have been created:
|
||||||
|
$ ip [-d] link show type vrf
|
||||||
|
NOTE: The -d option is needed to show the table id
|
||||||
|
|
||||||
|
For example:
|
||||||
|
$ ip -d link show type vrf
|
||||||
|
11: vrf-mgmt: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
|
||||||
|
link/ether 72:b3:ba:91:e2:24 brd ff:ff:ff:ff:ff:ff promiscuity 0
|
||||||
|
vrf table 1 addrgenmode eui64
|
||||||
|
12: vrf-red: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
|
||||||
|
link/ether b6:6f:6e:f6:da:73 brd ff:ff:ff:ff:ff:ff promiscuity 0
|
||||||
|
vrf table 10 addrgenmode eui64
|
||||||
|
13: vrf-blue: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
|
||||||
|
link/ether 36:62:e8:7d:bb:8c brd ff:ff:ff:ff:ff:ff promiscuity 0
|
||||||
|
vrf table 66 addrgenmode eui64
|
||||||
|
14: vrf-green: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
|
||||||
|
link/ether e6:28:b8:63:70:bb brd ff:ff:ff:ff:ff:ff promiscuity 0
|
||||||
|
vrf table 81 addrgenmode eui64
|
||||||
|
|
||||||
|
|
||||||
|
Or in brief output:
|
||||||
|
|
||||||
|
$ ip -br link show type vrf
|
||||||
|
vrf-mgmt UP 72:b3:ba:91:e2:24 <NOARP,MASTER,UP,LOWER_UP>
|
||||||
|
vrf-red UP b6:6f:6e:f6:da:73 <NOARP,MASTER,UP,LOWER_UP>
|
||||||
|
vrf-blue UP 36:62:e8:7d:bb:8c <NOARP,MASTER,UP,LOWER_UP>
|
||||||
|
vrf-green UP e6:28:b8:63:70:bb <NOARP,MASTER,UP,LOWER_UP>
|
||||||
|
|
||||||
|
|
||||||
|
3. Assign a Network Interface to a VRF
|
||||||
|
|
||||||
|
Network interfaces are assigned to a VRF by enslaving the netdevice to a
|
||||||
|
VRF device:
|
||||||
|
$ ip link set dev NAME master VRF-NAME
|
||||||
|
|
||||||
|
On enslavement connected and local routes are automatically moved to the
|
||||||
|
table associated with the VRF device.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
$ ip link set dev eth0 master vrf-mgmt
|
||||||
|
|
||||||
|
|
||||||
|
4. Show Devices Assigned to a VRF
|
||||||
|
|
||||||
|
To show devices that have been assigned to a specific VRF add the master
|
||||||
|
option to the ip command:
|
||||||
|
$ ip link show master VRF-NAME
|
||||||
|
|
||||||
|
For example:
|
||||||
|
$ ip link show master vrf-red
|
||||||
|
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master vrf-red state UP mode DEFAULT group default qlen 1000
|
||||||
|
link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
|
||||||
|
4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master vrf-red state UP mode DEFAULT group default qlen 1000
|
||||||
|
link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
|
||||||
|
7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master vrf-red state DOWN mode DEFAULT group default qlen 1000
|
||||||
|
link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
|
||||||
|
|
||||||
|
|
||||||
|
Or using the brief output:
|
||||||
|
$ ip -br link show master vrf-red
|
||||||
|
eth1 UP 02:00:00:00:02:02 <BROADCAST,MULTICAST,UP,LOWER_UP>
|
||||||
|
eth2 UP 02:00:00:00:02:03 <BROADCAST,MULTICAST,UP,LOWER_UP>
|
||||||
|
eth5 DOWN 02:00:00:00:02:06 <BROADCAST,MULTICAST>
|
||||||
|
|
||||||
|
|
||||||
|
5. Show Neighbor Entries for a VRF
|
||||||
|
|
||||||
|
To list neighbor entries associated with devices enslaved to a VRF device
|
||||||
|
add the master option to the ip command:
|
||||||
|
$ ip [-6] neigh show master VRF-NAME
|
||||||
|
|
||||||
|
For example:
|
||||||
|
$ ip neigh show master vrf-red
|
||||||
|
10.2.1.254 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
|
||||||
|
10.2.2.254 dev eth2 lladdr 5e:54:01:6a:ee:80 REACHABLE
|
||||||
|
|
||||||
|
$ ip -6 neigh show master vrf-red
|
||||||
|
2002:1::64 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
|
||||||
|
|
||||||
|
|
||||||
|
6. Show Addresses for a VRF
|
||||||
|
|
||||||
|
To show addresses for interfaces associated with a VRF add the master
|
||||||
|
option to the ip command:
|
||||||
|
$ ip addr show master VRF-NAME
|
||||||
|
|
||||||
|
For example:
|
||||||
|
$ ip addr show master vrf-red
|
||||||
|
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master vrf-red state UP group default qlen 1000
|
||||||
|
link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
|
||||||
|
inet 10.2.1.2/24 brd 10.2.1.255 scope global eth1
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
inet6 2002:1::2/120 scope global
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
inet6 fe80::ff:fe00:202/64 scope link
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master vrf-red state UP group default qlen 1000
|
||||||
|
link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
|
||||||
|
inet 10.2.2.2/24 brd 10.2.2.255 scope global eth2
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
inet6 2002:2::2/120 scope global
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
inet6 fe80::ff:fe00:203/64 scope link
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master vrf-red state DOWN group default qlen 1000
|
||||||
|
link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
|
||||||
|
|
||||||
|
Or in brief format:
|
||||||
|
$ ip -br addr show master vrf-red
|
||||||
|
eth1 UP 10.2.1.2/24 2002:1::2/120 fe80::ff:fe00:202/64
|
||||||
|
eth2 UP 10.2.2.2/24 2002:2::2/120 fe80::ff:fe00:203/64
|
||||||
|
eth5 DOWN
|
||||||
|
|
||||||
|
|
||||||
|
7. Show Routes for a VRF
|
||||||
|
|
||||||
|
To show routes for a VRF use the ip command to display the table associated
|
||||||
|
with the VRF device:
|
||||||
|
$ ip [-6] route show table ID
|
||||||
|
|
||||||
|
For example:
|
||||||
|
$ ip route show table vrf-red
|
||||||
|
prohibit default
|
||||||
|
broadcast 10.2.1.0 dev eth1 proto kernel scope link src 10.2.1.2
|
||||||
|
10.2.1.0/24 dev eth1 proto kernel scope link src 10.2.1.2
|
||||||
|
local 10.2.1.2 dev eth1 proto kernel scope host src 10.2.1.2
|
||||||
|
broadcast 10.2.1.255 dev eth1 proto kernel scope link src 10.2.1.2
|
||||||
|
broadcast 10.2.2.0 dev eth2 proto kernel scope link src 10.2.2.2
|
||||||
|
10.2.2.0/24 dev eth2 proto kernel scope link src 10.2.2.2
|
||||||
|
local 10.2.2.2 dev eth2 proto kernel scope host src 10.2.2.2
|
||||||
|
broadcast 10.2.2.255 dev eth2 proto kernel scope link src 10.2.2.2
|
||||||
|
|
||||||
|
$ ip -6 route show table vrf-red
|
||||||
|
local 2002:1:: dev lo proto none metric 0 pref medium
|
||||||
|
local 2002:1::2 dev lo proto none metric 0 pref medium
|
||||||
|
2002:1::/120 dev eth1 proto kernel metric 256 pref medium
|
||||||
|
local 2002:2:: dev lo proto none metric 0 pref medium
|
||||||
|
local 2002:2::2 dev lo proto none metric 0 pref medium
|
||||||
|
2002:2::/120 dev eth2 proto kernel metric 256 pref medium
|
||||||
|
local fe80:: dev lo proto none metric 0 pref medium
|
||||||
|
local fe80:: dev lo proto none metric 0 pref medium
|
||||||
|
local fe80::ff:fe00:202 dev lo proto none metric 0 pref medium
|
||||||
|
local fe80::ff:fe00:203 dev lo proto none metric 0 pref medium
|
||||||
|
fe80::/64 dev eth1 proto kernel metric 256 pref medium
|
||||||
|
fe80::/64 dev eth2 proto kernel metric 256 pref medium
|
||||||
|
ff00::/8 dev vrf-red metric 256 pref medium
|
||||||
|
ff00::/8 dev eth1 metric 256 pref medium
|
||||||
|
ff00::/8 dev eth2 metric 256 pref medium
|
||||||
|
|
||||||
|
|
||||||
|
8. Route Lookup for a VRF
|
||||||
|
|
||||||
|
A test route lookup can be done for a VRF by adding the oif option to ip:
|
||||||
|
$ ip [-6] route get oif VRF-NAME ADDRESS
|
||||||
|
|
||||||
|
For example:
|
||||||
|
$ ip route get 10.2.1.40 oif vrf-red
|
||||||
|
10.2.1.40 dev eth1 table vrf-red src 10.2.1.2
|
||||||
|
cache
|
||||||
|
|
||||||
|
$ ip -6 route get 2002:1::32 oif vrf-red
|
||||||
|
2002:1::32 from :: dev eth1 table vrf-red proto kernel src 2002:1::2 metric 256 pref medium
|
||||||
|
|
||||||
|
|
||||||
|
9. Removing Network Interface from a VRF
|
||||||
|
|
||||||
|
Network interfaces are removed from a VRF by breaking the enslavement to
|
||||||
|
the VRF device:
|
||||||
|
$ ip link set dev NAME nomaster
|
||||||
|
|
||||||
|
Connected routes are moved back to the default table and local entries are
|
||||||
|
moved to the local table.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
$ ip link set dev eth0 nomaster
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Commands used in this example:
|
||||||
|
|
||||||
|
cat >> /etc/iproute2/rt_tables <<EOF
|
||||||
|
1 vrf-mgmt
|
||||||
|
10 vrf-red
|
||||||
|
66 vrf-blue
|
||||||
|
81 vrf-green
|
||||||
|
EOF
|
||||||
|
|
||||||
|
function vrf_create
|
||||||
|
{
|
||||||
|
VRF=$1
|
||||||
|
TBID=$2
|
||||||
|
# create VRF device
|
||||||
|
ip link add vrf-${VRF} type vrf table ${TBID}
|
||||||
|
|
||||||
|
# add rules that direct lookups to vrf table
|
||||||
|
ip ru add pref 200 oif vrf-${VRF} table ${TBID}
|
||||||
|
ip ru add pref 200 iif vrf-${VRF} table ${TBID}
|
||||||
|
ip -6 ru add pref 200 oif vrf-${VRF} table ${TBID}
|
||||||
|
ip -6 ru add pref 200 iif vrf-${VRF} table ${TBID}
|
||||||
|
|
||||||
|
if [ "${VRF}" != "mgmt" ]; then
|
||||||
|
ip route add table ${TBID} prohibit default
|
||||||
|
fi
|
||||||
|
ip link set dev vrf-${VRF} up
|
||||||
|
ip link set dev vrf-${VRF} state up
|
||||||
|
}
|
||||||
|
|
||||||
|
vrf_create mgmt 1
|
||||||
|
ip link set dev eth0 master vrf-mgmt
|
||||||
|
|
||||||
|
vrf_create red 10
|
||||||
|
ip link set dev eth1 master vrf-red
|
||||||
|
ip link set dev eth2 master vrf-red
|
||||||
|
ip link set dev eth5 master vrf-red
|
||||||
|
|
||||||
|
vrf_create blue 66
|
||||||
|
ip link set dev eth3 master vrf-blue
|
||||||
|
|
||||||
|
vrf_create green 81
|
||||||
|
ip link set dev eth4 master vrf-green
|
||||||
|
|
||||||
|
|
||||||
|
Interface addresses from /etc/network/interfaces:
|
||||||
|
auto eth0
|
||||||
|
iface eth0 inet static
|
||||||
|
address 10.0.0.2
|
||||||
|
netmask 255.255.255.0
|
||||||
|
gateway 10.0.0.254
|
||||||
|
|
||||||
|
iface eth0 inet6 static
|
||||||
|
address 2000:1::2
|
||||||
|
netmask 120
|
||||||
|
|
||||||
|
auto eth1
|
||||||
|
iface eth1 inet static
|
||||||
|
address 10.2.1.2
|
||||||
|
netmask 255.255.255.0
|
||||||
|
|
||||||
|
iface eth1 inet6 static
|
||||||
|
address 2002:1::2
|
||||||
|
netmask 120
|
||||||
|
|
||||||
|
auto eth2
|
||||||
|
iface eth2 inet static
|
||||||
|
address 10.2.2.2
|
||||||
|
netmask 255.255.255.0
|
||||||
|
|
||||||
|
iface eth2 inet6 static
|
||||||
|
address 2002:2::2
|
||||||
|
netmask 120
|
||||||
|
|
||||||
|
auto eth3
|
||||||
|
iface eth3 inet static
|
||||||
|
address 10.2.3.2
|
||||||
|
netmask 255.255.255.0
|
||||||
|
|
||||||
|
iface eth3 inet6 static
|
||||||
|
address 2002:3::2
|
||||||
|
netmask 120
|
||||||
|
|
||||||
|
auto eth4
|
||||||
|
iface eth4 inet static
|
||||||
|
address 10.2.4.2
|
||||||
|
netmask 255.255.255.0
|
||||||
|
|
||||||
|
iface eth4 inet6 static
|
||||||
|
address 2002:4::2
|
||||||
|
netmask 120
|
||||||
|
29
MAINTAINERS
29
MAINTAINERS
@ -5574,7 +5574,7 @@ F: drivers/net/wireless/iwlegacy/
|
|||||||
INTEL WIRELESS WIFI LINK (iwlwifi)
|
INTEL WIRELESS WIFI LINK (iwlwifi)
|
||||||
M: Johannes Berg <johannes.berg@intel.com>
|
M: Johannes Berg <johannes.berg@intel.com>
|
||||||
M: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
|
M: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
|
||||||
M: Intel Linux Wireless <ilw@linux.intel.com>
|
M: Intel Linux Wireless <linuxwifi@intel.com>
|
||||||
L: linux-wireless@vger.kernel.org
|
L: linux-wireless@vger.kernel.org
|
||||||
W: http://intellinuxwireless.org
|
W: http://intellinuxwireless.org
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
|
||||||
@ -6121,6 +6121,13 @@ F: Documentation/auxdisplay/ks0108
|
|||||||
F: drivers/auxdisplay/ks0108.c
|
F: drivers/auxdisplay/ks0108.c
|
||||||
F: include/linux/ks0108.h
|
F: include/linux/ks0108.h
|
||||||
|
|
||||||
|
L3MDEV
|
||||||
|
M: David Ahern <dsa@cumulusnetworks.com>
|
||||||
|
L: netdev@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: net/l3mdev
|
||||||
|
F: include/net/l3mdev.h
|
||||||
|
|
||||||
LAPB module
|
LAPB module
|
||||||
L: linux-x25@vger.kernel.org
|
L: linux-x25@vger.kernel.org
|
||||||
S: Orphan
|
S: Orphan
|
||||||
@ -7005,6 +7012,7 @@ M: Alan Ott <alan@signal11.us>
|
|||||||
L: linux-wpan@vger.kernel.org
|
L: linux-wpan@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/net/ieee802154/mrf24j40.c
|
F: drivers/net/ieee802154/mrf24j40.c
|
||||||
|
F: Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
|
||||||
|
|
||||||
MSI LAPTOP SUPPORT
|
MSI LAPTOP SUPPORT
|
||||||
M: "Lee, Chun-Yi" <jlee@suse.com>
|
M: "Lee, Chun-Yi" <jlee@suse.com>
|
||||||
@ -7339,7 +7347,6 @@ S: Odd Fixes
|
|||||||
F: drivers/net/
|
F: drivers/net/
|
||||||
F: include/linux/if_*
|
F: include/linux/if_*
|
||||||
F: include/linux/netdevice.h
|
F: include/linux/netdevice.h
|
||||||
F: include/linux/arcdevice.h
|
|
||||||
F: include/linux/etherdevice.h
|
F: include/linux/etherdevice.h
|
||||||
F: include/linux/fcdevice.h
|
F: include/linux/fcdevice.h
|
||||||
F: include/linux/fddidevice.h
|
F: include/linux/fddidevice.h
|
||||||
@ -8560,6 +8567,16 @@ L: netdev@vger.kernel.org
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/net/ethernet/qlogic/qlge/
|
F: drivers/net/ethernet/qlogic/qlge/
|
||||||
|
|
||||||
|
QLOGIC QL4xxx ETHERNET DRIVER
|
||||||
|
M: Yuval Mintz <Yuval.Mintz@qlogic.com>
|
||||||
|
M: Ariel Elior <Ariel.Elior@qlogic.com>
|
||||||
|
M: everest-linux-l2@qlogic.com
|
||||||
|
L: netdev@vger.kernel.org
|
||||||
|
S: Supported
|
||||||
|
F: drivers/net/ethernet/qlogic/qed/
|
||||||
|
F: include/linux/qed/
|
||||||
|
F: drivers/net/ethernet/qlogic/qede/
|
||||||
|
|
||||||
QNX4 FILESYSTEM
|
QNX4 FILESYSTEM
|
||||||
M: Anders Larsen <al@alarsen.net>
|
M: Anders Larsen <al@alarsen.net>
|
||||||
W: http://www.alarsen.net/linux/qnx4fs/
|
W: http://www.alarsen.net/linux/qnx4fs/
|
||||||
@ -8911,6 +8928,13 @@ S: Maintained
|
|||||||
F: drivers/net/wireless/rtlwifi/
|
F: drivers/net/wireless/rtlwifi/
|
||||||
F: drivers/net/wireless/rtlwifi/rtl8192ce/
|
F: drivers/net/wireless/rtlwifi/rtl8192ce/
|
||||||
|
|
||||||
|
RTL8XXXU WIRELESS DRIVER (rtl8xxxu)
|
||||||
|
M: Jes Sorensen <Jes.Sorensen@redhat.com>
|
||||||
|
L: linux-wireless@vger.kernel.org
|
||||||
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jes/linux.git rtl8723au-mac80211
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/net/wireless/realtek/rtl8xxxu/
|
||||||
|
|
||||||
S3 SAVAGE FRAMEBUFFER DRIVER
|
S3 SAVAGE FRAMEBUFFER DRIVER
|
||||||
M: Antonino Daplas <adaplas@gmail.com>
|
M: Antonino Daplas <adaplas@gmail.com>
|
||||||
L: linux-fbdev@vger.kernel.org
|
L: linux-fbdev@vger.kernel.org
|
||||||
@ -11315,7 +11339,6 @@ M: Shrijeet Mukherjee <shm@cumulusnetworks.com>
|
|||||||
L: netdev@vger.kernel.org
|
L: netdev@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/net/vrf.c
|
F: drivers/net/vrf.c
|
||||||
F: include/net/vrf.h
|
|
||||||
F: Documentation/networking/vrf.txt
|
F: Documentation/networking/vrf.txt
|
||||||
|
|
||||||
VT1211 HARDWARE MONITOR DRIVER
|
VT1211 HARDWARE MONITOR DRIVER
|
||||||
|
@ -591,6 +591,7 @@
|
|||||||
cpts_clock_mult = <0x80000000>;
|
cpts_clock_mult = <0x80000000>;
|
||||||
cpts_clock_shift = <29>;
|
cpts_clock_shift = <29>;
|
||||||
ranges;
|
ranges;
|
||||||
|
syscon = <&scm_conf>;
|
||||||
|
|
||||||
davinci_mdio: mdio@4a101000 {
|
davinci_mdio: mdio@4a101000 {
|
||||||
compatible = "ti,am4372-mdio","ti,davinci_mdio";
|
compatible = "ti,am4372-mdio","ti,davinci_mdio";
|
||||||
|
@ -1448,6 +1448,7 @@
|
|||||||
<GIC_SPI 336 IRQ_TYPE_LEVEL_HIGH>,
|
<GIC_SPI 336 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
<GIC_SPI 337 IRQ_TYPE_LEVEL_HIGH>;
|
<GIC_SPI 337 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
ranges;
|
ranges;
|
||||||
|
syscon = <&scm_conf>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
|
||||||
davinci_mdio: mdio@48485000 {
|
davinci_mdio: mdio@48485000 {
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/gpio_keys.h>
|
#include <linux/gpio_keys.h>
|
||||||
#include <linux/mdio-gpio.h>
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
|
||||||
#include <asm/setup.h>
|
#include <asm/setup.h>
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/gpio_keys.h>
|
#include <linux/gpio_keys.h>
|
||||||
#include <linux/mdio-gpio.h>
|
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
#include <asm/mach-types.h>
|
#include <asm/mach-types.h>
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/gpio_keys.h>
|
#include <linux/gpio_keys.h>
|
||||||
#include <linux/mdio-gpio.h>
|
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
#include <asm/mach-types.h>
|
#include <asm/mach-types.h>
|
||||||
|
@ -125,7 +125,7 @@ static u64 jit_get_skb_w(struct sk_buff *skb, int offset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wrapper that handles both OABI and EABI and assures Thumb2 interworking
|
* Wrappers which handle both OABI and EABI and assures Thumb2 interworking
|
||||||
* (where the assembly routines like __aeabi_uidiv could cause problems).
|
* (where the assembly routines like __aeabi_uidiv could cause problems).
|
||||||
*/
|
*/
|
||||||
static u32 jit_udiv(u32 dividend, u32 divisor)
|
static u32 jit_udiv(u32 dividend, u32 divisor)
|
||||||
@ -133,6 +133,11 @@ static u32 jit_udiv(u32 dividend, u32 divisor)
|
|||||||
return dividend / divisor;
|
return dividend / divisor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 jit_mod(u32 dividend, u32 divisor)
|
||||||
|
{
|
||||||
|
return dividend % divisor;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx)
|
static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx)
|
||||||
{
|
{
|
||||||
inst |= (cond << 28);
|
inst |= (cond << 28);
|
||||||
@ -471,11 +476,17 @@ static inline void emit_blx_r(u8 tgt_reg, struct jit_ctx *ctx)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx)
|
static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx,
|
||||||
|
int bpf_op)
|
||||||
{
|
{
|
||||||
#if __LINUX_ARM_ARCH__ == 7
|
#if __LINUX_ARM_ARCH__ == 7
|
||||||
if (elf_hwcap & HWCAP_IDIVA) {
|
if (elf_hwcap & HWCAP_IDIVA) {
|
||||||
emit(ARM_UDIV(rd, rm, rn), ctx);
|
if (bpf_op == BPF_DIV)
|
||||||
|
emit(ARM_UDIV(rd, rm, rn), ctx);
|
||||||
|
else {
|
||||||
|
emit(ARM_UDIV(ARM_R3, rm, rn), ctx);
|
||||||
|
emit(ARM_MLS(rd, rn, ARM_R3, rm), ctx);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -496,7 +507,8 @@ static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx)
|
|||||||
emit(ARM_MOV_R(ARM_R0, rm), ctx);
|
emit(ARM_MOV_R(ARM_R0, rm), ctx);
|
||||||
|
|
||||||
ctx->seen |= SEEN_CALL;
|
ctx->seen |= SEEN_CALL;
|
||||||
emit_mov_i(ARM_R3, (u32)jit_udiv, ctx);
|
emit_mov_i(ARM_R3, bpf_op == BPF_DIV ? (u32)jit_udiv : (u32)jit_mod,
|
||||||
|
ctx);
|
||||||
emit_blx_r(ARM_R3, ctx);
|
emit_blx_r(ARM_R3, ctx);
|
||||||
|
|
||||||
if (rd != ARM_R0)
|
if (rd != ARM_R0)
|
||||||
@ -698,13 +710,27 @@ load_ind:
|
|||||||
if (k == 1)
|
if (k == 1)
|
||||||
break;
|
break;
|
||||||
emit_mov_i(r_scratch, k, ctx);
|
emit_mov_i(r_scratch, k, ctx);
|
||||||
emit_udiv(r_A, r_A, r_scratch, ctx);
|
emit_udivmod(r_A, r_A, r_scratch, ctx, BPF_DIV);
|
||||||
break;
|
break;
|
||||||
case BPF_ALU | BPF_DIV | BPF_X:
|
case BPF_ALU | BPF_DIV | BPF_X:
|
||||||
update_on_xread(ctx);
|
update_on_xread(ctx);
|
||||||
emit(ARM_CMP_I(r_X, 0), ctx);
|
emit(ARM_CMP_I(r_X, 0), ctx);
|
||||||
emit_err_ret(ARM_COND_EQ, ctx);
|
emit_err_ret(ARM_COND_EQ, ctx);
|
||||||
emit_udiv(r_A, r_A, r_X, ctx);
|
emit_udivmod(r_A, r_A, r_X, ctx, BPF_DIV);
|
||||||
|
break;
|
||||||
|
case BPF_ALU | BPF_MOD | BPF_K:
|
||||||
|
if (k == 1) {
|
||||||
|
emit_mov_i(r_A, 0, ctx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
emit_mov_i(r_scratch, k, ctx);
|
||||||
|
emit_udivmod(r_A, r_A, r_scratch, ctx, BPF_MOD);
|
||||||
|
break;
|
||||||
|
case BPF_ALU | BPF_MOD | BPF_X:
|
||||||
|
update_on_xread(ctx);
|
||||||
|
emit(ARM_CMP_I(r_X, 0), ctx);
|
||||||
|
emit_err_ret(ARM_COND_EQ, ctx);
|
||||||
|
emit_udivmod(r_A, r_A, r_X, ctx, BPF_MOD);
|
||||||
break;
|
break;
|
||||||
case BPF_ALU | BPF_OR | BPF_K:
|
case BPF_ALU | BPF_OR | BPF_K:
|
||||||
/* A |= K */
|
/* A |= K */
|
||||||
@ -1048,7 +1074,7 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||||||
|
|
||||||
set_memory_ro((unsigned long)header, header->pages);
|
set_memory_ro((unsigned long)header, header->pages);
|
||||||
fp->bpf_func = (void *)ctx.target;
|
fp->bpf_func = (void *)ctx.target;
|
||||||
fp->jited = true;
|
fp->jited = 1;
|
||||||
out:
|
out:
|
||||||
kfree(ctx.offsets);
|
kfree(ctx.offsets);
|
||||||
return;
|
return;
|
||||||
|
@ -115,6 +115,8 @@
|
|||||||
|
|
||||||
#define ARM_INST_UMULL 0x00800090
|
#define ARM_INST_UMULL 0x00800090
|
||||||
|
|
||||||
|
#define ARM_INST_MLS 0x00600090
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use a suitable undefined instruction to use for ARM/Thumb2 faulting.
|
* Use a suitable undefined instruction to use for ARM/Thumb2 faulting.
|
||||||
* We need to be careful not to conflict with those used by other modules
|
* We need to be careful not to conflict with those used by other modules
|
||||||
@ -210,4 +212,7 @@
|
|||||||
#define ARM_UMULL(rd_lo, rd_hi, rn, rm) (ARM_INST_UMULL | (rd_hi) << 16 \
|
#define ARM_UMULL(rd_lo, rd_hi, rn, rm) (ARM_INST_UMULL | (rd_hi) << 16 \
|
||||||
| (rd_lo) << 12 | (rm) << 8 | rn)
|
| (rd_lo) << 12 | (rm) << 8 | rn)
|
||||||
|
|
||||||
|
#define ARM_MLS(rd, rn, rm, ra) (ARM_INST_MLS | (rd) << 16 | (rn) | (rm) << 8 \
|
||||||
|
| (ra) << 12)
|
||||||
|
|
||||||
#endif /* PFILTER_OPCODES_ARM_H */
|
#endif /* PFILTER_OPCODES_ARM_H */
|
||||||
|
@ -207,6 +207,17 @@
|
|||||||
clock-output-names = "xge0clk";
|
clock-output-names = "xge0clk";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
xge1clk: xge1clk@1f62c000 {
|
||||||
|
compatible = "apm,xgene-device-clock";
|
||||||
|
status = "disabled";
|
||||||
|
#clock-cells = <1>;
|
||||||
|
clocks = <&socplldiv2 0>;
|
||||||
|
reg = <0x0 0x1f62c000 0x0 0x1000>;
|
||||||
|
reg-names = "csr-reg";
|
||||||
|
csr-mask = <0x3>;
|
||||||
|
clock-output-names = "xge1clk";
|
||||||
|
};
|
||||||
|
|
||||||
sataphy1clk: sataphy1clk@1f21c000 {
|
sataphy1clk: sataphy1clk@1f21c000 {
|
||||||
compatible = "apm,xgene-device-clock";
|
compatible = "apm,xgene-device-clock";
|
||||||
#clock-cells = <1>;
|
#clock-cells = <1>;
|
||||||
@ -826,6 +837,23 @@
|
|||||||
phy-connection-type = "xgmii";
|
phy-connection-type = "xgmii";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
xgenet1: ethernet@1f620000 {
|
||||||
|
compatible = "apm,xgene1-xgenet";
|
||||||
|
status = "disabled";
|
||||||
|
reg = <0x0 0x1f620000 0x0 0xd100>,
|
||||||
|
<0x0 0x1f600000 0x0 0Xc300>,
|
||||||
|
<0x0 0x18000000 0x0 0X8000>;
|
||||||
|
reg-names = "enet_csr", "ring_csr", "ring_cmd";
|
||||||
|
interrupts = <0x0 0x6C 0x4>,
|
||||||
|
<0x0 0x6D 0x4>;
|
||||||
|
port-id = <1>;
|
||||||
|
dma-coherent;
|
||||||
|
clocks = <&xge1clk 0>;
|
||||||
|
/* mac address will be overwritten by the bootloader */
|
||||||
|
local-mac-address = [00 00 00 00 00 00];
|
||||||
|
phy-connection-type = "xgmii";
|
||||||
|
};
|
||||||
|
|
||||||
rng: rng@10520000 {
|
rng: rng@10520000 {
|
||||||
compatible = "apm,xgene-rng";
|
compatible = "apm,xgene-rng";
|
||||||
reg = <0x0 0x10520000 0x0 0x100>;
|
reg = <0x0 0x10520000 0x0 0x100>;
|
||||||
|
191
arch/arm64/boot/dts/hisilicon/hip05_hns.dtsi
Normal file
191
arch/arm64/boot/dts/hisilicon/hip05_hns.dtsi
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
soc0: soc@000000000 {
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
device_type = "soc";
|
||||||
|
compatible = "simple-bus";
|
||||||
|
ranges = <0x0 0x0 0x0 0x0 0x1 0x0>;
|
||||||
|
chip-id = <0>;
|
||||||
|
|
||||||
|
soc0_mdio0: mdio@803c0000 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
compatible = "hisilicon,hns-mdio";
|
||||||
|
reg = <0x0 0x803c0000 0x0 0x10000
|
||||||
|
0x0 0x80000000 0x0 0x10000>;
|
||||||
|
|
||||||
|
soc0_phy0: ethernet-phy@0 {
|
||||||
|
reg = <0x0>;
|
||||||
|
compatible = "ethernet-phy-ieee802.3-c22";
|
||||||
|
};
|
||||||
|
soc0_phy1: ethernet-phy@1 {
|
||||||
|
reg = <0x1>;
|
||||||
|
compatible = "ethernet-phy-ieee802.3-c22";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
dsa: dsa@c7000000 {
|
||||||
|
compatible = "hisilicon,hns-dsaf-v1";
|
||||||
|
dsa_name = "dsaf0";
|
||||||
|
mode = "6port-16rss";
|
||||||
|
interrupt-parent = <&mbigen_dsa>;
|
||||||
|
|
||||||
|
reg = <0x0 0xC0000000 0x0 0x420000
|
||||||
|
0x0 0xC2000000 0x0 0x300000
|
||||||
|
0x0 0xc5000000 0x0 0x890000
|
||||||
|
0x0 0xc7000000 0x0 0x60000
|
||||||
|
>;
|
||||||
|
|
||||||
|
phy-handle = <0 0 0 0 &soc0_phy0 &soc0_phy1 0 0>;
|
||||||
|
interrupts = <
|
||||||
|
/* [14] ge fifo err 8 / xge 6**/
|
||||||
|
149 0x4 150 0x4 151 0x4 152 0x4
|
||||||
|
153 0x4 154 0x4 26 0x4 27 0x4
|
||||||
|
155 0x4 156 0x4 157 0x4 158 0x4 159 0x4 160 0x4
|
||||||
|
/* [12] rcb com 4*3**/
|
||||||
|
0x6 0x4 0x7 0x4 0x8 0x4 0x9 0x4
|
||||||
|
16 0x4 17 0x4 18 0x4 19 0x4
|
||||||
|
22 0x4 23 0x4 24 0x4 25 0x4
|
||||||
|
/* [8] ppe tnl 0-7***/
|
||||||
|
0x0 0x4 0x1 0x4 0x2 0x4 0x3 0x4
|
||||||
|
0x4 0x4 0x5 0x4 12 0x4 13 0x4
|
||||||
|
/* [21] dsaf event int 3+18**/
|
||||||
|
128 0x4 129 0x4 130 0x4
|
||||||
|
0x83 0x4 0x84 0x4 0x85 0x4 0x86 0x4 0x87 0x4 0x88 0x4
|
||||||
|
0x89 0x4 0x8a 0x4 0x8b 0x4 0x8c 0x4 0x8d 0x4 0x8e 0x4
|
||||||
|
0x8f 0x4 0x90 0x4 0x91 0x4 0x92 0x4 0x93 0x4 0x94 0x4
|
||||||
|
/* [4] debug rcb 2*2*/
|
||||||
|
0xe 0x1 0xf 0x1 0x14 0x1 0x15 0x1
|
||||||
|
/* [256] sevice rcb 2*128*/
|
||||||
|
0x180 0x1 0x181 0x1 0x182 0x1 0x183 0x1
|
||||||
|
0x184 0x1 0x185 0x1 0x186 0x1 0x187 0x1
|
||||||
|
0x188 0x1 0x189 0x1 0x18a 0x1 0x18b 0x1
|
||||||
|
0x18c 0x1 0x18d 0x1 0x18e 0x1 0x18f 0x1
|
||||||
|
0x190 0x1 0x191 0x1 0x192 0x1 0x193 0x1
|
||||||
|
0x194 0x1 0x195 0x1 0x196 0x1 0x197 0x1
|
||||||
|
0x198 0x1 0x199 0x1 0x19a 0x1 0x19b 0x1
|
||||||
|
0x19c 0x1 0x19d 0x1 0x19e 0x1 0x19f 0x1
|
||||||
|
0x1a0 0x1 0x1a1 0x1 0x1a2 0x1 0x1a3 0x1
|
||||||
|
0x1a4 0x1 0x1a5 0x1 0x1a6 0x1 0x1a7 0x1
|
||||||
|
0x1a8 0x1 0x1a9 0x1 0x1aa 0x1 0x1ab 0x1
|
||||||
|
0x1ac 0x1 0x1ad 0x1 0x1ae 0x1 0x1af 0x1
|
||||||
|
0x1b0 0x1 0x1b1 0x1 0x1b2 0x1 0x1b3 0x1
|
||||||
|
0x1b4 0x1 0x1b5 0x1 0x1b6 0x1 0x1b7 0x1
|
||||||
|
0x1b8 0x1 0x1b9 0x1 0x1ba 0x1 0x1bb 0x1
|
||||||
|
0x1bc 0x1 0x1bd 0x1 0x1be 0x1 0x1bf 0x1
|
||||||
|
0x1c0 0x1 0x1c1 0x1 0x1c2 0x1 0x1c3 0x1
|
||||||
|
0x1c4 0x1 0x1c5 0x1 0x1c6 0x1 0x1c7 0x1
|
||||||
|
0x1c8 0x1 0x1c9 0x1 0x1ca 0x1 0x1cb 0x1
|
||||||
|
0x1cc 0x1 0x1cd 0x1 0x1ce 0x1 0x1cf 0x1
|
||||||
|
0x1d0 0x1 0x1d1 0x1 0x1d2 0x1 0x1d3 0x1
|
||||||
|
0x1d4 0x1 0x1d5 0x1 0x1d6 0x1 0x1d7 0x1
|
||||||
|
0x1d8 0x1 0x1d9 0x1 0x1da 0x1 0x1db 0x1
|
||||||
|
0x1dc 0x1 0x1dd 0x1 0x1de 0x1 0x1df 0x1
|
||||||
|
0x1e0 0x1 0x1e1 0x1 0x1e2 0x1 0x1e3 0x1
|
||||||
|
0x1e4 0x1 0x1e5 0x1 0x1e6 0x1 0x1e7 0x1
|
||||||
|
0x1e8 0x1 0x1e9 0x1 0x1ea 0x1 0x1eb 0x1
|
||||||
|
0x1ec 0x1 0x1ed 0x1 0x1ee 0x1 0x1ef 0x1
|
||||||
|
0x1f0 0x1 0x1f1 0x1 0x1f2 0x1 0x1f3 0x1
|
||||||
|
0x1f4 0x1 0x1f5 0x1 0x1f6 0x1 0x1f7 0x1
|
||||||
|
0x1f8 0x1 0x1f9 0x1 0x1fa 0x1 0x1fb 0x1
|
||||||
|
0x1fc 0x1 0x1fd 0x1 0x1fe 0x1 0x1ff 0x1
|
||||||
|
0x200 0x1 0x201 0x1 0x202 0x1 0x203 0x1
|
||||||
|
0x204 0x1 0x205 0x1 0x206 0x1 0x207 0x1
|
||||||
|
0x208 0x1 0x209 0x1 0x20a 0x1 0x20b 0x1
|
||||||
|
0x20c 0x1 0x20d 0x1 0x20e 0x1 0x20f 0x1
|
||||||
|
0x210 0x1 0x211 0x1 0x212 0x1 0x213 0x1
|
||||||
|
0x214 0x1 0x215 0x1 0x216 0x1 0x217 0x1
|
||||||
|
0x218 0x1 0x219 0x1 0x21a 0x1 0x21b 0x1
|
||||||
|
0x21c 0x1 0x21d 0x1 0x21e 0x1 0x21f 0x1
|
||||||
|
0x220 0x1 0x221 0x1 0x222 0x1 0x223 0x1
|
||||||
|
0x224 0x1 0x225 0x1 0x226 0x1 0x227 0x1
|
||||||
|
0x228 0x1 0x229 0x1 0x22a 0x1 0x22b 0x1
|
||||||
|
0x22c 0x1 0x22d 0x1 0x22e 0x1 0x22f 0x1
|
||||||
|
0x230 0x1 0x231 0x1 0x232 0x1 0x233 0x1
|
||||||
|
0x234 0x1 0x235 0x1 0x236 0x1 0x237 0x1
|
||||||
|
0x238 0x1 0x239 0x1 0x23a 0x1 0x23b 0x1
|
||||||
|
0x23c 0x1 0x23d 0x1 0x23e 0x1 0x23f 0x1
|
||||||
|
0x240 0x1 0x241 0x1 0x242 0x1 0x243 0x1
|
||||||
|
0x244 0x1 0x245 0x1 0x246 0x1 0x247 0x1
|
||||||
|
0x248 0x1 0x249 0x1 0x24a 0x1 0x24b 0x1
|
||||||
|
0x24c 0x1 0x24d 0x1 0x24e 0x1 0x24f 0x1
|
||||||
|
0x250 0x1 0x251 0x1 0x252 0x1 0x253 0x1
|
||||||
|
0x254 0x1 0x255 0x1 0x256 0x1 0x257 0x1
|
||||||
|
0x258 0x1 0x259 0x1 0x25a 0x1 0x25b 0x1
|
||||||
|
0x25c 0x1 0x25d 0x1 0x25e 0x1 0x25f 0x1
|
||||||
|
0x260 0x1 0x261 0x1 0x262 0x1 0x263 0x1
|
||||||
|
0x264 0x1 0x265 0x1 0x266 0x1 0x267 0x1
|
||||||
|
0x268 0x1 0x269 0x1 0x26a 0x1 0x26b 0x1
|
||||||
|
0x26c 0x1 0x26d 0x1 0x26e 0x1 0x26f 0x1
|
||||||
|
0x270 0x1 0x271 0x1 0x272 0x1 0x273 0x1
|
||||||
|
0x274 0x1 0x275 0x1 0x276 0x1 0x277 0x1
|
||||||
|
0x278 0x1 0x279 0x1 0x27a 0x1 0x27b 0x1
|
||||||
|
0x27c 0x1 0x27d 0x1 0x27e 0x1 0x27f 0x1>;
|
||||||
|
buf-size = <4096>;
|
||||||
|
desc-num = <1024>;
|
||||||
|
dma-coherent;
|
||||||
|
};
|
||||||
|
|
||||||
|
eth0: ethernet@0{
|
||||||
|
compatible = "hisilicon,hns-nic-v1";
|
||||||
|
ae-name = "dsaf0";
|
||||||
|
port-id = <0>;
|
||||||
|
local-mac-address = [00 00 00 01 00 58];
|
||||||
|
status = "disabled";
|
||||||
|
dma-coherent;
|
||||||
|
};
|
||||||
|
eth1: ethernet@1{
|
||||||
|
compatible = "hisilicon,hns-nic-v1";
|
||||||
|
ae-name = "dsaf0";
|
||||||
|
port-id = <1>;
|
||||||
|
status = "disabled";
|
||||||
|
dma-coherent;
|
||||||
|
};
|
||||||
|
eth2: ethernet@2{
|
||||||
|
compatible = "hisilicon,hns-nic-v1";
|
||||||
|
ae-name = "dsaf0";
|
||||||
|
port-id = <2>;
|
||||||
|
local-mac-address = [00 00 00 01 00 5a];
|
||||||
|
status = "disabled";
|
||||||
|
dma-coherent;
|
||||||
|
};
|
||||||
|
eth3: ethernet@3{
|
||||||
|
compatible = "hisilicon,hns-nic-v1";
|
||||||
|
ae-name = "dsaf0";
|
||||||
|
port-id = <3>;
|
||||||
|
local-mac-address = [00 00 00 01 00 5b];
|
||||||
|
status = "disabled";
|
||||||
|
dma-coherent;
|
||||||
|
};
|
||||||
|
eth4: ethernet@4{
|
||||||
|
compatible = "hisilicon,hns-nic-v1";
|
||||||
|
ae-name = "dsaf0";
|
||||||
|
port-id = <4>;
|
||||||
|
local-mac-address = [00 00 00 01 00 5c];
|
||||||
|
status = "disabled";
|
||||||
|
dma-coherent;
|
||||||
|
};
|
||||||
|
eth5: ethernet@5{
|
||||||
|
compatible = "hisilicon,hns-nic-v1";
|
||||||
|
ae-name = "dsaf0";
|
||||||
|
port-id = <5>;
|
||||||
|
local-mac-address = [00 00 00 01 00 5d];
|
||||||
|
status = "disabled";
|
||||||
|
dma-coherent;
|
||||||
|
};
|
||||||
|
eth6: ethernet@6{
|
||||||
|
compatible = "hisilicon,hns-nic-v1";
|
||||||
|
ae-name = "dsaf0";
|
||||||
|
port-id = <6>;
|
||||||
|
local-mac-address = [00 00 00 01 00 5e];
|
||||||
|
status = "disabled";
|
||||||
|
dma-coherent;
|
||||||
|
};
|
||||||
|
eth7: ethernet@7{
|
||||||
|
compatible = "hisilicon,hns-nic-v1";
|
||||||
|
ae-name = "dsaf0";
|
||||||
|
port-id = <7>;
|
||||||
|
local-mac-address = [00 00 00 01 00 5f];
|
||||||
|
status = "disabled";
|
||||||
|
dma-coherent;
|
||||||
|
};
|
||||||
|
};
|
@ -744,7 +744,7 @@ void bpf_int_jit_compile(struct bpf_prog *prog)
|
|||||||
|
|
||||||
set_memory_ro((unsigned long)header, header->pages);
|
set_memory_ro((unsigned long)header, header->pages);
|
||||||
prog->bpf_func = (void *)ctx.image;
|
prog->bpf_func = (void *)ctx.image;
|
||||||
prog->jited = true;
|
prog->jited = 1;
|
||||||
out:
|
out:
|
||||||
kfree(ctx.offset);
|
kfree(ctx.offset);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
int psc_present;
|
int psc_present;
|
||||||
volatile __u8 *psc;
|
volatile __u8 *psc;
|
||||||
|
EXPORT_SYMBOL_GPL(psc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Debugging dump, used in various places to see what's going on.
|
* Debugging dump, used in various places to see what's going on.
|
||||||
|
@ -4,6 +4,7 @@ config BCM47XX_SSB
|
|||||||
bool "SSB Support for Broadcom BCM47XX"
|
bool "SSB Support for Broadcom BCM47XX"
|
||||||
select SYS_HAS_CPU_BMIPS32_3300
|
select SYS_HAS_CPU_BMIPS32_3300
|
||||||
select SSB
|
select SSB
|
||||||
|
select SSB_HOST_SOC
|
||||||
select SSB_DRIVER_MIPS
|
select SSB_DRIVER_MIPS
|
||||||
select SSB_DRIVER_EXTIF
|
select SSB_DRIVER_EXTIF
|
||||||
select SSB_EMBEDDED
|
select SSB_EMBEDDED
|
||||||
|
@ -1251,7 +1251,7 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||||||
bpf_jit_dump(fp->len, alloc_size, 2, ctx.target);
|
bpf_jit_dump(fp->len, alloc_size, 2, ctx.target);
|
||||||
|
|
||||||
fp->bpf_func = (void *)ctx.target;
|
fp->bpf_func = (void *)ctx.target;
|
||||||
fp->jited = true;
|
fp->jited = 1;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
kfree(ctx.offsets);
|
kfree(ctx.offsets);
|
||||||
|
@ -224,10 +224,12 @@
|
|||||||
|
|
||||||
/include/ "pq3-etsec2-0.dtsi"
|
/include/ "pq3-etsec2-0.dtsi"
|
||||||
enet0: enet0_grp2: ethernet@b0000 {
|
enet0: enet0_grp2: ethernet@b0000 {
|
||||||
|
fsl,wake-on-filer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/include/ "pq3-etsec2-1.dtsi"
|
/include/ "pq3-etsec2-1.dtsi"
|
||||||
enet1: enet1_grp2: ethernet@b1000 {
|
enet1: enet1_grp2: ethernet@b1000 {
|
||||||
|
fsl,wake-on-filer;
|
||||||
};
|
};
|
||||||
|
|
||||||
global-utilities@e0000 {
|
global-utilities@e0000 {
|
||||||
|
@ -679,7 +679,7 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||||||
((u64 *)image)[1] = local_paca->kernel_toc;
|
((u64 *)image)[1] = local_paca->kernel_toc;
|
||||||
#endif
|
#endif
|
||||||
fp->bpf_func = (void *)image;
|
fp->bpf_func = (void *)image;
|
||||||
fp->jited = true;
|
fp->jited = 1;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
kfree(addrs);
|
kfree(addrs);
|
||||||
|
@ -1310,7 +1310,7 @@ void bpf_int_jit_compile(struct bpf_prog *fp)
|
|||||||
if (jit.prg_buf) {
|
if (jit.prg_buf) {
|
||||||
set_memory_ro((unsigned long)header, header->pages);
|
set_memory_ro((unsigned long)header, header->pages);
|
||||||
fp->bpf_func = (void *) jit.prg_buf;
|
fp->bpf_func = (void *) jit.prg_buf;
|
||||||
fp->jited = true;
|
fp->jited = 1;
|
||||||
}
|
}
|
||||||
free_addrs:
|
free_addrs:
|
||||||
kfree(jit.addrs);
|
kfree(jit.addrs);
|
||||||
|
@ -812,7 +812,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf];
|
|||||||
if (image) {
|
if (image) {
|
||||||
bpf_flush_icache(image, image + proglen);
|
bpf_flush_icache(image, image + proglen);
|
||||||
fp->bpf_func = (void *)image;
|
fp->bpf_func = (void *)image;
|
||||||
fp->jited = true;
|
fp->jited = 1;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
kfree(addrs);
|
kfree(addrs);
|
||||||
|
@ -1109,7 +1109,7 @@ void bpf_int_jit_compile(struct bpf_prog *prog)
|
|||||||
bpf_flush_icache(header, image + proglen);
|
bpf_flush_icache(header, image + proglen);
|
||||||
set_memory_ro((unsigned long)header, header->pages);
|
set_memory_ro((unsigned long)header, header->pages);
|
||||||
prog->bpf_func = (void *)image;
|
prog->bpf_func = (void *)image;
|
||||||
prog->jited = true;
|
prog->jited = 1;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
kfree(addrs);
|
kfree(addrs);
|
||||||
|
@ -112,7 +112,8 @@ static void ia_enque_head_rtn_q (IARTN_Q *que, IARTN_Q * data)
|
|||||||
|
|
||||||
static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) {
|
static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) {
|
||||||
IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
|
IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
|
||||||
if (!entry) return -1;
|
if (!entry)
|
||||||
|
return -ENOMEM;
|
||||||
entry->data = data;
|
entry->data = data;
|
||||||
entry->next = NULL;
|
entry->next = NULL;
|
||||||
if (que->next == NULL)
|
if (que->next == NULL)
|
||||||
@ -1175,7 +1176,7 @@ static int rx_pkt(struct atm_dev *dev)
|
|||||||
if (!(skb = atm_alloc_charge(vcc, len, GFP_ATOMIC))) {
|
if (!(skb = atm_alloc_charge(vcc, len, GFP_ATOMIC))) {
|
||||||
if (vcc->vci < 32)
|
if (vcc->vci < 32)
|
||||||
printk("Drop control packets\n");
|
printk("Drop control packets\n");
|
||||||
goto out_free_desc;
|
goto out_free_desc;
|
||||||
}
|
}
|
||||||
skb_put(skb,len);
|
skb_put(skb,len);
|
||||||
// pwang_test
|
// pwang_test
|
||||||
|
@ -436,13 +436,8 @@ int bcma_bus_register(struct bcma_bus *bus)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dev = bcma_bus_get_host_dev(bus);
|
dev = bcma_bus_get_host_dev(bus);
|
||||||
/* TODO: remove check for IS_BUILTIN(CONFIG_BCMA) check when
|
if (dev) {
|
||||||
* of_default_bus_match_table is exported or in some other way
|
of_platform_default_populate(dev->of_node, NULL, dev);
|
||||||
* accessible. This is just a temporary workaround.
|
|
||||||
*/
|
|
||||||
if (IS_BUILTIN(CONFIG_BCMA) && dev) {
|
|
||||||
of_platform_populate(dev->of_node, of_default_bus_match_table,
|
|
||||||
NULL, dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cores providing flash access go before SPROM init */
|
/* Cores providing flash access go before SPROM init */
|
||||||
|
@ -4,6 +4,7 @@ menu "Bluetooth device drivers"
|
|||||||
|
|
||||||
config BT_INTEL
|
config BT_INTEL
|
||||||
tristate
|
tristate
|
||||||
|
select REGMAP
|
||||||
|
|
||||||
config BT_BCM
|
config BT_BCM
|
||||||
tristate
|
tristate
|
||||||
@ -182,7 +183,8 @@ config BT_HCIBCM203X
|
|||||||
|
|
||||||
config BT_HCIBPA10X
|
config BT_HCIBPA10X
|
||||||
tristate "HCI BPA10x USB driver"
|
tristate "HCI BPA10x USB driver"
|
||||||
depends on USB
|
depends on USB && BT_HCIUART
|
||||||
|
select BT_HCIUART_H4
|
||||||
help
|
help
|
||||||
Bluetooth HCI BPA10x USB driver.
|
Bluetooth HCI BPA10x USB driver.
|
||||||
This driver provides support for the Digianswer BPA 100/105 Bluetooth
|
This driver provides support for the Digianswer BPA 100/105 Bluetooth
|
||||||
@ -275,7 +277,7 @@ config BT_MRVL
|
|||||||
The core driver to support Marvell Bluetooth devices.
|
The core driver to support Marvell Bluetooth devices.
|
||||||
|
|
||||||
This driver is required if you want to support
|
This driver is required if you want to support
|
||||||
Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897.
|
Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8997.
|
||||||
|
|
||||||
Say Y here to compile Marvell Bluetooth driver
|
Say Y here to compile Marvell Bluetooth driver
|
||||||
into the kernel or say M to compile it as module.
|
into the kernel or say M to compile it as module.
|
||||||
@ -289,7 +291,7 @@ config BT_MRVL_SDIO
|
|||||||
The driver for Marvell Bluetooth chipsets with SDIO interface.
|
The driver for Marvell Bluetooth chipsets with SDIO interface.
|
||||||
|
|
||||||
This driver is required if you want to use Marvell Bluetooth
|
This driver is required if you want to use Marvell Bluetooth
|
||||||
devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897
|
devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8997
|
||||||
chipsets are supported.
|
chipsets are supported.
|
||||||
|
|
||||||
Say Y here to compile support for Marvell BT-over-SDIO driver
|
Say Y here to compile support for Marvell BT-over-SDIO driver
|
||||||
|
@ -93,6 +93,7 @@ static const struct usb_device_id ath3k_table[] = {
|
|||||||
{ USB_DEVICE(0x04CA, 0x300f) },
|
{ USB_DEVICE(0x04CA, 0x300f) },
|
||||||
{ USB_DEVICE(0x04CA, 0x3010) },
|
{ USB_DEVICE(0x04CA, 0x3010) },
|
||||||
{ USB_DEVICE(0x0930, 0x0219) },
|
{ USB_DEVICE(0x0930, 0x0219) },
|
||||||
|
{ USB_DEVICE(0x0930, 0x021c) },
|
||||||
{ USB_DEVICE(0x0930, 0x0220) },
|
{ USB_DEVICE(0x0930, 0x0220) },
|
||||||
{ USB_DEVICE(0x0930, 0x0227) },
|
{ USB_DEVICE(0x0930, 0x0227) },
|
||||||
{ USB_DEVICE(0x0b05, 0x17d0) },
|
{ USB_DEVICE(0x0b05, 0x17d0) },
|
||||||
@ -104,6 +105,7 @@ static const struct usb_device_id ath3k_table[] = {
|
|||||||
{ USB_DEVICE(0x0CF3, 0x311F) },
|
{ USB_DEVICE(0x0CF3, 0x311F) },
|
||||||
{ USB_DEVICE(0x0cf3, 0x3121) },
|
{ USB_DEVICE(0x0cf3, 0x3121) },
|
||||||
{ USB_DEVICE(0x0CF3, 0x817a) },
|
{ USB_DEVICE(0x0CF3, 0x817a) },
|
||||||
|
{ USB_DEVICE(0x0CF3, 0x817b) },
|
||||||
{ USB_DEVICE(0x0cf3, 0xe003) },
|
{ USB_DEVICE(0x0cf3, 0xe003) },
|
||||||
{ USB_DEVICE(0x0CF3, 0xE004) },
|
{ USB_DEVICE(0x0CF3, 0xE004) },
|
||||||
{ USB_DEVICE(0x0CF3, 0xE005) },
|
{ USB_DEVICE(0x0CF3, 0xE005) },
|
||||||
@ -153,6 +155,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
|
|||||||
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
||||||
|
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
|
||||||
@ -164,6 +167,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
|
|||||||
{ USB_DEVICE(0x0cf3, 0x311F), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0x311F), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
|
||||||
|
{ USB_DEVICE(0x0CF3, 0x817b), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },
|
||||||
|
@ -422,17 +422,12 @@ static int bfusb_open(struct hci_dev *hdev)
|
|||||||
|
|
||||||
BT_DBG("hdev %p bfusb %p", hdev, data);
|
BT_DBG("hdev %p bfusb %p", hdev, data);
|
||||||
|
|
||||||
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
write_lock_irqsave(&data->lock, flags);
|
write_lock_irqsave(&data->lock, flags);
|
||||||
|
|
||||||
err = bfusb_rx_submit(data, NULL);
|
err = bfusb_rx_submit(data, NULL);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
|
for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
|
||||||
bfusb_rx_submit(data, NULL);
|
bfusb_rx_submit(data, NULL);
|
||||||
} else {
|
|
||||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write_unlock_irqrestore(&data->lock, flags);
|
write_unlock_irqrestore(&data->lock, flags);
|
||||||
@ -458,9 +453,6 @@ static int bfusb_close(struct hci_dev *hdev)
|
|||||||
|
|
||||||
BT_DBG("hdev %p bfusb %p", hdev, data);
|
BT_DBG("hdev %p bfusb %p", hdev, data);
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
write_lock_irqsave(&data->lock, flags);
|
write_lock_irqsave(&data->lock, flags);
|
||||||
write_unlock_irqrestore(&data->lock, flags);
|
write_unlock_irqrestore(&data->lock, flags);
|
||||||
|
|
||||||
@ -479,9 +471,6 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
|
BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
|
||||||
|
|
||||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
switch (bt_cb(skb)->pkt_type) {
|
switch (bt_cb(skb)->pkt_type) {
|
||||||
case HCI_COMMAND_PKT:
|
case HCI_COMMAND_PKT:
|
||||||
hdev->stat.cmd_tx++;
|
hdev->stat.cmd_tx++;
|
||||||
|
@ -390,7 +390,7 @@ static void bluecard_receive(struct bluecard_info *info,
|
|||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
|
|
||||||
/* Allocate packet */
|
/* Allocate packet */
|
||||||
if (info->rx_skb == NULL) {
|
if (!info->rx_skb) {
|
||||||
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
||||||
info->rx_count = 0;
|
info->rx_count = 0;
|
||||||
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||||
@ -628,9 +628,6 @@ static int bluecard_hci_open(struct hci_dev *hdev)
|
|||||||
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
|
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
|
||||||
bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
|
bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
|
||||||
|
|
||||||
if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
|
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
|
||||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||||
|
|
||||||
@ -646,9 +643,6 @@ static int bluecard_hci_close(struct hci_dev *hdev)
|
|||||||
{
|
{
|
||||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
bluecard_hci_flush(hdev);
|
bluecard_hci_flush(hdev);
|
||||||
|
|
||||||
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
|
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
|
||||||
|
@ -35,7 +35,9 @@
|
|||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
|
|
||||||
#define VERSION "0.10"
|
#include "hci_uart.h"
|
||||||
|
|
||||||
|
#define VERSION "0.11"
|
||||||
|
|
||||||
static const struct usb_device_id bpa10x_table[] = {
|
static const struct usb_device_id bpa10x_table[] = {
|
||||||
/* Tektronix BPA 100/105 (Digianswer) */
|
/* Tektronix BPA 100/105 (Digianswer) */
|
||||||
@ -56,112 +58,6 @@ struct bpa10x_data {
|
|||||||
struct sk_buff *rx_skb[2];
|
struct sk_buff *rx_skb[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HCI_VENDOR_HDR_SIZE 5
|
|
||||||
|
|
||||||
struct hci_vendor_hdr {
|
|
||||||
__u8 type;
|
|
||||||
__le16 snum;
|
|
||||||
__le16 dlen;
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
|
|
||||||
{
|
|
||||||
struct bpa10x_data *data = hci_get_drvdata(hdev);
|
|
||||||
|
|
||||||
BT_DBG("%s queue %d buffer %p count %d", hdev->name,
|
|
||||||
queue, buf, count);
|
|
||||||
|
|
||||||
if (queue < 0 || queue > 1)
|
|
||||||
return -EILSEQ;
|
|
||||||
|
|
||||||
hdev->stat.byte_rx += count;
|
|
||||||
|
|
||||||
while (count) {
|
|
||||||
struct sk_buff *skb = data->rx_skb[queue];
|
|
||||||
struct { __u8 type; int expect; } *scb;
|
|
||||||
int type, len = 0;
|
|
||||||
|
|
||||||
if (!skb) {
|
|
||||||
/* Start of the frame */
|
|
||||||
|
|
||||||
type = *((__u8 *) buf);
|
|
||||||
count--; buf++;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case HCI_EVENT_PKT:
|
|
||||||
if (count >= HCI_EVENT_HDR_SIZE) {
|
|
||||||
struct hci_event_hdr *h = buf;
|
|
||||||
len = HCI_EVENT_HDR_SIZE + h->plen;
|
|
||||||
} else
|
|
||||||
return -EILSEQ;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HCI_ACLDATA_PKT:
|
|
||||||
if (count >= HCI_ACL_HDR_SIZE) {
|
|
||||||
struct hci_acl_hdr *h = buf;
|
|
||||||
len = HCI_ACL_HDR_SIZE +
|
|
||||||
__le16_to_cpu(h->dlen);
|
|
||||||
} else
|
|
||||||
return -EILSEQ;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HCI_SCODATA_PKT:
|
|
||||||
if (count >= HCI_SCO_HDR_SIZE) {
|
|
||||||
struct hci_sco_hdr *h = buf;
|
|
||||||
len = HCI_SCO_HDR_SIZE + h->dlen;
|
|
||||||
} else
|
|
||||||
return -EILSEQ;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HCI_VENDOR_PKT:
|
|
||||||
if (count >= HCI_VENDOR_HDR_SIZE) {
|
|
||||||
struct hci_vendor_hdr *h = buf;
|
|
||||||
len = HCI_VENDOR_HDR_SIZE +
|
|
||||||
__le16_to_cpu(h->dlen);
|
|
||||||
} else
|
|
||||||
return -EILSEQ;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
skb = bt_skb_alloc(len, GFP_ATOMIC);
|
|
||||||
if (!skb) {
|
|
||||||
BT_ERR("%s no memory for packet", hdev->name);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->rx_skb[queue] = skb;
|
|
||||||
|
|
||||||
scb = (void *) skb->cb;
|
|
||||||
scb->type = type;
|
|
||||||
scb->expect = len;
|
|
||||||
} else {
|
|
||||||
/* Continuation */
|
|
||||||
|
|
||||||
scb = (void *) skb->cb;
|
|
||||||
len = scb->expect;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = min(len, count);
|
|
||||||
|
|
||||||
memcpy(skb_put(skb, len), buf, len);
|
|
||||||
|
|
||||||
scb->expect -= len;
|
|
||||||
|
|
||||||
if (scb->expect == 0) {
|
|
||||||
/* Complete frame */
|
|
||||||
|
|
||||||
data->rx_skb[queue] = NULL;
|
|
||||||
|
|
||||||
bt_cb(skb)->pkt_type = scb->type;
|
|
||||||
hci_recv_frame(hdev, skb);
|
|
||||||
}
|
|
||||||
|
|
||||||
count -= len; buf += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bpa10x_tx_complete(struct urb *urb)
|
static void bpa10x_tx_complete(struct urb *urb)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb = urb->context;
|
struct sk_buff *skb = urb->context;
|
||||||
@ -184,6 +80,22 @@ done:
|
|||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define HCI_VENDOR_HDR_SIZE 5
|
||||||
|
|
||||||
|
#define HCI_RECV_VENDOR \
|
||||||
|
.type = HCI_VENDOR_PKT, \
|
||||||
|
.hlen = HCI_VENDOR_HDR_SIZE, \
|
||||||
|
.loff = 3, \
|
||||||
|
.lsize = 2, \
|
||||||
|
.maxlen = HCI_MAX_FRAME_SIZE
|
||||||
|
|
||||||
|
static const struct h4_recv_pkt bpa10x_recv_pkts[] = {
|
||||||
|
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||||
|
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||||
|
{ H4_RECV_EVENT, .recv = hci_recv_frame },
|
||||||
|
{ HCI_RECV_VENDOR, .recv = hci_recv_diag },
|
||||||
|
};
|
||||||
|
|
||||||
static void bpa10x_rx_complete(struct urb *urb)
|
static void bpa10x_rx_complete(struct urb *urb)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = urb->context;
|
struct hci_dev *hdev = urb->context;
|
||||||
@ -197,11 +109,17 @@ static void bpa10x_rx_complete(struct urb *urb)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (urb->status == 0) {
|
if (urb->status == 0) {
|
||||||
if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe),
|
bool idx = usb_pipebulk(urb->pipe);
|
||||||
|
|
||||||
|
data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx],
|
||||||
urb->transfer_buffer,
|
urb->transfer_buffer,
|
||||||
urb->actual_length) < 0) {
|
urb->actual_length,
|
||||||
|
bpa10x_recv_pkts,
|
||||||
|
ARRAY_SIZE(bpa10x_recv_pkts));
|
||||||
|
if (IS_ERR(data->rx_skb[idx])) {
|
||||||
BT_ERR("%s corrupted event packet", hdev->name);
|
BT_ERR("%s corrupted event packet", hdev->name);
|
||||||
hdev->stat.err_rx++;
|
hdev->stat.err_rx++;
|
||||||
|
data->rx_skb[idx] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,9 +222,6 @@ static int bpa10x_open(struct hci_dev *hdev)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err = bpa10x_submit_intr_urb(hdev);
|
err = bpa10x_submit_intr_urb(hdev);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto error;
|
goto error;
|
||||||
@ -320,8 +235,6 @@ static int bpa10x_open(struct hci_dev *hdev)
|
|||||||
error:
|
error:
|
||||||
usb_kill_anchored_urbs(&data->rx_anchor);
|
usb_kill_anchored_urbs(&data->rx_anchor);
|
||||||
|
|
||||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,9 +244,6 @@ static int bpa10x_close(struct hci_dev *hdev)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
usb_kill_anchored_urbs(&data->rx_anchor);
|
usb_kill_anchored_urbs(&data->rx_anchor);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -350,6 +260,24 @@ static int bpa10x_flush(struct hci_dev *hdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bpa10x_setup(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
const u8 req[] = { 0x07 };
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
/* Read revision string */
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb))
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
|
||||||
|
BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct bpa10x_data *data = hci_get_drvdata(hdev);
|
struct bpa10x_data *data = hci_get_drvdata(hdev);
|
||||||
@ -360,9 +288,6 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
skb->dev = (void *) hdev;
|
skb->dev = (void *) hdev;
|
||||||
|
|
||||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||||
@ -431,6 +356,25 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bpa10x_set_diag(struct hci_dev *hdev, bool enable)
|
||||||
|
{
|
||||||
|
const u8 req[] = { 0x00, enable };
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||||
|
return -ENETDOWN;
|
||||||
|
|
||||||
|
/* Enable sniffer operation */
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb))
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
struct bpa10x_data *data;
|
struct bpa10x_data *data;
|
||||||
@ -465,7 +409,9 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *
|
|||||||
hdev->open = bpa10x_open;
|
hdev->open = bpa10x_open;
|
||||||
hdev->close = bpa10x_close;
|
hdev->close = bpa10x_close;
|
||||||
hdev->flush = bpa10x_flush;
|
hdev->flush = bpa10x_flush;
|
||||||
|
hdev->setup = bpa10x_setup;
|
||||||
hdev->send = bpa10x_send_frame;
|
hdev->send = bpa10x_send_frame;
|
||||||
|
hdev->set_diag = bpa10x_set_diag;
|
||||||
|
|
||||||
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ static void bt3c_receive(struct bt3c_info *info)
|
|||||||
info->hdev->stat.byte_rx++;
|
info->hdev->stat.byte_rx++;
|
||||||
|
|
||||||
/* Allocate packet */
|
/* Allocate packet */
|
||||||
if (info->rx_skb == NULL) {
|
if (!info->rx_skb) {
|
||||||
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
||||||
info->rx_count = 0;
|
info->rx_count = 0;
|
||||||
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||||
@ -270,7 +270,6 @@ static void bt3c_receive(struct bt3c_info *info)
|
|||||||
/* Unknown packet */
|
/* Unknown packet */
|
||||||
BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
|
BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
|
||||||
info->hdev->stat.err_rx++;
|
info->hdev->stat.err_rx++;
|
||||||
clear_bit(HCI_RUNNING, &(info->hdev->flags));
|
|
||||||
|
|
||||||
kfree_skb(info->rx_skb);
|
kfree_skb(info->rx_skb);
|
||||||
info->rx_skb = NULL;
|
info->rx_skb = NULL;
|
||||||
@ -395,17 +394,12 @@ static int bt3c_hci_flush(struct hci_dev *hdev)
|
|||||||
|
|
||||||
static int bt3c_hci_open(struct hci_dev *hdev)
|
static int bt3c_hci_open(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
set_bit(HCI_RUNNING, &(hdev->flags));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int bt3c_hci_close(struct hci_dev *hdev)
|
static int bt3c_hci_close(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
bt3c_hci_flush(hdev);
|
bt3c_hci_flush(hdev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -453,7 +447,8 @@ static int bt3c_load_firmware(struct bt3c_info *info,
|
|||||||
{
|
{
|
||||||
char *ptr = (char *) firmware;
|
char *ptr = (char *) firmware;
|
||||||
char b[9];
|
char b[9];
|
||||||
unsigned int iobase, size, addr, fcs, tmp;
|
unsigned int iobase, tmp;
|
||||||
|
unsigned long size, addr, fcs;
|
||||||
int i, err = 0;
|
int i, err = 0;
|
||||||
|
|
||||||
iobase = info->p_dev->resource[0]->start;
|
iobase = info->p_dev->resource[0]->start;
|
||||||
@ -478,15 +473,18 @@ static int bt3c_load_firmware(struct bt3c_info *info,
|
|||||||
|
|
||||||
memset(b, 0, sizeof(b));
|
memset(b, 0, sizeof(b));
|
||||||
memcpy(b, ptr + 2, 2);
|
memcpy(b, ptr + 2, 2);
|
||||||
size = simple_strtoul(b, NULL, 16);
|
if (kstrtoul(b, 16, &size) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
memset(b, 0, sizeof(b));
|
memset(b, 0, sizeof(b));
|
||||||
memcpy(b, ptr + 4, 8);
|
memcpy(b, ptr + 4, 8);
|
||||||
addr = simple_strtoul(b, NULL, 16);
|
if (kstrtoul(b, 16, &addr) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
memset(b, 0, sizeof(b));
|
memset(b, 0, sizeof(b));
|
||||||
memcpy(b, ptr + (size * 2) + 2, 2);
|
memcpy(b, ptr + (size * 2) + 2, 2);
|
||||||
fcs = simple_strtoul(b, NULL, 16);
|
if (kstrtoul(b, 16, &fcs) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
memset(b, 0, sizeof(b));
|
memset(b, 0, sizeof(b));
|
||||||
for (tmp = 0, i = 0; i < size; i++) {
|
for (tmp = 0, i = 0; i < size; i++) {
|
||||||
|
@ -181,6 +181,27 @@ static int btbcm_reset(struct hci_dev *hdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct sk_buff *btbcm_read_local_name(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL,
|
||||||
|
HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
BT_ERR("%s: BCM: Reading local name failed (%ld)",
|
||||||
|
hdev->name, PTR_ERR(skb));
|
||||||
|
return skb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skb->len != sizeof(struct hci_rp_read_local_name)) {
|
||||||
|
BT_ERR("%s: BCM: Local name length mismatch", hdev->name);
|
||||||
|
kfree_skb(skb);
|
||||||
|
return ERR_PTR(-EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return skb;
|
||||||
|
}
|
||||||
|
|
||||||
static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
|
static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
@ -302,7 +323,7 @@ int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
||||||
hw_name ? : "BCM", (subver & 0x7000) >> 13,
|
hw_name ? : "BCM", (subver & 0xe000) >> 13,
|
||||||
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -332,7 +353,7 @@ int btbcm_finalize(struct hci_dev *hdev)
|
|||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
||||||
BT_INFO("%s: BCM (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
BT_INFO("%s: BCM (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
||||||
(subver & 0x7000) >> 13, (subver & 0x1f00) >> 8,
|
(subver & 0xe000) >> 13, (subver & 0x1f00) >> 8,
|
||||||
(subver & 0x00ff), rev & 0x0fff);
|
(subver & 0x00ff), rev & 0x0fff);
|
||||||
|
|
||||||
btbcm_check_bdaddr(hdev);
|
btbcm_check_bdaddr(hdev);
|
||||||
@ -393,6 +414,14 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
|
|||||||
BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
|
BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
/* Read Local Name */
|
||||||
|
skb = btbcm_read_local_name(hdev);
|
||||||
|
if (IS_ERR(skb))
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
|
||||||
|
BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
switch ((rev & 0xf000) >> 12) {
|
switch ((rev & 0xf000) >> 12) {
|
||||||
case 0:
|
case 0:
|
||||||
case 3:
|
case 3:
|
||||||
@ -432,7 +461,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
||||||
hw_name ? : "BCM", (subver & 0x7000) >> 13,
|
hw_name ? : "BCM", (subver & 0xe000) >> 13,
|
||||||
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
||||||
|
|
||||||
err = request_firmware(&fw, fw_name, &hdev->dev);
|
err = request_firmware(&fw, fw_name, &hdev->dev);
|
||||||
@ -461,9 +490,17 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
|
|||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
||||||
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
||||||
hw_name ? : "BCM", (subver & 0x7000) >> 13,
|
hw_name ? : "BCM", (subver & 0xe000) >> 13,
|
||||||
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
||||||
|
|
||||||
|
/* Read Local Name */
|
||||||
|
skb = btbcm_read_local_name(hdev);
|
||||||
|
if (IS_ERR(skb))
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
|
||||||
|
BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
btbcm_check_bdaddr(hdev);
|
btbcm_check_bdaddr(hdev);
|
||||||
|
|
||||||
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
||||||
@ -475,12 +512,34 @@ EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
|
|||||||
int btbcm_setup_apple(struct hci_dev *hdev)
|
int btbcm_setup_apple(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Reset */
|
||||||
|
err = btbcm_reset(hdev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
/* Read Verbose Config Version Info */
|
/* Read Verbose Config Version Info */
|
||||||
skb = btbcm_read_verbose_config(hdev);
|
skb = btbcm_read_verbose_config(hdev);
|
||||||
if (!IS_ERR(skb)) {
|
if (!IS_ERR(skb)) {
|
||||||
BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1],
|
BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name,
|
||||||
get_unaligned_le16(skb->data + 5));
|
skb->data[1], get_unaligned_le16(skb->data + 5));
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read USB Product Info */
|
||||||
|
skb = btbcm_read_usb_product(hdev);
|
||||||
|
if (!IS_ERR(skb)) {
|
||||||
|
BT_INFO("%s: BCM: product %4.4x:%4.4x", hdev->name,
|
||||||
|
get_unaligned_le16(skb->data + 1),
|
||||||
|
get_unaligned_le16(skb->data + 3));
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read Local Name */
|
||||||
|
skb = btbcm_read_local_name(hdev);
|
||||||
|
if (!IS_ERR(skb)) {
|
||||||
|
BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
@ -89,6 +91,75 @@ int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(btintel_set_bdaddr);
|
EXPORT_SYMBOL_GPL(btintel_set_bdaddr);
|
||||||
|
|
||||||
|
int btintel_set_diag(struct hci_dev *hdev, bool enable)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
u8 param[3];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
param[0] = 0x03;
|
||||||
|
param[1] = 0x03;
|
||||||
|
param[2] = 0x03;
|
||||||
|
} else {
|
||||||
|
param[0] = 0x00;
|
||||||
|
param[1] = 0x00;
|
||||||
|
param[2] = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc43, 3, param, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
err = PTR_ERR(skb);
|
||||||
|
if (err == -ENODATA)
|
||||||
|
goto done;
|
||||||
|
BT_ERR("%s: Changing Intel diagnostic mode failed (%d)",
|
||||||
|
hdev->name, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
done:
|
||||||
|
btintel_set_event_mask(hdev, enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(btintel_set_diag);
|
||||||
|
|
||||||
|
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
u8 param[2];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
param[0] = 0x01;
|
||||||
|
param[1] = 0x00;
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
err = PTR_ERR(skb);
|
||||||
|
BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
|
||||||
|
hdev->name, err);
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
err = btintel_set_diag(hdev, enable);
|
||||||
|
|
||||||
|
param[0] = 0x00;
|
||||||
|
param[1] = 0x00;
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
err = PTR_ERR(skb);
|
||||||
|
BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
|
||||||
|
hdev->name, err);
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(btintel_set_diag_mfg);
|
||||||
|
|
||||||
void btintel_hw_error(struct hci_dev *hdev, u8 code)
|
void btintel_hw_error(struct hci_dev *hdev, u8 code)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
@ -169,6 +240,304 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(btintel_secure_send);
|
EXPORT_SYMBOL_GPL(btintel_secure_send);
|
||||||
|
|
||||||
|
int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name)
|
||||||
|
{
|
||||||
|
const struct firmware *fw;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
const u8 *fw_ptr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = request_firmware_direct(&fw, ddc_name, &hdev->dev);
|
||||||
|
if (err < 0) {
|
||||||
|
bt_dev_err(hdev, "Failed to load Intel DDC file %s (%d)",
|
||||||
|
ddc_name, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_dev_info(hdev, "Found Intel DDC parameters: %s", ddc_name);
|
||||||
|
|
||||||
|
fw_ptr = fw->data;
|
||||||
|
|
||||||
|
/* DDC file contains one or more DDC structure which has
|
||||||
|
* Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
|
||||||
|
*/
|
||||||
|
while (fw->size > fw_ptr - fw->data) {
|
||||||
|
u8 cmd_plen = fw_ptr[0] + sizeof(u8);
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
|
||||||
|
HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
bt_dev_err(hdev, "Failed to send Intel_Write_DDC (%ld)",
|
||||||
|
PTR_ERR(skb));
|
||||||
|
release_firmware(fw);
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
fw_ptr += cmd_plen;
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
release_firmware(fw);
|
||||||
|
|
||||||
|
bt_dev_info(hdev, "Applying Intel DDC parameters completed");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(btintel_load_ddc_config);
|
||||||
|
|
||||||
|
int btintel_set_event_mask(struct hci_dev *hdev, bool debug)
|
||||||
|
{
|
||||||
|
u8 mask[8] = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
|
struct sk_buff *skb;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
mask[1] |= 0x62;
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc52, 8, mask, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
err = PTR_ERR(skb);
|
||||||
|
BT_ERR("%s: Setting Intel event mask failed (%d)",
|
||||||
|
hdev->name, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(btintel_set_event_mask);
|
||||||
|
|
||||||
|
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
u8 param[2];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
param[0] = 0x01;
|
||||||
|
param[1] = 0x00;
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
err = PTR_ERR(skb);
|
||||||
|
BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
|
||||||
|
hdev->name, err);
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
err = btintel_set_event_mask(hdev, debug);
|
||||||
|
|
||||||
|
param[0] = 0x00;
|
||||||
|
param[1] = 0x00;
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
err = PTR_ERR(skb);
|
||||||
|
BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
|
||||||
|
hdev->name, err);
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg);
|
||||||
|
|
||||||
|
/* ------- REGMAP IBT SUPPORT ------- */
|
||||||
|
|
||||||
|
#define IBT_REG_MODE_8BIT 0x00
|
||||||
|
#define IBT_REG_MODE_16BIT 0x01
|
||||||
|
#define IBT_REG_MODE_32BIT 0x02
|
||||||
|
|
||||||
|
struct regmap_ibt_context {
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
__u16 op_write;
|
||||||
|
__u16 op_read;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ibt_cp_reg_access {
|
||||||
|
__le32 addr;
|
||||||
|
__u8 mode;
|
||||||
|
__u8 len;
|
||||||
|
__u8 data[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct ibt_rp_reg_access {
|
||||||
|
__u8 status;
|
||||||
|
__le32 addr;
|
||||||
|
__u8 data[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
static int regmap_ibt_read(void *context, const void *addr, size_t reg_size,
|
||||||
|
void *val, size_t val_size)
|
||||||
|
{
|
||||||
|
struct regmap_ibt_context *ctx = context;
|
||||||
|
struct ibt_cp_reg_access cp;
|
||||||
|
struct ibt_rp_reg_access *rp;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (reg_size != sizeof(__le32))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch (val_size) {
|
||||||
|
case 1:
|
||||||
|
cp.mode = IBT_REG_MODE_8BIT;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
cp.mode = IBT_REG_MODE_16BIT;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
cp.mode = IBT_REG_MODE_32BIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* regmap provides a little-endian formatted addr */
|
||||||
|
cp.addr = *(__le32 *)addr;
|
||||||
|
cp.len = val_size;
|
||||||
|
|
||||||
|
bt_dev_dbg(ctx->hdev, "Register (0x%x) read", le32_to_cpu(cp.addr));
|
||||||
|
|
||||||
|
skb = hci_cmd_sync(ctx->hdev, ctx->op_read, sizeof(cp), &cp,
|
||||||
|
HCI_CMD_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
err = PTR_ERR(skb);
|
||||||
|
bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error (%d)",
|
||||||
|
le32_to_cpu(cp.addr), err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skb->len != sizeof(*rp) + val_size) {
|
||||||
|
bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad len",
|
||||||
|
le32_to_cpu(cp.addr));
|
||||||
|
err = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
rp = (struct ibt_rp_reg_access *)skb->data;
|
||||||
|
|
||||||
|
if (rp->addr != cp.addr) {
|
||||||
|
bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad addr",
|
||||||
|
le32_to_cpu(rp->addr));
|
||||||
|
err = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(val, rp->data, val_size);
|
||||||
|
|
||||||
|
done:
|
||||||
|
kfree_skb(skb);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regmap_ibt_gather_write(void *context,
|
||||||
|
const void *addr, size_t reg_size,
|
||||||
|
const void *val, size_t val_size)
|
||||||
|
{
|
||||||
|
struct regmap_ibt_context *ctx = context;
|
||||||
|
struct ibt_cp_reg_access *cp;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
int plen = sizeof(*cp) + val_size;
|
||||||
|
u8 mode;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (reg_size != sizeof(__le32))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch (val_size) {
|
||||||
|
case 1:
|
||||||
|
mode = IBT_REG_MODE_8BIT;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mode = IBT_REG_MODE_16BIT;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
mode = IBT_REG_MODE_32BIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = kmalloc(plen, GFP_KERNEL);
|
||||||
|
if (!cp)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* regmap provides a little-endian formatted addr/value */
|
||||||
|
cp->addr = *(__le32 *)addr;
|
||||||
|
cp->mode = mode;
|
||||||
|
cp->len = val_size;
|
||||||
|
memcpy(&cp->data, val, val_size);
|
||||||
|
|
||||||
|
bt_dev_dbg(ctx->hdev, "Register (0x%x) write", le32_to_cpu(cp->addr));
|
||||||
|
|
||||||
|
skb = hci_cmd_sync(ctx->hdev, ctx->op_write, plen, cp, HCI_CMD_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
err = PTR_ERR(skb);
|
||||||
|
bt_dev_err(ctx->hdev, "regmap: Register (0x%x) write error (%d)",
|
||||||
|
le32_to_cpu(cp->addr), err);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
done:
|
||||||
|
kfree(cp);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regmap_ibt_write(void *context, const void *data, size_t count)
|
||||||
|
{
|
||||||
|
/* data contains register+value, since we only support 32bit addr,
|
||||||
|
* minimum data size is 4 bytes.
|
||||||
|
*/
|
||||||
|
if (WARN_ONCE(count < 4, "Invalid register access"))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return regmap_ibt_gather_write(context, data, 4, data + 4, count - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void regmap_ibt_free_context(void *context)
|
||||||
|
{
|
||||||
|
kfree(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct regmap_bus regmap_ibt = {
|
||||||
|
.read = regmap_ibt_read,
|
||||||
|
.write = regmap_ibt_write,
|
||||||
|
.gather_write = regmap_ibt_gather_write,
|
||||||
|
.free_context = regmap_ibt_free_context,
|
||||||
|
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||||
|
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Config is the same for all register regions */
|
||||||
|
static const struct regmap_config regmap_ibt_cfg = {
|
||||||
|
.name = "btintel_regmap",
|
||||||
|
.reg_bits = 32,
|
||||||
|
.val_bits = 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
|
||||||
|
u16 opcode_write)
|
||||||
|
{
|
||||||
|
struct regmap_ibt_context *ctx;
|
||||||
|
|
||||||
|
bt_dev_info(hdev, "regmap: Init R%x-W%x region", opcode_read,
|
||||||
|
opcode_write);
|
||||||
|
|
||||||
|
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||||
|
if (!ctx)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
ctx->op_read = opcode_read;
|
||||||
|
ctx->op_write = opcode_write;
|
||||||
|
ctx->hdev = hdev;
|
||||||
|
|
||||||
|
return regmap_init(&hdev->dev, ®map_ibt, ctx, ®map_ibt_cfg);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(btintel_regmap_init);
|
||||||
|
|
||||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||||
MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
|
MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
|
||||||
MODULE_VERSION(VERSION);
|
MODULE_VERSION(VERSION);
|
||||||
|
@ -73,11 +73,19 @@ struct intel_secure_send_result {
|
|||||||
|
|
||||||
int btintel_check_bdaddr(struct hci_dev *hdev);
|
int btintel_check_bdaddr(struct hci_dev *hdev);
|
||||||
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||||
|
int btintel_set_diag(struct hci_dev *hdev, bool enable);
|
||||||
|
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
|
||||||
void btintel_hw_error(struct hci_dev *hdev, u8 code);
|
void btintel_hw_error(struct hci_dev *hdev, u8 code);
|
||||||
|
|
||||||
void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver);
|
void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver);
|
||||||
int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
|
int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
|
||||||
const void *param);
|
const void *param);
|
||||||
|
int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
|
||||||
|
int btintel_set_event_mask(struct hci_dev *hdev, bool debug);
|
||||||
|
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug);
|
||||||
|
|
||||||
|
struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
|
||||||
|
u16 opcode_write);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
@ -91,11 +99,22 @@ static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdadd
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int btintel_set_diag(struct hci_dev *hdev, bool enable)
|
||||||
|
{
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
|
||||||
|
{
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
|
static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
|
static inline void btintel_version_info(struct hci_dev *hdev,
|
||||||
|
struct intel_version *ver)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,4 +124,26 @@ static inline int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type,
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int btintel_load_ddc_config(struct hci_dev *hdev,
|
||||||
|
const char *ddc_name)
|
||||||
|
{
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int btintel_set_event_mask(struct hci_dev *hdev, bool debug)
|
||||||
|
{
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
|
||||||
|
{
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
|
||||||
|
u16 opcode_read,
|
||||||
|
u16 opcode_write)
|
||||||
|
{
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -184,7 +184,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
|
skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
|
||||||
if (skb == NULL) {
|
if (!skb) {
|
||||||
BT_ERR("No free skb");
|
BT_ERR("No free skb");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
@ -377,20 +377,6 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skb_headroom(skb) < BTM_HEADER_LEN) {
|
|
||||||
struct sk_buff *tmp = skb;
|
|
||||||
|
|
||||||
skb = skb_realloc_headroom(skb, BTM_HEADER_LEN);
|
|
||||||
if (!skb) {
|
|
||||||
BT_ERR("Tx Error: realloc_headroom failed %d",
|
|
||||||
BTM_HEADER_LEN);
|
|
||||||
skb = tmp;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree_skb(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
skb_push(skb, BTM_HEADER_LEN);
|
skb_push(skb, BTM_HEADER_LEN);
|
||||||
|
|
||||||
/* header type: byte[3]
|
/* header type: byte[3]
|
||||||
@ -450,13 +436,6 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
|
BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
|
||||||
|
|
||||||
if (!test_bit(HCI_RUNNING, &hdev->flags)) {
|
|
||||||
BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
|
|
||||||
print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
|
|
||||||
skb->data, skb->len);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (bt_cb(skb)->pkt_type) {
|
switch (bt_cb(skb)->pkt_type) {
|
||||||
case HCI_COMMAND_PKT:
|
case HCI_COMMAND_PKT:
|
||||||
hdev->stat.cmd_tx++;
|
hdev->stat.cmd_tx++;
|
||||||
@ -491,9 +470,6 @@ static int btmrvl_close(struct hci_dev *hdev)
|
|||||||
{
|
{
|
||||||
struct btmrvl_private *priv = hci_get_drvdata(hdev);
|
struct btmrvl_private *priv = hci_get_drvdata(hdev);
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
skb_queue_purge(&priv->adapter->tx_queue);
|
skb_queue_purge(&priv->adapter->tx_queue);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -501,8 +477,6 @@ static int btmrvl_close(struct hci_dev *hdev)
|
|||||||
|
|
||||||
static int btmrvl_open(struct hci_dev *hdev)
|
static int btmrvl_open(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
set_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,14 +516,17 @@ static int btmrvl_check_device_tree(struct btmrvl_private *priv)
|
|||||||
ret = of_property_read_u8_array(dt_node, "btmrvl,cal-data",
|
ret = of_property_read_u8_array(dt_node, "btmrvl,cal-data",
|
||||||
cal_data + BT_CAL_HDR_LEN,
|
cal_data + BT_CAL_HDR_LEN,
|
||||||
BT_CAL_DATA_SIZE);
|
BT_CAL_DATA_SIZE);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
of_node_put(dt_node);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
BT_DBG("Use cal data from device tree");
|
BT_DBG("Use cal data from device tree");
|
||||||
ret = btmrvl_download_cal_data(priv, cal_data,
|
ret = btmrvl_download_cal_data(priv, cal_data,
|
||||||
BT_CAL_DATA_SIZE);
|
BT_CAL_DATA_SIZE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
BT_ERR("Fail to download calibrate data");
|
BT_ERR("Fail to download calibrate data");
|
||||||
|
of_node_put(dt_node);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,29 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
|
|||||||
.fw_dump_end = 0xea,
|
.fw_dump_end = 0xea,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct btmrvl_sdio_card_reg btmrvl_reg_8997 = {
|
||||||
|
.cfg = 0x00,
|
||||||
|
.host_int_mask = 0x08,
|
||||||
|
.host_intstatus = 0x0c,
|
||||||
|
.card_status = 0x5c,
|
||||||
|
.sq_read_base_addr_a0 = 0xf8,
|
||||||
|
.sq_read_base_addr_a1 = 0xf9,
|
||||||
|
.card_revision = 0xc8,
|
||||||
|
.card_fw_status0 = 0xe8,
|
||||||
|
.card_fw_status1 = 0xe9,
|
||||||
|
.card_rx_len = 0xea,
|
||||||
|
.card_rx_unit = 0xeb,
|
||||||
|
.io_port_0 = 0xe4,
|
||||||
|
.io_port_1 = 0xe5,
|
||||||
|
.io_port_2 = 0xe6,
|
||||||
|
.int_read_to_clear = true,
|
||||||
|
.host_int_rsr = 0x04,
|
||||||
|
.card_misc_cfg = 0xD8,
|
||||||
|
.fw_dump_ctrl = 0xf0,
|
||||||
|
.fw_dump_start = 0xf1,
|
||||||
|
.fw_dump_end = 0xf8,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
|
static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
|
||||||
.helper = "mrvl/sd8688_helper.bin",
|
.helper = "mrvl/sd8688_helper.bin",
|
||||||
.firmware = "mrvl/sd8688.bin",
|
.firmware = "mrvl/sd8688.bin",
|
||||||
@ -191,25 +214,37 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
|
|||||||
.supports_fw_dump = true,
|
.supports_fw_dump = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = {
|
||||||
|
.helper = NULL,
|
||||||
|
.firmware = "mrvl/sd8997_uapsta.bin",
|
||||||
|
.reg = &btmrvl_reg_8997,
|
||||||
|
.support_pscan_win_report = true,
|
||||||
|
.sd_blksz_fw_dl = 256,
|
||||||
|
.supports_fw_dump = true,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct sdio_device_id btmrvl_sdio_ids[] = {
|
static const struct sdio_device_id btmrvl_sdio_ids[] = {
|
||||||
/* Marvell SD8688 Bluetooth device */
|
/* Marvell SD8688 Bluetooth device */
|
||||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
|
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
|
||||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8688 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8688 },
|
||||||
/* Marvell SD8787 Bluetooth device */
|
/* Marvell SD8787 Bluetooth device */
|
||||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
|
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
|
||||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
|
||||||
/* Marvell SD8787 Bluetooth AMP device */
|
/* Marvell SD8787 Bluetooth AMP device */
|
||||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B),
|
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B),
|
||||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
|
||||||
/* Marvell SD8797 Bluetooth device */
|
/* Marvell SD8797 Bluetooth device */
|
||||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
|
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
|
||||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8797 },
|
||||||
/* Marvell SD8887 Bluetooth device */
|
/* Marvell SD8887 Bluetooth device */
|
||||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
|
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
|
||||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
|
||||||
/* Marvell SD8897 Bluetooth device */
|
/* Marvell SD8897 Bluetooth device */
|
||||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
|
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
|
||||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8897 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8897 },
|
||||||
|
/* Marvell SD8997 Bluetooth device */
|
||||||
|
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142),
|
||||||
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8997 },
|
||||||
|
|
||||||
{ } /* Terminating entry */
|
{ } /* Terminating entry */
|
||||||
};
|
};
|
||||||
@ -619,7 +654,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
|||||||
|
|
||||||
/* Allocate buffer */
|
/* Allocate buffer */
|
||||||
skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
|
skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
|
||||||
if (skb == NULL) {
|
if (!skb) {
|
||||||
BT_ERR("No free skb");
|
BT_ERR("No free skb");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto exit;
|
goto exit;
|
||||||
@ -1278,6 +1313,12 @@ static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv)
|
|||||||
|
|
||||||
if (memory_size == 0) {
|
if (memory_size == 0) {
|
||||||
BT_INFO("Firmware dump finished!");
|
BT_INFO("Firmware dump finished!");
|
||||||
|
sdio_writeb(card->func, FW_DUMP_READ_DONE,
|
||||||
|
card->reg->fw_dump_ctrl, &ret);
|
||||||
|
if (ret) {
|
||||||
|
BT_ERR("SDIO Write MEMDUMP_FINISH ERR");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1616,3 +1657,4 @@ MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
|
|||||||
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
|
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
|
||||||
MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin");
|
MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin");
|
||||||
MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
|
MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
|
||||||
|
MODULE_FIRMWARE("mrvl/sd8997_uapsta.bin");
|
||||||
|
@ -194,21 +194,15 @@ static int btsdio_open(struct hci_dev *hdev)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
sdio_claim_host(data->func);
|
sdio_claim_host(data->func);
|
||||||
|
|
||||||
err = sdio_enable_func(data->func);
|
err = sdio_enable_func(data->func);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
goto release;
|
goto release;
|
||||||
}
|
|
||||||
|
|
||||||
err = sdio_claim_irq(data->func, btsdio_interrupt);
|
err = sdio_claim_irq(data->func, btsdio_interrupt);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
sdio_disable_func(data->func);
|
sdio_disable_func(data->func);
|
||||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
goto release;
|
goto release;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,9 +223,6 @@ static int btsdio_close(struct hci_dev *hdev)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
sdio_claim_host(data->func);
|
sdio_claim_host(data->func);
|
||||||
|
|
||||||
sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
|
sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
|
||||||
@ -261,9 +252,6 @@ static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
switch (bt_cb(skb)->pkt_type) {
|
switch (bt_cb(skb)->pkt_type) {
|
||||||
case HCI_COMMAND_PKT:
|
case HCI_COMMAND_PKT:
|
||||||
hdev->stat.cmd_tx++;
|
hdev->stat.cmd_tx++;
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
#include <linux/serial.h>
|
#include <linux/serial.h>
|
||||||
#include <linux/serial_reg.h>
|
#include <linux/serial_reg.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <asm/io.h>
|
#include <linux/io.h>
|
||||||
|
|
||||||
#include <pcmcia/cistpl.h>
|
#include <pcmcia/cistpl.h>
|
||||||
#include <pcmcia/ciscode.h>
|
#include <pcmcia/ciscode.h>
|
||||||
@ -188,7 +188,7 @@ static void btuart_receive(struct btuart_info *info)
|
|||||||
info->hdev->stat.byte_rx++;
|
info->hdev->stat.byte_rx++;
|
||||||
|
|
||||||
/* Allocate packet */
|
/* Allocate packet */
|
||||||
if (info->rx_skb == NULL) {
|
if (!info->rx_skb) {
|
||||||
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
||||||
info->rx_count = 0;
|
info->rx_count = 0;
|
||||||
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||||
@ -223,7 +223,6 @@ static void btuart_receive(struct btuart_info *info)
|
|||||||
/* Unknown packet */
|
/* Unknown packet */
|
||||||
BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
|
BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
|
||||||
info->hdev->stat.err_rx++;
|
info->hdev->stat.err_rx++;
|
||||||
clear_bit(HCI_RUNNING, &(info->hdev->flags));
|
|
||||||
|
|
||||||
kfree_skb(info->rx_skb);
|
kfree_skb(info->rx_skb);
|
||||||
info->rx_skb = NULL;
|
info->rx_skb = NULL;
|
||||||
@ -409,17 +408,12 @@ static int btuart_hci_flush(struct hci_dev *hdev)
|
|||||||
|
|
||||||
static int btuart_hci_open(struct hci_dev *hdev)
|
static int btuart_hci_open(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
set_bit(HCI_RUNNING, &(hdev->flags));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int btuart_hci_close(struct hci_dev *hdev)
|
static int btuart_hci_close(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
btuart_hci_flush(hdev);
|
btuart_hci_flush(hdev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -60,6 +60,8 @@ static struct usb_driver btusb_driver;
|
|||||||
#define BTUSB_QCA_ROME 0x8000
|
#define BTUSB_QCA_ROME 0x8000
|
||||||
#define BTUSB_BCM_APPLE 0x10000
|
#define BTUSB_BCM_APPLE 0x10000
|
||||||
#define BTUSB_REALTEK 0x20000
|
#define BTUSB_REALTEK 0x20000
|
||||||
|
#define BTUSB_BCM2045 0x40000
|
||||||
|
#define BTUSB_IFNUM_2 0x80000
|
||||||
|
|
||||||
static const struct usb_device_id btusb_table[] = {
|
static const struct usb_device_id btusb_table[] = {
|
||||||
/* Generic Bluetooth USB device */
|
/* Generic Bluetooth USB device */
|
||||||
@ -73,7 +75,7 @@ static const struct usb_device_id btusb_table[] = {
|
|||||||
|
|
||||||
/* Apple-specific (Broadcom) devices */
|
/* Apple-specific (Broadcom) devices */
|
||||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01),
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01),
|
||||||
.driver_info = BTUSB_BCM_APPLE },
|
.driver_info = BTUSB_BCM_APPLE | BTUSB_IFNUM_2 },
|
||||||
|
|
||||||
/* MediaTek MT76x0E */
|
/* MediaTek MT76x0E */
|
||||||
{ USB_DEVICE(0x0e8d, 0x763f) },
|
{ USB_DEVICE(0x0e8d, 0x763f) },
|
||||||
@ -124,6 +126,9 @@ static const struct usb_device_id btusb_table[] = {
|
|||||||
/* Broadcom BCM20702B0 (Dynex/Insignia) */
|
/* Broadcom BCM20702B0 (Dynex/Insignia) */
|
||||||
{ USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
|
{ USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
|
||||||
|
|
||||||
|
/* Broadcom BCM43142A0 (Foxconn/Lenovo) */
|
||||||
|
{ USB_DEVICE(0x105b, 0xe065), .driver_info = BTUSB_BCM_PATCHRAM },
|
||||||
|
|
||||||
/* Foxconn - Hon Hai */
|
/* Foxconn - Hon Hai */
|
||||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01),
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01),
|
||||||
.driver_info = BTUSB_BCM_PATCHRAM },
|
.driver_info = BTUSB_BCM_PATCHRAM },
|
||||||
@ -164,6 +169,9 @@ static const struct usb_device_id blacklist_table[] = {
|
|||||||
/* Broadcom BCM2033 without firmware */
|
/* Broadcom BCM2033 without firmware */
|
||||||
{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
|
{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
|
||||||
|
|
||||||
|
/* Broadcom BCM2045 devices */
|
||||||
|
{ USB_DEVICE(0x0a5c, 0x2045), .driver_info = BTUSB_BCM2045 },
|
||||||
|
|
||||||
/* Atheros 3011 with sflash firmware */
|
/* Atheros 3011 with sflash firmware */
|
||||||
{ USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
|
{ USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
|
||||||
{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
|
{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
|
||||||
@ -195,6 +203,7 @@ static const struct usb_device_id blacklist_table[] = {
|
|||||||
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
||||||
|
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
|
||||||
@ -206,6 +215,7 @@ static const struct usb_device_id blacklist_table[] = {
|
|||||||
{ USB_DEVICE(0x0cf3, 0x311f), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0x311f), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
|
||||||
|
{ USB_DEVICE(0x0cf3, 0x817b), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
|
||||||
@ -341,12 +351,14 @@ static const struct usb_device_id blacklist_table[] = {
|
|||||||
#define BTUSB_FIRMWARE_FAILED 8
|
#define BTUSB_FIRMWARE_FAILED 8
|
||||||
#define BTUSB_BOOTING 9
|
#define BTUSB_BOOTING 9
|
||||||
#define BTUSB_RESET_RESUME 10
|
#define BTUSB_RESET_RESUME 10
|
||||||
|
#define BTUSB_DIAG_RUNNING 11
|
||||||
|
|
||||||
struct btusb_data {
|
struct btusb_data {
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
struct usb_device *udev;
|
struct usb_device *udev;
|
||||||
struct usb_interface *intf;
|
struct usb_interface *intf;
|
||||||
struct usb_interface *isoc;
|
struct usb_interface *isoc;
|
||||||
|
struct usb_interface *diag;
|
||||||
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
@ -361,6 +373,7 @@ struct btusb_data {
|
|||||||
struct usb_anchor intr_anchor;
|
struct usb_anchor intr_anchor;
|
||||||
struct usb_anchor bulk_anchor;
|
struct usb_anchor bulk_anchor;
|
||||||
struct usb_anchor isoc_anchor;
|
struct usb_anchor isoc_anchor;
|
||||||
|
struct usb_anchor diag_anchor;
|
||||||
spinlock_t rxlock;
|
spinlock_t rxlock;
|
||||||
|
|
||||||
struct sk_buff *evt_skb;
|
struct sk_buff *evt_skb;
|
||||||
@ -372,6 +385,8 @@ struct btusb_data {
|
|||||||
struct usb_endpoint_descriptor *bulk_rx_ep;
|
struct usb_endpoint_descriptor *bulk_rx_ep;
|
||||||
struct usb_endpoint_descriptor *isoc_tx_ep;
|
struct usb_endpoint_descriptor *isoc_tx_ep;
|
||||||
struct usb_endpoint_descriptor *isoc_rx_ep;
|
struct usb_endpoint_descriptor *isoc_rx_ep;
|
||||||
|
struct usb_endpoint_descriptor *diag_tx_ep;
|
||||||
|
struct usb_endpoint_descriptor *diag_rx_ep;
|
||||||
|
|
||||||
__u8 cmdreq_type;
|
__u8 cmdreq_type;
|
||||||
__u8 cmdreq;
|
__u8 cmdreq;
|
||||||
@ -869,6 +884,92 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void btusb_diag_complete(struct urb *urb)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = urb->context;
|
||||||
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
|
||||||
|
urb->actual_length);
|
||||||
|
|
||||||
|
if (urb->status == 0) {
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
skb = bt_skb_alloc(urb->actual_length, GFP_ATOMIC);
|
||||||
|
if (skb) {
|
||||||
|
memcpy(skb_put(skb, urb->actual_length),
|
||||||
|
urb->transfer_buffer, urb->actual_length);
|
||||||
|
hci_recv_diag(hdev, skb);
|
||||||
|
}
|
||||||
|
} else if (urb->status == -ENOENT) {
|
||||||
|
/* Avoid suspend failed when usb_kill_urb */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!test_bit(BTUSB_DIAG_RUNNING, &data->flags))
|
||||||
|
return;
|
||||||
|
|
||||||
|
usb_anchor_urb(urb, &data->diag_anchor);
|
||||||
|
usb_mark_last_busy(data->udev);
|
||||||
|
|
||||||
|
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
|
if (err < 0) {
|
||||||
|
/* -EPERM: urb is being killed;
|
||||||
|
* -ENODEV: device got disconnected */
|
||||||
|
if (err != -EPERM && err != -ENODEV)
|
||||||
|
BT_ERR("%s urb %p failed to resubmit (%d)",
|
||||||
|
hdev->name, urb, -err);
|
||||||
|
usb_unanchor_urb(urb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btusb_submit_diag_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
||||||
|
{
|
||||||
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||||
|
struct urb *urb;
|
||||||
|
unsigned char *buf;
|
||||||
|
unsigned int pipe;
|
||||||
|
int err, size = HCI_MAX_FRAME_SIZE;
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
if (!data->diag_rx_ep)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
urb = usb_alloc_urb(0, mem_flags);
|
||||||
|
if (!urb)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
buf = kmalloc(size, mem_flags);
|
||||||
|
if (!buf) {
|
||||||
|
usb_free_urb(urb);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe = usb_rcvbulkpipe(data->udev, data->diag_rx_ep->bEndpointAddress);
|
||||||
|
|
||||||
|
usb_fill_bulk_urb(urb, data->udev, pipe, buf, size,
|
||||||
|
btusb_diag_complete, hdev);
|
||||||
|
|
||||||
|
urb->transfer_flags |= URB_FREE_BUFFER;
|
||||||
|
|
||||||
|
usb_mark_last_busy(data->udev);
|
||||||
|
usb_anchor_urb(urb, &data->diag_anchor);
|
||||||
|
|
||||||
|
err = usb_submit_urb(urb, mem_flags);
|
||||||
|
if (err < 0) {
|
||||||
|
if (err != -EPERM && err != -ENODEV)
|
||||||
|
BT_ERR("%s urb %p submission failed (%d)",
|
||||||
|
hdev->name, urb, -err);
|
||||||
|
usb_unanchor_urb(urb);
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_free_urb(urb);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static void btusb_tx_complete(struct urb *urb)
|
static void btusb_tx_complete(struct urb *urb)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb = urb->context;
|
struct sk_buff *skb = urb->context;
|
||||||
@ -940,9 +1041,6 @@ static int btusb_open(struct hci_dev *hdev)
|
|||||||
|
|
||||||
data->intf->needs_remote_wakeup = 1;
|
data->intf->needs_remote_wakeup = 1;
|
||||||
|
|
||||||
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
|
if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
@ -959,13 +1057,17 @@ static int btusb_open(struct hci_dev *hdev)
|
|||||||
set_bit(BTUSB_BULK_RUNNING, &data->flags);
|
set_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||||
btusb_submit_bulk_urb(hdev, GFP_KERNEL);
|
btusb_submit_bulk_urb(hdev, GFP_KERNEL);
|
||||||
|
|
||||||
|
if (data->diag) {
|
||||||
|
if (!btusb_submit_diag_urb(hdev, GFP_KERNEL))
|
||||||
|
set_bit(BTUSB_DIAG_RUNNING, &data->flags);
|
||||||
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
usb_autopm_put_interface(data->intf);
|
usb_autopm_put_interface(data->intf);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
||||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
usb_autopm_put_interface(data->intf);
|
usb_autopm_put_interface(data->intf);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -975,6 +1077,7 @@ static void btusb_stop_traffic(struct btusb_data *data)
|
|||||||
usb_kill_anchored_urbs(&data->intr_anchor);
|
usb_kill_anchored_urbs(&data->intr_anchor);
|
||||||
usb_kill_anchored_urbs(&data->bulk_anchor);
|
usb_kill_anchored_urbs(&data->bulk_anchor);
|
||||||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||||
|
usb_kill_anchored_urbs(&data->diag_anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int btusb_close(struct hci_dev *hdev)
|
static int btusb_close(struct hci_dev *hdev)
|
||||||
@ -984,15 +1087,13 @@ static int btusb_close(struct hci_dev *hdev)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
cancel_work_sync(&data->work);
|
cancel_work_sync(&data->work);
|
||||||
cancel_work_sync(&data->waker);
|
cancel_work_sync(&data->waker);
|
||||||
|
|
||||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||||
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||||
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
||||||
|
clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
|
||||||
|
|
||||||
btusb_stop_traffic(data);
|
btusb_stop_traffic(data);
|
||||||
btusb_free_frags(data);
|
btusb_free_frags(data);
|
||||||
@ -1156,9 +1257,6 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
switch (bt_cb(skb)->pkt_type) {
|
switch (bt_cb(skb)->pkt_type) {
|
||||||
case HCI_COMMAND_PKT:
|
case HCI_COMMAND_PKT:
|
||||||
urb = alloc_ctrl_urb(hdev, skb);
|
urb = alloc_ctrl_urb(hdev, skb);
|
||||||
@ -1277,6 +1375,20 @@ static void btusb_work(struct work_struct *work)
|
|||||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||||
|
|
||||||
|
/* When isochronous alternate setting needs to be
|
||||||
|
* changed, because SCO connection has been added
|
||||||
|
* or removed, a packet fragment may be left in the
|
||||||
|
* reassembling state. This could lead to wrongly
|
||||||
|
* assembled fragments.
|
||||||
|
*
|
||||||
|
* Clear outstanding fragment when selecting a new
|
||||||
|
* alternate setting.
|
||||||
|
*/
|
||||||
|
spin_lock(&data->rxlock);
|
||||||
|
kfree_skb(data->sco_skb);
|
||||||
|
data->sco_skb = NULL;
|
||||||
|
spin_unlock(&data->rxlock);
|
||||||
|
|
||||||
if (__set_isoc_interface(hdev, new_alts) < 0)
|
if (__set_isoc_interface(hdev, new_alts) < 0)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1348,7 +1460,9 @@ static int btusb_setup_csr(struct hci_dev *hdev)
|
|||||||
|
|
||||||
rp = (struct hci_rp_read_local_version *)skb->data;
|
rp = (struct hci_rp_read_local_version *)skb->data;
|
||||||
|
|
||||||
if (le16_to_cpu(rp->manufacturer) != 10) {
|
/* Detect controllers which aren't real CSR ones. */
|
||||||
|
if (le16_to_cpu(rp->manufacturer) != 10 ||
|
||||||
|
le16_to_cpu(rp->lmp_subver) == 0x0c5c) {
|
||||||
/* Clear the reset quirk since this is not an actual
|
/* Clear the reset quirk since this is not an actual
|
||||||
* early Bluetooth 1.1 device from CSR.
|
* early Bluetooth 1.1 device from CSR.
|
||||||
*/
|
*/
|
||||||
@ -1587,8 +1701,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
|
|||||||
BT_INFO("%s: Intel device is already patched. patch num: %02x",
|
BT_INFO("%s: Intel device is already patched. patch num: %02x",
|
||||||
hdev->name, ver->fw_patch_num);
|
hdev->name, ver->fw_patch_num);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
btintel_check_bdaddr(hdev);
|
goto complete;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Opens the firmware patch file based on the firmware version read
|
/* Opens the firmware patch file based on the firmware version read
|
||||||
@ -1600,8 +1713,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
|
|||||||
fw = btusb_setup_intel_get_fw(hdev, ver);
|
fw = btusb_setup_intel_get_fw(hdev, ver);
|
||||||
if (!fw) {
|
if (!fw) {
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
btintel_check_bdaddr(hdev);
|
goto complete;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
fw_ptr = fw->data;
|
fw_ptr = fw->data;
|
||||||
|
|
||||||
@ -1674,8 +1786,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
|
|||||||
BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
|
BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
|
||||||
hdev->name);
|
hdev->name);
|
||||||
|
|
||||||
btintel_check_bdaddr(hdev);
|
goto complete;
|
||||||
return 0;
|
|
||||||
|
|
||||||
exit_mfg_disable:
|
exit_mfg_disable:
|
||||||
/* Disable the manufacturer mode without reset */
|
/* Disable the manufacturer mode without reset */
|
||||||
@ -1690,8 +1801,7 @@ exit_mfg_disable:
|
|||||||
|
|
||||||
BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
|
BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
|
||||||
|
|
||||||
btintel_check_bdaddr(hdev);
|
goto complete;
|
||||||
return 0;
|
|
||||||
|
|
||||||
exit_mfg_deactivate:
|
exit_mfg_deactivate:
|
||||||
release_firmware(fw);
|
release_firmware(fw);
|
||||||
@ -1711,6 +1821,12 @@ exit_mfg_deactivate:
|
|||||||
BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
|
BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
|
||||||
hdev->name);
|
hdev->name);
|
||||||
|
|
||||||
|
complete:
|
||||||
|
/* Set the event mask for Intel specific vendor events. This enables
|
||||||
|
* a few extra events that are useful during general operation.
|
||||||
|
*/
|
||||||
|
btintel_set_event_mask_mfg(hdev, false);
|
||||||
|
|
||||||
btintel_check_bdaddr(hdev);
|
btintel_check_bdaddr(hdev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1827,9 +1943,6 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
switch (bt_cb(skb)->pkt_type) {
|
switch (bt_cb(skb)->pkt_type) {
|
||||||
case HCI_COMMAND_PKT:
|
case HCI_COMMAND_PKT:
|
||||||
if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
|
if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
|
||||||
@ -2003,6 +2116,15 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
|||||||
BT_INFO("%s: Secure boot is %s", hdev->name,
|
BT_INFO("%s: Secure boot is %s", hdev->name,
|
||||||
params->secure_boot ? "enabled" : "disabled");
|
params->secure_boot ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
BT_INFO("%s: OTP lock is %s", hdev->name,
|
||||||
|
params->otp_lock ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
BT_INFO("%s: API lock is %s", hdev->name,
|
||||||
|
params->api_lock ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
BT_INFO("%s: Debug lock is %s", hdev->name,
|
||||||
|
params->debug_lock ? "enabled" : "disabled");
|
||||||
|
|
||||||
BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name,
|
BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name,
|
||||||
params->min_fw_build_nn, params->min_fw_build_cw,
|
params->min_fw_build_nn, params->min_fw_build_cw,
|
||||||
2000 + params->min_fw_build_yy);
|
2000 + params->min_fw_build_yy);
|
||||||
@ -2217,36 +2339,16 @@ done:
|
|||||||
* The device can work without DDC parameters, so even if it fails
|
* The device can work without DDC parameters, so even if it fails
|
||||||
* to load the file, no need to fail the setup.
|
* to load the file, no need to fail the setup.
|
||||||
*/
|
*/
|
||||||
err = request_firmware_direct(&fw, fwname, &hdev->dev);
|
btintel_load_ddc_config(hdev, fwname);
|
||||||
if (err < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
BT_INFO("%s: Found Intel DDC parameters: %s", hdev->name, fwname);
|
/* Set the event mask for Intel specific vendor events. This enables
|
||||||
|
* a few extra events that are useful during general operation. It
|
||||||
fw_ptr = fw->data;
|
* does not enable any debugging related events.
|
||||||
|
*
|
||||||
/* DDC file contains one or more DDC structure which has
|
* The device will function correctly without these events enabled
|
||||||
* Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
|
* and thus no need to fail the setup.
|
||||||
*/
|
*/
|
||||||
while (fw->size > fw_ptr - fw->data) {
|
btintel_set_event_mask(hdev, false);
|
||||||
u8 cmd_plen = fw_ptr[0] + sizeof(u8);
|
|
||||||
|
|
||||||
skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
|
|
||||||
HCI_INIT_TIMEOUT);
|
|
||||||
if (IS_ERR(skb)) {
|
|
||||||
BT_ERR("%s: Failed to send Intel_Write_DDC (%ld)",
|
|
||||||
hdev->name, PTR_ERR(skb));
|
|
||||||
release_firmware(fw);
|
|
||||||
return PTR_ERR(skb);
|
|
||||||
}
|
|
||||||
|
|
||||||
fw_ptr += cmd_plen;
|
|
||||||
kfree_skb(skb);
|
|
||||||
}
|
|
||||||
|
|
||||||
release_firmware(fw);
|
|
||||||
|
|
||||||
BT_INFO("%s: Applying Intel DDC parameters completed", hdev->name);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2573,19 +2675,115 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_BT_HCIBTUSB_BCM
|
||||||
|
static inline int __set_diag_interface(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||||
|
struct usb_interface *intf = data->diag;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!data->diag)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
data->diag_tx_ep = NULL;
|
||||||
|
data->diag_rx_ep = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
|
||||||
|
struct usb_endpoint_descriptor *ep_desc;
|
||||||
|
|
||||||
|
ep_desc = &intf->cur_altsetting->endpoint[i].desc;
|
||||||
|
|
||||||
|
if (!data->diag_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
|
||||||
|
data->diag_tx_ep = ep_desc;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data->diag_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
|
||||||
|
data->diag_rx_ep = ep_desc;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data->diag_tx_ep || !data->diag_rx_ep) {
|
||||||
|
BT_ERR("%s invalid diagnostic descriptors", hdev->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct urb *alloc_diag_urb(struct hci_dev *hdev, bool enable)
|
||||||
|
{
|
||||||
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct urb *urb;
|
||||||
|
unsigned int pipe;
|
||||||
|
|
||||||
|
if (!data->diag_tx_ep)
|
||||||
|
return ERR_PTR(-ENODEV);
|
||||||
|
|
||||||
|
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||||
|
if (!urb)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
skb = bt_skb_alloc(2, GFP_KERNEL);
|
||||||
|
if (!skb) {
|
||||||
|
usb_free_urb(urb);
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
*skb_put(skb, 1) = 0xf0;
|
||||||
|
*skb_put(skb, 1) = enable;
|
||||||
|
|
||||||
|
pipe = usb_sndbulkpipe(data->udev, data->diag_tx_ep->bEndpointAddress);
|
||||||
|
|
||||||
|
usb_fill_bulk_urb(urb, data->udev, pipe,
|
||||||
|
skb->data, skb->len, btusb_tx_complete, skb);
|
||||||
|
|
||||||
|
skb->dev = (void *)hdev;
|
||||||
|
|
||||||
|
return urb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btusb_bcm_set_diag(struct hci_dev *hdev, bool enable)
|
||||||
|
{
|
||||||
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||||
|
struct urb *urb;
|
||||||
|
|
||||||
|
if (!data->diag)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||||
|
return -ENETDOWN;
|
||||||
|
|
||||||
|
urb = alloc_diag_urb(hdev, enable);
|
||||||
|
if (IS_ERR(urb))
|
||||||
|
return PTR_ERR(urb);
|
||||||
|
|
||||||
|
return submit_or_queue_tx_urb(hdev, urb);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int btusb_probe(struct usb_interface *intf,
|
static int btusb_probe(struct usb_interface *intf,
|
||||||
const struct usb_device_id *id)
|
const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
struct usb_endpoint_descriptor *ep_desc;
|
struct usb_endpoint_descriptor *ep_desc;
|
||||||
struct btusb_data *data;
|
struct btusb_data *data;
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
|
unsigned ifnum_base;
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
BT_DBG("intf %p id %p", intf, id);
|
BT_DBG("intf %p id %p", intf, id);
|
||||||
|
|
||||||
/* interface numbers are hardcoded in the spec */
|
/* interface numbers are hardcoded in the spec */
|
||||||
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
|
if (intf->cur_altsetting->desc.bInterfaceNumber != 0) {
|
||||||
return -ENODEV;
|
if (!(id->driver_info & BTUSB_IFNUM_2))
|
||||||
|
return -ENODEV;
|
||||||
|
if (intf->cur_altsetting->desc.bInterfaceNumber != 2)
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
|
||||||
|
|
||||||
if (!id->driver_info) {
|
if (!id->driver_info) {
|
||||||
const struct usb_device_id *match;
|
const struct usb_device_id *match;
|
||||||
@ -2653,6 +2851,7 @@ static int btusb_probe(struct usb_interface *intf,
|
|||||||
init_usb_anchor(&data->intr_anchor);
|
init_usb_anchor(&data->intr_anchor);
|
||||||
init_usb_anchor(&data->bulk_anchor);
|
init_usb_anchor(&data->bulk_anchor);
|
||||||
init_usb_anchor(&data->isoc_anchor);
|
init_usb_anchor(&data->isoc_anchor);
|
||||||
|
init_usb_anchor(&data->diag_anchor);
|
||||||
spin_lock_init(&data->rxlock);
|
spin_lock_init(&data->rxlock);
|
||||||
|
|
||||||
if (id->driver_info & BTUSB_INTEL_NEW) {
|
if (id->driver_info & BTUSB_INTEL_NEW) {
|
||||||
@ -2686,33 +2885,53 @@ static int btusb_probe(struct usb_interface *intf,
|
|||||||
hdev->send = btusb_send_frame;
|
hdev->send = btusb_send_frame;
|
||||||
hdev->notify = btusb_notify;
|
hdev->notify = btusb_notify;
|
||||||
|
|
||||||
|
if (id->driver_info & BTUSB_BCM2045)
|
||||||
|
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
|
||||||
|
|
||||||
if (id->driver_info & BTUSB_BCM92035)
|
if (id->driver_info & BTUSB_BCM92035)
|
||||||
hdev->setup = btusb_setup_bcm92035;
|
hdev->setup = btusb_setup_bcm92035;
|
||||||
|
|
||||||
#ifdef CONFIG_BT_HCIBTUSB_BCM
|
#ifdef CONFIG_BT_HCIBTUSB_BCM
|
||||||
if (id->driver_info & BTUSB_BCM_PATCHRAM) {
|
if (id->driver_info & BTUSB_BCM_PATCHRAM) {
|
||||||
|
hdev->manufacturer = 15;
|
||||||
hdev->setup = btbcm_setup_patchram;
|
hdev->setup = btbcm_setup_patchram;
|
||||||
|
hdev->set_diag = btusb_bcm_set_diag;
|
||||||
hdev->set_bdaddr = btbcm_set_bdaddr;
|
hdev->set_bdaddr = btbcm_set_bdaddr;
|
||||||
|
|
||||||
|
/* Broadcom LM_DIAG Interface numbers are hardcoded */
|
||||||
|
data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id->driver_info & BTUSB_BCM_APPLE)
|
if (id->driver_info & BTUSB_BCM_APPLE) {
|
||||||
|
hdev->manufacturer = 15;
|
||||||
hdev->setup = btbcm_setup_apple;
|
hdev->setup = btbcm_setup_apple;
|
||||||
|
hdev->set_diag = btusb_bcm_set_diag;
|
||||||
|
|
||||||
|
/* Broadcom LM_DIAG Interface numbers are hardcoded */
|
||||||
|
data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (id->driver_info & BTUSB_INTEL) {
|
if (id->driver_info & BTUSB_INTEL) {
|
||||||
|
hdev->manufacturer = 2;
|
||||||
hdev->setup = btusb_setup_intel;
|
hdev->setup = btusb_setup_intel;
|
||||||
hdev->shutdown = btusb_shutdown_intel;
|
hdev->shutdown = btusb_shutdown_intel;
|
||||||
|
hdev->set_diag = btintel_set_diag_mfg;
|
||||||
hdev->set_bdaddr = btintel_set_bdaddr;
|
hdev->set_bdaddr = btintel_set_bdaddr;
|
||||||
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
||||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||||
|
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id->driver_info & BTUSB_INTEL_NEW) {
|
if (id->driver_info & BTUSB_INTEL_NEW) {
|
||||||
|
hdev->manufacturer = 2;
|
||||||
hdev->send = btusb_send_frame_intel;
|
hdev->send = btusb_send_frame_intel;
|
||||||
hdev->setup = btusb_setup_intel_new;
|
hdev->setup = btusb_setup_intel_new;
|
||||||
hdev->hw_error = btintel_hw_error;
|
hdev->hw_error = btintel_hw_error;
|
||||||
|
hdev->set_diag = btintel_set_diag;
|
||||||
hdev->set_bdaddr = btintel_set_bdaddr;
|
hdev->set_bdaddr = btintel_set_bdaddr;
|
||||||
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
||||||
|
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id->driver_info & BTUSB_MARVELL)
|
if (id->driver_info & BTUSB_MARVELL)
|
||||||
@ -2723,8 +2942,10 @@ static int btusb_probe(struct usb_interface *intf,
|
|||||||
set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
|
set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id->driver_info & BTUSB_INTEL_BOOT)
|
if (id->driver_info & BTUSB_INTEL_BOOT) {
|
||||||
|
hdev->manufacturer = 2;
|
||||||
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
||||||
|
}
|
||||||
|
|
||||||
if (id->driver_info & BTUSB_ATH3012) {
|
if (id->driver_info & BTUSB_ATH3012) {
|
||||||
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
|
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
|
||||||
@ -2753,8 +2974,8 @@ static int btusb_probe(struct usb_interface *intf,
|
|||||||
/* AMP controllers do not support SCO packets */
|
/* AMP controllers do not support SCO packets */
|
||||||
data->isoc = NULL;
|
data->isoc = NULL;
|
||||||
} else {
|
} else {
|
||||||
/* Interface numbers are hardcoded in the specification */
|
/* Interface orders are hardcoded in the specification */
|
||||||
data->isoc = usb_ifnum_to_if(data->udev, 1);
|
data->isoc = usb_ifnum_to_if(data->udev, ifnum_base + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reset)
|
if (!reset)
|
||||||
@ -2782,7 +3003,7 @@ static int btusb_probe(struct usb_interface *intf,
|
|||||||
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||||
|
|
||||||
/* Fake CSR devices with broken commands */
|
/* Fake CSR devices with broken commands */
|
||||||
if (bcdDevice <= 0x100)
|
if (bcdDevice <= 0x100 || bcdDevice == 0x134)
|
||||||
hdev->setup = btusb_setup_csr;
|
hdev->setup = btusb_setup_csr;
|
||||||
|
|
||||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||||
@ -2817,6 +3038,16 @@ static int btusb_probe(struct usb_interface *intf,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_BT_HCIBTUSB_BCM
|
||||||
|
if (data->diag) {
|
||||||
|
if (!usb_driver_claim_interface(&btusb_driver,
|
||||||
|
data->diag, data))
|
||||||
|
__set_diag_interface(hdev);
|
||||||
|
else
|
||||||
|
data->diag = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
err = hci_register_dev(hdev);
|
err = hci_register_dev(hdev);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
hci_free_dev(hdev);
|
hci_free_dev(hdev);
|
||||||
@ -2844,12 +3075,25 @@ static void btusb_disconnect(struct usb_interface *intf)
|
|||||||
if (data->isoc)
|
if (data->isoc)
|
||||||
usb_set_intfdata(data->isoc, NULL);
|
usb_set_intfdata(data->isoc, NULL);
|
||||||
|
|
||||||
|
if (data->diag)
|
||||||
|
usb_set_intfdata(data->diag, NULL);
|
||||||
|
|
||||||
hci_unregister_dev(hdev);
|
hci_unregister_dev(hdev);
|
||||||
|
|
||||||
if (intf == data->isoc)
|
if (intf == data->intf) {
|
||||||
|
if (data->isoc)
|
||||||
|
usb_driver_release_interface(&btusb_driver, data->isoc);
|
||||||
|
if (data->diag)
|
||||||
|
usb_driver_release_interface(&btusb_driver, data->diag);
|
||||||
|
} else if (intf == data->isoc) {
|
||||||
|
if (data->diag)
|
||||||
|
usb_driver_release_interface(&btusb_driver, data->diag);
|
||||||
usb_driver_release_interface(&btusb_driver, data->intf);
|
usb_driver_release_interface(&btusb_driver, data->intf);
|
||||||
else if (data->isoc)
|
} else if (intf == data->diag) {
|
||||||
usb_driver_release_interface(&btusb_driver, data->isoc);
|
usb_driver_release_interface(&btusb_driver, data->intf);
|
||||||
|
if (data->isoc)
|
||||||
|
usb_driver_release_interface(&btusb_driver, data->isoc);
|
||||||
|
}
|
||||||
|
|
||||||
hci_free_dev(hdev);
|
hci_free_dev(hdev);
|
||||||
}
|
}
|
||||||
|
@ -155,9 +155,6 @@ static int ti_st_open(struct hci_dev *hdev)
|
|||||||
|
|
||||||
BT_DBG("%s %p", hdev->name, hdev);
|
BT_DBG("%s %p", hdev->name, hdev);
|
||||||
|
|
||||||
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
/* provide contexts for callbacks from ST */
|
/* provide contexts for callbacks from ST */
|
||||||
hst = hci_get_drvdata(hdev);
|
hst = hci_get_drvdata(hdev);
|
||||||
|
|
||||||
@ -181,7 +178,6 @@ static int ti_st_open(struct hci_dev *hdev)
|
|||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (err != -EINPROGRESS) {
|
if (err != -EINPROGRESS) {
|
||||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
BT_ERR("st_register failed %d", err);
|
BT_ERR("st_register failed %d", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -195,7 +191,6 @@ static int ti_st_open(struct hci_dev *hdev)
|
|||||||
(&hst->wait_reg_completion,
|
(&hst->wait_reg_completion,
|
||||||
msecs_to_jiffies(BT_REGISTER_TIMEOUT));
|
msecs_to_jiffies(BT_REGISTER_TIMEOUT));
|
||||||
if (!timeleft) {
|
if (!timeleft) {
|
||||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
BT_ERR("Timeout(%d sec),didn't get reg "
|
BT_ERR("Timeout(%d sec),didn't get reg "
|
||||||
"completion signal from ST",
|
"completion signal from ST",
|
||||||
BT_REGISTER_TIMEOUT / 1000);
|
BT_REGISTER_TIMEOUT / 1000);
|
||||||
@ -205,7 +200,6 @@ static int ti_st_open(struct hci_dev *hdev)
|
|||||||
/* Is ST registration callback
|
/* Is ST registration callback
|
||||||
* called with ERROR status? */
|
* called with ERROR status? */
|
||||||
if (hst->reg_status != 0) {
|
if (hst->reg_status != 0) {
|
||||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
BT_ERR("ST registration completed with invalid "
|
BT_ERR("ST registration completed with invalid "
|
||||||
"status %d", hst->reg_status);
|
"status %d", hst->reg_status);
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
@ -215,7 +209,6 @@ done:
|
|||||||
hst->st_write = ti_st_proto[i].write;
|
hst->st_write = ti_st_proto[i].write;
|
||||||
if (!hst->st_write) {
|
if (!hst->st_write) {
|
||||||
BT_ERR("undefined ST write function");
|
BT_ERR("undefined ST write function");
|
||||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
|
for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
|
||||||
/* Undo registration with ST */
|
/* Undo registration with ST */
|
||||||
err = st_unregister(&ti_st_proto[i]);
|
err = st_unregister(&ti_st_proto[i]);
|
||||||
@ -236,9 +229,6 @@ static int ti_st_close(struct hci_dev *hdev)
|
|||||||
int err, i;
|
int err, i;
|
||||||
struct ti_st *hst = hci_get_drvdata(hdev);
|
struct ti_st *hst = hci_get_drvdata(hdev);
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
|
for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
|
||||||
err = st_unregister(&ti_st_proto[i]);
|
err = st_unregister(&ti_st_proto[i]);
|
||||||
if (err)
|
if (err)
|
||||||
@ -256,9 +246,6 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
struct ti_st *hst;
|
struct ti_st *hst;
|
||||||
long len;
|
long len;
|
||||||
|
|
||||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
hst = hci_get_drvdata(hdev);
|
hst = hci_get_drvdata(hdev);
|
||||||
|
|
||||||
/* Prepend skb with frame type */
|
/* Prepend skb with frame type */
|
||||||
|
@ -357,8 +357,6 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
|
|||||||
|
|
||||||
static int dtl1_hci_open(struct hci_dev *hdev)
|
static int dtl1_hci_open(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
set_bit(HCI_RUNNING, &(hdev->flags));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,9 +374,6 @@ static int dtl1_hci_flush(struct hci_dev *hdev)
|
|||||||
|
|
||||||
static int dtl1_hci_close(struct hci_dev *hdev)
|
static int dtl1_hci_close(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
dtl1_hci_flush(hdev);
|
dtl1_hci_flush(hdev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -243,6 +243,7 @@ static struct sk_buff *ath_dequeue(struct hci_uart *hu)
|
|||||||
static const struct hci_uart_proto athp = {
|
static const struct hci_uart_proto athp = {
|
||||||
.id = HCI_UART_ATH3K,
|
.id = HCI_UART_ATH3K,
|
||||||
.name = "ATH3K",
|
.name = "ATH3K",
|
||||||
|
.manufacturer = 69,
|
||||||
.open = ath_open,
|
.open = ath_open,
|
||||||
.close = ath_close,
|
.close = ath_close,
|
||||||
.flush = ath_flush,
|
.flush = ath_flush,
|
||||||
|
@ -31,6 +31,9 @@
|
|||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/gpio/consumer.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/tty.h>
|
#include <linux/tty.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
@ -38,6 +41,11 @@
|
|||||||
#include "btbcm.h"
|
#include "btbcm.h"
|
||||||
#include "hci_uart.h"
|
#include "hci_uart.h"
|
||||||
|
|
||||||
|
#define BCM_LM_DIAG_PKT 0x07
|
||||||
|
#define BCM_LM_DIAG_SIZE 63
|
||||||
|
|
||||||
|
#define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */
|
||||||
|
|
||||||
struct bcm_device {
|
struct bcm_device {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
|
||||||
@ -51,8 +59,10 @@ struct bcm_device {
|
|||||||
bool clk_enabled;
|
bool clk_enabled;
|
||||||
|
|
||||||
u32 init_speed;
|
u32 init_speed;
|
||||||
|
int irq;
|
||||||
|
u8 irq_polarity;
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM
|
||||||
struct hci_uart *hu;
|
struct hci_uart *hu;
|
||||||
bool is_suspended; /* suspend/resume flag */
|
bool is_suspended; /* suspend/resume flag */
|
||||||
#endif
|
#endif
|
||||||
@ -66,7 +76,7 @@ struct bcm_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* List of BCM BT UART devices */
|
/* List of BCM BT UART devices */
|
||||||
static DEFINE_SPINLOCK(bcm_device_lock);
|
static DEFINE_MUTEX(bcm_device_lock);
|
||||||
static LIST_HEAD(bcm_device_list);
|
static LIST_HEAD(bcm_device_list);
|
||||||
|
|
||||||
static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
||||||
@ -80,7 +90,7 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
|||||||
|
|
||||||
clock.type = BCM_UART_CLOCK_48MHZ;
|
clock.type = BCM_UART_CLOCK_48MHZ;
|
||||||
|
|
||||||
BT_DBG("%s: Set Controller clock (%d)", hdev->name, clock.type);
|
bt_dev_dbg(hdev, "Set Controller clock (%d)", clock.type);
|
||||||
|
|
||||||
/* This Broadcom specific command changes the UART's controller
|
/* This Broadcom specific command changes the UART's controller
|
||||||
* clock for baud rate > 3000000.
|
* clock for baud rate > 3000000.
|
||||||
@ -88,15 +98,15 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
|||||||
skb = __hci_cmd_sync(hdev, 0xfc45, 1, &clock, HCI_INIT_TIMEOUT);
|
skb = __hci_cmd_sync(hdev, 0xfc45, 1, &clock, HCI_INIT_TIMEOUT);
|
||||||
if (IS_ERR(skb)) {
|
if (IS_ERR(skb)) {
|
||||||
int err = PTR_ERR(skb);
|
int err = PTR_ERR(skb);
|
||||||
BT_ERR("%s: BCM: failed to write clock command (%d)",
|
bt_dev_err(hdev, "BCM: failed to write clock (%d)",
|
||||||
hdev->name, err);
|
err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("%s: Set Controller UART speed to %d bit/s", hdev->name, speed);
|
bt_dev_dbg(hdev, "Set Controller UART speed to %d bit/s", speed);
|
||||||
|
|
||||||
param.zero = cpu_to_le16(0);
|
param.zero = cpu_to_le16(0);
|
||||||
param.baud_rate = cpu_to_le32(speed);
|
param.baud_rate = cpu_to_le32(speed);
|
||||||
@ -108,8 +118,8 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
|||||||
HCI_INIT_TIMEOUT);
|
HCI_INIT_TIMEOUT);
|
||||||
if (IS_ERR(skb)) {
|
if (IS_ERR(skb)) {
|
||||||
int err = PTR_ERR(skb);
|
int err = PTR_ERR(skb);
|
||||||
BT_ERR("%s: BCM: failed to write update baudrate command (%d)",
|
bt_dev_err(hdev, "BCM: failed to write update baudrate (%d)",
|
||||||
hdev->name, err);
|
err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,12 +159,125 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static irqreturn_t bcm_host_wake(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct bcm_device *bdev = data;
|
||||||
|
|
||||||
|
bt_dev_dbg(bdev, "Host wake IRQ");
|
||||||
|
|
||||||
|
pm_runtime_get(&bdev->pdev->dev);
|
||||||
|
pm_runtime_mark_last_busy(&bdev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&bdev->pdev->dev);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_request_irq(struct bcm_data *bcm)
|
||||||
|
{
|
||||||
|
struct bcm_device *bdev = bcm->dev;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* If this is not a platform device, do not enable PM functionalities */
|
||||||
|
mutex_lock(&bcm_device_lock);
|
||||||
|
if (!bcm_device_exists(bdev)) {
|
||||||
|
err = -ENODEV;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bdev->irq > 0) {
|
||||||
|
err = devm_request_irq(&bdev->pdev->dev, bdev->irq,
|
||||||
|
bcm_host_wake, IRQF_TRIGGER_RISING,
|
||||||
|
"host_wake", bdev);
|
||||||
|
if (err)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
device_init_wakeup(&bdev->pdev->dev, true);
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(&bdev->pdev->dev,
|
||||||
|
BCM_AUTOSUSPEND_DELAY);
|
||||||
|
pm_runtime_use_autosuspend(&bdev->pdev->dev);
|
||||||
|
pm_runtime_set_active(&bdev->pdev->dev);
|
||||||
|
pm_runtime_enable(&bdev->pdev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&bcm_device_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct bcm_set_sleep_mode default_sleep_params = {
|
||||||
|
.sleep_mode = 1, /* 0=Disabled, 1=UART, 2=Reserved, 3=USB */
|
||||||
|
.idle_host = 2, /* idle threshold HOST, in 300ms */
|
||||||
|
.idle_dev = 2, /* idle threshold device, in 300ms */
|
||||||
|
.bt_wake_active = 1, /* BT_WAKE active mode: 1 = high, 0 = low */
|
||||||
|
.host_wake_active = 0, /* HOST_WAKE active mode: 1 = high, 0 = low */
|
||||||
|
.allow_host_sleep = 1, /* Allow host sleep in SCO flag */
|
||||||
|
.combine_modes = 1, /* Combine sleep and LPM flag */
|
||||||
|
.tristate_control = 0, /* Allow tri-state control of UART tx flag */
|
||||||
|
/* Irrelevant USB flags */
|
||||||
|
.usb_auto_sleep = 0,
|
||||||
|
.usb_resume_timeout = 0,
|
||||||
|
.pulsed_host_wake = 0,
|
||||||
|
.break_to_host = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
static int bcm_setup_sleep(struct hci_uart *hu)
|
||||||
|
{
|
||||||
|
struct bcm_data *bcm = hu->priv;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct bcm_set_sleep_mode sleep_params = default_sleep_params;
|
||||||
|
|
||||||
|
sleep_params.host_wake_active = !bcm->dev->irq_polarity;
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hu->hdev, 0xfc27, sizeof(sleep_params),
|
||||||
|
&sleep_params, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
int err = PTR_ERR(skb);
|
||||||
|
bt_dev_err(hu->hdev, "Sleep VSC failed (%d)", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
bt_dev_dbg(hu->hdev, "Set Sleep Parameters VSC succeeded");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int bcm_request_irq(struct bcm_data *bcm) { return 0; }
|
||||||
|
static inline int bcm_setup_sleep(struct hci_uart *hu) { return 0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int bcm_set_diag(struct hci_dev *hdev, bool enable)
|
||||||
|
{
|
||||||
|
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||||
|
struct bcm_data *bcm = hu->priv;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||||
|
return -ENETDOWN;
|
||||||
|
|
||||||
|
skb = bt_skb_alloc(3, GFP_KERNEL);
|
||||||
|
if (!skb)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*skb_put(skb, 1) = BCM_LM_DIAG_PKT;
|
||||||
|
*skb_put(skb, 1) = 0xf0;
|
||||||
|
*skb_put(skb, 1) = enable;
|
||||||
|
|
||||||
|
skb_queue_tail(&bcm->txq, skb);
|
||||||
|
hci_uart_tx_wakeup(hu);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int bcm_open(struct hci_uart *hu)
|
static int bcm_open(struct hci_uart *hu)
|
||||||
{
|
{
|
||||||
struct bcm_data *bcm;
|
struct bcm_data *bcm;
|
||||||
struct list_head *p;
|
struct list_head *p;
|
||||||
|
|
||||||
BT_DBG("hu %p", hu);
|
bt_dev_dbg(hu->hdev, "hu %p", hu);
|
||||||
|
|
||||||
bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
|
bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
|
||||||
if (!bcm)
|
if (!bcm)
|
||||||
@ -164,7 +287,7 @@ static int bcm_open(struct hci_uart *hu)
|
|||||||
|
|
||||||
hu->priv = bcm;
|
hu->priv = bcm;
|
||||||
|
|
||||||
spin_lock(&bcm_device_lock);
|
mutex_lock(&bcm_device_lock);
|
||||||
list_for_each(p, &bcm_device_list) {
|
list_for_each(p, &bcm_device_list) {
|
||||||
struct bcm_device *dev = list_entry(p, struct bcm_device, list);
|
struct bcm_device *dev = list_entry(p, struct bcm_device, list);
|
||||||
|
|
||||||
@ -175,17 +298,15 @@ static int bcm_open(struct hci_uart *hu)
|
|||||||
if (hu->tty->dev->parent == dev->pdev->dev.parent) {
|
if (hu->tty->dev->parent == dev->pdev->dev.parent) {
|
||||||
bcm->dev = dev;
|
bcm->dev = dev;
|
||||||
hu->init_speed = dev->init_speed;
|
hu->init_speed = dev->init_speed;
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM
|
||||||
dev->hu = hu;
|
dev->hu = hu;
|
||||||
#endif
|
#endif
|
||||||
|
bcm_gpio_set_power(bcm->dev, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bcm->dev)
|
mutex_unlock(&bcm_device_lock);
|
||||||
bcm_gpio_set_power(bcm->dev, true);
|
|
||||||
|
|
||||||
spin_unlock(&bcm_device_lock);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -193,18 +314,27 @@ static int bcm_open(struct hci_uart *hu)
|
|||||||
static int bcm_close(struct hci_uart *hu)
|
static int bcm_close(struct hci_uart *hu)
|
||||||
{
|
{
|
||||||
struct bcm_data *bcm = hu->priv;
|
struct bcm_data *bcm = hu->priv;
|
||||||
|
struct bcm_device *bdev = bcm->dev;
|
||||||
|
|
||||||
BT_DBG("hu %p", hu);
|
bt_dev_dbg(hu->hdev, "hu %p", hu);
|
||||||
|
|
||||||
/* Protect bcm->dev against removal of the device or driver */
|
/* Protect bcm->dev against removal of the device or driver */
|
||||||
spin_lock(&bcm_device_lock);
|
mutex_lock(&bcm_device_lock);
|
||||||
if (bcm_device_exists(bcm->dev)) {
|
if (bcm_device_exists(bdev)) {
|
||||||
bcm_gpio_set_power(bcm->dev, false);
|
bcm_gpio_set_power(bdev, false);
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM
|
||||||
bcm->dev->hu = NULL;
|
pm_runtime_disable(&bdev->pdev->dev);
|
||||||
|
pm_runtime_set_suspended(&bdev->pdev->dev);
|
||||||
|
|
||||||
|
if (device_can_wakeup(&bdev->pdev->dev)) {
|
||||||
|
devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev);
|
||||||
|
device_init_wakeup(&bdev->pdev->dev, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bdev->hu = NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
spin_unlock(&bcm_device_lock);
|
mutex_unlock(&bcm_device_lock);
|
||||||
|
|
||||||
skb_queue_purge(&bcm->txq);
|
skb_queue_purge(&bcm->txq);
|
||||||
kfree_skb(bcm->rx_skb);
|
kfree_skb(bcm->rx_skb);
|
||||||
@ -218,7 +348,7 @@ static int bcm_flush(struct hci_uart *hu)
|
|||||||
{
|
{
|
||||||
struct bcm_data *bcm = hu->priv;
|
struct bcm_data *bcm = hu->priv;
|
||||||
|
|
||||||
BT_DBG("hu %p", hu);
|
bt_dev_dbg(hu->hdev, "hu %p", hu);
|
||||||
|
|
||||||
skb_queue_purge(&bcm->txq);
|
skb_queue_purge(&bcm->txq);
|
||||||
|
|
||||||
@ -227,13 +357,15 @@ static int bcm_flush(struct hci_uart *hu)
|
|||||||
|
|
||||||
static int bcm_setup(struct hci_uart *hu)
|
static int bcm_setup(struct hci_uart *hu)
|
||||||
{
|
{
|
||||||
|
struct bcm_data *bcm = hu->priv;
|
||||||
char fw_name[64];
|
char fw_name[64];
|
||||||
const struct firmware *fw;
|
const struct firmware *fw;
|
||||||
unsigned int speed;
|
unsigned int speed;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("hu %p", hu);
|
bt_dev_dbg(hu->hdev, "hu %p", hu);
|
||||||
|
|
||||||
|
hu->hdev->set_diag = bcm_set_diag;
|
||||||
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
|
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
|
||||||
|
|
||||||
err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
|
err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
|
||||||
@ -242,13 +374,13 @@ static int bcm_setup(struct hci_uart *hu)
|
|||||||
|
|
||||||
err = request_firmware(&fw, fw_name, &hu->hdev->dev);
|
err = request_firmware(&fw, fw_name, &hu->hdev->dev);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
BT_INFO("%s: BCM: Patch %s not found", hu->hdev->name, fw_name);
|
bt_dev_info(hu->hdev, "BCM: Patch %s not found", fw_name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = btbcm_patchram(hu->hdev, fw);
|
err = btbcm_patchram(hu->hdev, fw);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_INFO("%s: BCM: Patch failed (%d)", hu->hdev->name, err);
|
bt_dev_info(hu->hdev, "BCM: Patch failed (%d)", err);
|
||||||
goto finalize;
|
goto finalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,14 +413,28 @@ finalize:
|
|||||||
release_firmware(fw);
|
release_firmware(fw);
|
||||||
|
|
||||||
err = btbcm_finalize(hu->hdev);
|
err = btbcm_finalize(hu->hdev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = bcm_request_irq(bcm);
|
||||||
|
if (!err)
|
||||||
|
err = bcm_setup_sleep(hu);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BCM_RECV_LM_DIAG \
|
||||||
|
.type = BCM_LM_DIAG_PKT, \
|
||||||
|
.hlen = BCM_LM_DIAG_SIZE, \
|
||||||
|
.loff = 0, \
|
||||||
|
.lsize = 0, \
|
||||||
|
.maxlen = BCM_LM_DIAG_SIZE
|
||||||
|
|
||||||
static const struct h4_recv_pkt bcm_recv_pkts[] = {
|
static const struct h4_recv_pkt bcm_recv_pkts[] = {
|
||||||
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||||
{ H4_RECV_EVENT, .recv = hci_recv_frame },
|
{ H4_RECV_EVENT, .recv = hci_recv_frame },
|
||||||
|
{ BCM_RECV_LM_DIAG, .recv = hci_recv_diag },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int bcm_recv(struct hci_uart *hu, const void *data, int count)
|
static int bcm_recv(struct hci_uart *hu, const void *data, int count)
|
||||||
@ -302,9 +448,18 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
|
|||||||
bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
|
bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
|
||||||
if (IS_ERR(bcm->rx_skb)) {
|
if (IS_ERR(bcm->rx_skb)) {
|
||||||
int err = PTR_ERR(bcm->rx_skb);
|
int err = PTR_ERR(bcm->rx_skb);
|
||||||
BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
|
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
|
||||||
bcm->rx_skb = NULL;
|
bcm->rx_skb = NULL;
|
||||||
return err;
|
return err;
|
||||||
|
} else if (!bcm->rx_skb) {
|
||||||
|
/* Delay auto-suspend when receiving completed packet */
|
||||||
|
mutex_lock(&bcm_device_lock);
|
||||||
|
if (bcm->dev && bcm_device_exists(bcm->dev)) {
|
||||||
|
pm_runtime_get(&bcm->dev->pdev->dev);
|
||||||
|
pm_runtime_mark_last_busy(&bcm->dev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&bcm->dev->pdev->dev);
|
||||||
|
}
|
||||||
|
mutex_unlock(&bcm_device_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
@ -314,7 +469,7 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
|||||||
{
|
{
|
||||||
struct bcm_data *bcm = hu->priv;
|
struct bcm_data *bcm = hu->priv;
|
||||||
|
|
||||||
BT_DBG("hu %p skb %p", hu, skb);
|
bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb);
|
||||||
|
|
||||||
/* Prepend skb with frame type */
|
/* Prepend skb with frame type */
|
||||||
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
|
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
|
||||||
@ -326,39 +481,105 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
|||||||
static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
|
static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
|
||||||
{
|
{
|
||||||
struct bcm_data *bcm = hu->priv;
|
struct bcm_data *bcm = hu->priv;
|
||||||
|
struct sk_buff *skb = NULL;
|
||||||
|
struct bcm_device *bdev = NULL;
|
||||||
|
|
||||||
return skb_dequeue(&bcm->txq);
|
mutex_lock(&bcm_device_lock);
|
||||||
|
|
||||||
|
if (bcm_device_exists(bcm->dev)) {
|
||||||
|
bdev = bcm->dev;
|
||||||
|
pm_runtime_get_sync(&bdev->pdev->dev);
|
||||||
|
/* Shall be resumed here */
|
||||||
|
}
|
||||||
|
|
||||||
|
skb = skb_dequeue(&bcm->txq);
|
||||||
|
|
||||||
|
if (bdev) {
|
||||||
|
pm_runtime_mark_last_busy(&bdev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&bdev->pdev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&bcm_device_lock);
|
||||||
|
|
||||||
|
return skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM
|
||||||
/* Platform suspend callback */
|
static int bcm_suspend_device(struct device *dev)
|
||||||
static int bcm_suspend(struct device *dev)
|
|
||||||
{
|
{
|
||||||
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
||||||
|
|
||||||
BT_DBG("suspend (%p): is_suspended %d", bdev, bdev->is_suspended);
|
bt_dev_dbg(bdev, "");
|
||||||
|
|
||||||
spin_lock(&bcm_device_lock);
|
if (!bdev->is_suspended && bdev->hu) {
|
||||||
|
|
||||||
if (!bdev->hu)
|
|
||||||
goto unlock;
|
|
||||||
|
|
||||||
if (!bdev->is_suspended) {
|
|
||||||
hci_uart_set_flow_control(bdev->hu, true);
|
hci_uart_set_flow_control(bdev->hu, true);
|
||||||
|
|
||||||
/* Once this callback returns, driver suspends BT via GPIO */
|
/* Once this returns, driver suspends BT via GPIO */
|
||||||
bdev->is_suspended = true;
|
bdev->is_suspended = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Suspend the device */
|
/* Suspend the device */
|
||||||
if (bdev->device_wakeup) {
|
if (bdev->device_wakeup) {
|
||||||
gpiod_set_value(bdev->device_wakeup, false);
|
gpiod_set_value(bdev->device_wakeup, false);
|
||||||
BT_DBG("suspend, delaying 15 ms");
|
bt_dev_dbg(bdev, "suspend, delaying 15 ms");
|
||||||
mdelay(15);
|
mdelay(15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_resume_device(struct device *dev)
|
||||||
|
{
|
||||||
|
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
||||||
|
|
||||||
|
bt_dev_dbg(bdev, "");
|
||||||
|
|
||||||
|
if (bdev->device_wakeup) {
|
||||||
|
gpiod_set_value(bdev->device_wakeup, true);
|
||||||
|
bt_dev_dbg(bdev, "resume, delaying 15 ms");
|
||||||
|
mdelay(15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When this executes, the device has woken up already */
|
||||||
|
if (bdev->is_suspended && bdev->hu) {
|
||||||
|
bdev->is_suspended = false;
|
||||||
|
|
||||||
|
hci_uart_set_flow_control(bdev->hu, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
/* Platform suspend callback */
|
||||||
|
static int bcm_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
||||||
|
int error;
|
||||||
|
|
||||||
|
bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended);
|
||||||
|
|
||||||
|
/* bcm_suspend can be called at any time as long as platform device is
|
||||||
|
* bound, so it should use bcm_device_lock to protect access to hci_uart
|
||||||
|
* and device_wake-up GPIO.
|
||||||
|
*/
|
||||||
|
mutex_lock(&bcm_device_lock);
|
||||||
|
|
||||||
|
if (!bdev->hu)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
if (pm_runtime_active(dev))
|
||||||
|
bcm_suspend_device(dev);
|
||||||
|
|
||||||
|
if (device_may_wakeup(&bdev->pdev->dev)) {
|
||||||
|
error = enable_irq_wake(bdev->irq);
|
||||||
|
if (!error)
|
||||||
|
bt_dev_dbg(bdev, "BCM irq: enabled");
|
||||||
|
}
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
spin_unlock(&bcm_device_lock);
|
mutex_unlock(&bcm_device_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -368,28 +589,30 @@ static int bcm_resume(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
||||||
|
|
||||||
BT_DBG("resume (%p): is_suspended %d", bdev, bdev->is_suspended);
|
bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended);
|
||||||
|
|
||||||
spin_lock(&bcm_device_lock);
|
/* bcm_resume can be called at any time as long as platform device is
|
||||||
|
* bound, so it should use bcm_device_lock to protect access to hci_uart
|
||||||
|
* and device_wake-up GPIO.
|
||||||
|
*/
|
||||||
|
mutex_lock(&bcm_device_lock);
|
||||||
|
|
||||||
if (!bdev->hu)
|
if (!bdev->hu)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
if (bdev->device_wakeup) {
|
if (device_may_wakeup(&bdev->pdev->dev)) {
|
||||||
gpiod_set_value(bdev->device_wakeup, true);
|
disable_irq_wake(bdev->irq);
|
||||||
BT_DBG("resume, delaying 15 ms");
|
bt_dev_dbg(bdev, "BCM irq: disabled");
|
||||||
mdelay(15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When this callback executes, the device has woken up already */
|
bcm_resume_device(dev);
|
||||||
if (bdev->is_suspended) {
|
|
||||||
bdev->is_suspended = false;
|
|
||||||
|
|
||||||
hci_uart_set_flow_control(bdev->hu, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
spin_unlock(&bcm_device_lock);
|
mutex_unlock(&bcm_device_lock);
|
||||||
|
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
pm_runtime_set_active(dev);
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -397,24 +620,59 @@ unlock:
|
|||||||
|
|
||||||
static const struct acpi_gpio_params device_wakeup_gpios = { 0, 0, false };
|
static const struct acpi_gpio_params device_wakeup_gpios = { 0, 0, false };
|
||||||
static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false };
|
static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false };
|
||||||
|
static const struct acpi_gpio_params host_wakeup_gpios = { 2, 0, false };
|
||||||
|
|
||||||
static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = {
|
static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = {
|
||||||
{ "device-wakeup-gpios", &device_wakeup_gpios, 1 },
|
{ "device-wakeup-gpios", &device_wakeup_gpios, 1 },
|
||||||
{ "shutdown-gpios", &shutdown_gpios, 1 },
|
{ "shutdown-gpios", &shutdown_gpios, 1 },
|
||||||
|
{ "host-wakeup-gpios", &host_wakeup_gpios, 1 },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
|
static u8 acpi_active_low = ACPI_ACTIVE_LOW;
|
||||||
|
|
||||||
|
/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
|
||||||
|
static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = {
|
||||||
|
{
|
||||||
|
.ident = "Asus T100TA",
|
||||||
|
.matches = {
|
||||||
|
DMI_EXACT_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"ASUSTeK COMPUTER INC."),
|
||||||
|
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
|
||||||
|
},
|
||||||
|
.driver_data = &acpi_active_low,
|
||||||
|
},
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
static int bcm_resource(struct acpi_resource *ares, void *data)
|
static int bcm_resource(struct acpi_resource *ares, void *data)
|
||||||
{
|
{
|
||||||
struct bcm_device *dev = data;
|
struct bcm_device *dev = data;
|
||||||
|
struct acpi_resource_extended_irq *irq;
|
||||||
|
struct acpi_resource_gpio *gpio;
|
||||||
|
struct acpi_resource_uart_serialbus *sb;
|
||||||
|
|
||||||
if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
switch (ares->type) {
|
||||||
struct acpi_resource_uart_serialbus *sb;
|
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||||||
|
irq = &ares->data.extended_irq;
|
||||||
|
dev->irq_polarity = irq->polarity;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ACPI_RESOURCE_TYPE_GPIO:
|
||||||
|
gpio = &ares->data.gpio;
|
||||||
|
if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT)
|
||||||
|
dev->irq_polarity = gpio->polarity;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ACPI_RESOURCE_TYPE_SERIAL_BUS:
|
||||||
sb = &ares->data.uart_serial_bus;
|
sb = &ares->data.uart_serial_bus;
|
||||||
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_UART)
|
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_UART)
|
||||||
dev->init_speed = sb->default_baud_rate;
|
dev->init_speed = sb->default_baud_rate;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Always tell the ACPI core to skip this resource */
|
/* Always tell the ACPI core to skip this resource */
|
||||||
@ -424,15 +682,10 @@ static int bcm_resource(struct acpi_resource *ares, void *data)
|
|||||||
static int bcm_acpi_probe(struct bcm_device *dev)
|
static int bcm_acpi_probe(struct bcm_device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = dev->pdev;
|
struct platform_device *pdev = dev->pdev;
|
||||||
const struct acpi_device_id *id;
|
|
||||||
struct acpi_device *adev;
|
|
||||||
LIST_HEAD(resources);
|
LIST_HEAD(resources);
|
||||||
|
const struct dmi_system_id *dmi_id;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
|
|
||||||
if (!id)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* Retrieve GPIO data */
|
/* Retrieve GPIO data */
|
||||||
dev->name = dev_name(&pdev->dev);
|
dev->name = dev_name(&pdev->dev);
|
||||||
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
|
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
|
||||||
@ -453,6 +706,21 @@ static int bcm_acpi_probe(struct bcm_device *dev)
|
|||||||
if (IS_ERR(dev->shutdown))
|
if (IS_ERR(dev->shutdown))
|
||||||
return PTR_ERR(dev->shutdown);
|
return PTR_ERR(dev->shutdown);
|
||||||
|
|
||||||
|
/* IRQ can be declared in ACPI table as Interrupt or GpioInt */
|
||||||
|
dev->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (dev->irq <= 0) {
|
||||||
|
struct gpio_desc *gpio;
|
||||||
|
|
||||||
|
gpio = devm_gpiod_get_optional(&pdev->dev, "host-wakeup",
|
||||||
|
GPIOD_IN);
|
||||||
|
if (IS_ERR(gpio))
|
||||||
|
return PTR_ERR(gpio);
|
||||||
|
|
||||||
|
dev->irq = gpiod_to_irq(gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "BCM irq: %d\n", dev->irq);
|
||||||
|
|
||||||
/* Make sure at-least one of the GPIO is defined and that
|
/* Make sure at-least one of the GPIO is defined and that
|
||||||
* a name is specified for this instance
|
* a name is specified for this instance
|
||||||
*/
|
*/
|
||||||
@ -462,11 +730,18 @@ static int bcm_acpi_probe(struct bcm_device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve UART ACPI info */
|
/* Retrieve UART ACPI info */
|
||||||
adev = ACPI_COMPANION(&dev->pdev->dev);
|
ret = acpi_dev_get_resources(ACPI_COMPANION(&dev->pdev->dev),
|
||||||
if (!adev)
|
&resources, bcm_resource, dev);
|
||||||
return 0;
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
acpi_dev_free_resource_list(&resources);
|
||||||
|
|
||||||
acpi_dev_get_resources(adev, &resources, bcm_resource, dev);
|
dmi_id = dmi_first_match(bcm_wrong_irq_dmi_table);
|
||||||
|
if (dmi_id) {
|
||||||
|
bt_dev_warn(dev, "%s: Overwriting IRQ polarity to active low",
|
||||||
|
dmi_id->ident);
|
||||||
|
dev->irq_polarity = *(u8 *)dmi_id->driver_data;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -480,7 +755,6 @@ static int bcm_acpi_probe(struct bcm_device *dev)
|
|||||||
static int bcm_probe(struct platform_device *pdev)
|
static int bcm_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct bcm_device *dev;
|
struct bcm_device *dev;
|
||||||
struct acpi_device_id *pdata = pdev->dev.platform_data;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||||
@ -489,24 +763,18 @@ static int bcm_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
dev->pdev = pdev;
|
dev->pdev = pdev;
|
||||||
|
|
||||||
if (ACPI_HANDLE(&pdev->dev)) {
|
ret = bcm_acpi_probe(dev);
|
||||||
ret = bcm_acpi_probe(dev);
|
if (ret)
|
||||||
if (ret)
|
return ret;
|
||||||
return ret;
|
|
||||||
} else if (pdata) {
|
|
||||||
dev->name = pdata->id;
|
|
||||||
} else {
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, dev);
|
platform_set_drvdata(pdev, dev);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "%s device registered.\n", dev->name);
|
dev_info(&pdev->dev, "%s device registered.\n", dev->name);
|
||||||
|
|
||||||
/* Place this instance on the device list */
|
/* Place this instance on the device list */
|
||||||
spin_lock(&bcm_device_lock);
|
mutex_lock(&bcm_device_lock);
|
||||||
list_add_tail(&dev->list, &bcm_device_list);
|
list_add_tail(&dev->list, &bcm_device_list);
|
||||||
spin_unlock(&bcm_device_lock);
|
mutex_unlock(&bcm_device_lock);
|
||||||
|
|
||||||
bcm_gpio_set_power(dev, false);
|
bcm_gpio_set_power(dev, false);
|
||||||
|
|
||||||
@ -517,9 +785,9 @@ static int bcm_remove(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct bcm_device *dev = platform_get_drvdata(pdev);
|
struct bcm_device *dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
spin_lock(&bcm_device_lock);
|
mutex_lock(&bcm_device_lock);
|
||||||
list_del(&dev->list);
|
list_del(&dev->list);
|
||||||
spin_unlock(&bcm_device_lock);
|
mutex_unlock(&bcm_device_lock);
|
||||||
|
|
||||||
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev));
|
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev));
|
||||||
|
|
||||||
@ -531,6 +799,7 @@ static int bcm_remove(struct platform_device *pdev)
|
|||||||
static const struct hci_uart_proto bcm_proto = {
|
static const struct hci_uart_proto bcm_proto = {
|
||||||
.id = HCI_UART_BCM,
|
.id = HCI_UART_BCM,
|
||||||
.name = "BCM",
|
.name = "BCM",
|
||||||
|
.manufacturer = 15,
|
||||||
.init_speed = 115200,
|
.init_speed = 115200,
|
||||||
.oper_speed = 4000000,
|
.oper_speed = 4000000,
|
||||||
.open = bcm_open,
|
.open = bcm_open,
|
||||||
@ -553,7 +822,10 @@ MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Platform suspend and resume callbacks */
|
/* Platform suspend and resume callbacks */
|
||||||
static SIMPLE_DEV_PM_OPS(bcm_pm_ops, bcm_suspend, bcm_resume);
|
static const struct dev_pm_ops bcm_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(bcm_suspend, bcm_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(bcm_suspend_device, bcm_resume_device, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver bcm_driver = {
|
static struct platform_driver bcm_driver = {
|
||||||
.probe = bcm_probe,
|
.probe = bcm_probe,
|
||||||
|
@ -266,3 +266,4 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
|
|||||||
|
|
||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(h4_recv_buf);
|
||||||
|
@ -128,7 +128,7 @@ static void h5_timed_event(unsigned long arg)
|
|||||||
{
|
{
|
||||||
const unsigned char sync_req[] = { 0x01, 0x7e };
|
const unsigned char sync_req[] = { 0x01, 0x7e };
|
||||||
unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
|
unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
|
||||||
struct hci_uart *hu = (struct hci_uart *) arg;
|
struct hci_uart *hu = (struct hci_uart *)arg;
|
||||||
struct h5 *h5 = hu->priv;
|
struct h5 *h5 = hu->priv;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
@ -210,7 +210,7 @@ static int h5_open(struct hci_uart *hu)
|
|||||||
|
|
||||||
init_timer(&h5->timer);
|
init_timer(&h5->timer);
|
||||||
h5->timer.function = h5_timed_event;
|
h5->timer.function = h5_timed_event;
|
||||||
h5->timer.data = (unsigned long) hu;
|
h5->timer.data = (unsigned long)hu;
|
||||||
|
|
||||||
h5->tx_win = H5_TX_WIN_MAX;
|
h5->tx_win = H5_TX_WIN_MAX;
|
||||||
|
|
||||||
@ -453,7 +453,7 @@ static int h5_rx_pkt_start(struct hci_uart *hu, unsigned char c)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
h5->rx_skb->dev = (void *) hu->hdev;
|
h5->rx_skb->dev = (void *)hu->hdev;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -696,7 +696,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
skb = skb_dequeue(&h5->unrel);
|
skb = skb_dequeue(&h5->unrel);
|
||||||
if (skb != NULL) {
|
if (skb) {
|
||||||
nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
|
nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
|
||||||
skb->data, skb->len);
|
skb->data, skb->len);
|
||||||
if (nskb) {
|
if (nskb) {
|
||||||
@ -714,7 +714,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
|
|||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
skb = skb_dequeue(&h5->rel);
|
skb = skb_dequeue(&h5->rel);
|
||||||
if (skb != NULL) {
|
if (skb) {
|
||||||
nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
|
nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
|
||||||
skb->data, skb->len);
|
skb->data, skb->len);
|
||||||
if (nskb) {
|
if (nskb) {
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/gpio/consumer.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
@ -43,19 +45,45 @@
|
|||||||
#define STATE_FIRMWARE_LOADED 2
|
#define STATE_FIRMWARE_LOADED 2
|
||||||
#define STATE_FIRMWARE_FAILED 3
|
#define STATE_FIRMWARE_FAILED 3
|
||||||
#define STATE_BOOTING 4
|
#define STATE_BOOTING 4
|
||||||
|
#define STATE_LPM_ENABLED 5
|
||||||
|
#define STATE_TX_ACTIVE 6
|
||||||
|
#define STATE_SUSPENDED 7
|
||||||
|
#define STATE_LPM_TRANSACTION 8
|
||||||
|
|
||||||
|
#define HCI_LPM_WAKE_PKT 0xf0
|
||||||
|
#define HCI_LPM_PKT 0xf1
|
||||||
|
#define HCI_LPM_MAX_SIZE 10
|
||||||
|
#define HCI_LPM_HDR_SIZE HCI_EVENT_HDR_SIZE
|
||||||
|
|
||||||
|
#define LPM_OP_TX_NOTIFY 0x00
|
||||||
|
#define LPM_OP_SUSPEND_ACK 0x02
|
||||||
|
#define LPM_OP_RESUME_ACK 0x03
|
||||||
|
|
||||||
|
#define LPM_SUSPEND_DELAY_MS 1000
|
||||||
|
|
||||||
|
struct hci_lpm_pkt {
|
||||||
|
__u8 opcode;
|
||||||
|
__u8 dlen;
|
||||||
|
__u8 data[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
struct intel_device {
|
struct intel_device {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct gpio_desc *reset;
|
struct gpio_desc *reset;
|
||||||
|
struct hci_uart *hu;
|
||||||
|
struct mutex hu_lock;
|
||||||
|
int irq;
|
||||||
};
|
};
|
||||||
|
|
||||||
static LIST_HEAD(intel_device_list);
|
static LIST_HEAD(intel_device_list);
|
||||||
static DEFINE_SPINLOCK(intel_device_list_lock);
|
static DEFINE_MUTEX(intel_device_list_lock);
|
||||||
|
|
||||||
struct intel_data {
|
struct intel_data {
|
||||||
struct sk_buff *rx_skb;
|
struct sk_buff *rx_skb;
|
||||||
struct sk_buff_head txq;
|
struct sk_buff_head txq;
|
||||||
|
struct work_struct busy_work;
|
||||||
|
struct hci_uart *hu;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -101,24 +129,185 @@ static int intel_wait_booting(struct hci_uart *hu)
|
|||||||
msecs_to_jiffies(1000));
|
msecs_to_jiffies(1000));
|
||||||
|
|
||||||
if (err == 1) {
|
if (err == 1) {
|
||||||
BT_ERR("%s: Device boot interrupted", hu->hdev->name);
|
bt_dev_err(hu->hdev, "Device boot interrupted");
|
||||||
return -EINTR;
|
return -EINTR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("%s: Device boot timeout", hu->hdev->name);
|
bt_dev_err(hu->hdev, "Device boot timeout");
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int intel_wait_lpm_transaction(struct hci_uart *hu)
|
||||||
|
{
|
||||||
|
struct intel_data *intel = hu->priv;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = wait_on_bit_timeout(&intel->flags, STATE_LPM_TRANSACTION,
|
||||||
|
TASK_INTERRUPTIBLE,
|
||||||
|
msecs_to_jiffies(1000));
|
||||||
|
|
||||||
|
if (err == 1) {
|
||||||
|
bt_dev_err(hu->hdev, "LPM transaction interrupted");
|
||||||
|
return -EINTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
bt_dev_err(hu->hdev, "LPM transaction timeout");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intel_lpm_suspend(struct hci_uart *hu)
|
||||||
|
{
|
||||||
|
static const u8 suspend[] = { 0x01, 0x01, 0x01 };
|
||||||
|
struct intel_data *intel = hu->priv;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
if (!test_bit(STATE_LPM_ENABLED, &intel->flags) ||
|
||||||
|
test_bit(STATE_SUSPENDED, &intel->flags))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (test_bit(STATE_TX_ACTIVE, &intel->flags))
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
|
bt_dev_dbg(hu->hdev, "Suspending");
|
||||||
|
|
||||||
|
skb = bt_skb_alloc(sizeof(suspend), GFP_KERNEL);
|
||||||
|
if (!skb) {
|
||||||
|
bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(skb_put(skb, sizeof(suspend)), suspend, sizeof(suspend));
|
||||||
|
bt_cb(skb)->pkt_type = HCI_LPM_PKT;
|
||||||
|
|
||||||
|
set_bit(STATE_LPM_TRANSACTION, &intel->flags);
|
||||||
|
|
||||||
|
/* LPM flow is a priority, enqueue packet at list head */
|
||||||
|
skb_queue_head(&intel->txq, skb);
|
||||||
|
hci_uart_tx_wakeup(hu);
|
||||||
|
|
||||||
|
intel_wait_lpm_transaction(hu);
|
||||||
|
/* Even in case of failure, continue and test the suspended flag */
|
||||||
|
|
||||||
|
clear_bit(STATE_LPM_TRANSACTION, &intel->flags);
|
||||||
|
|
||||||
|
if (!test_bit(STATE_SUSPENDED, &intel->flags)) {
|
||||||
|
bt_dev_err(hu->hdev, "Device suspend error");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_dev_dbg(hu->hdev, "Suspended");
|
||||||
|
|
||||||
|
hci_uart_set_flow_control(hu, true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intel_lpm_resume(struct hci_uart *hu)
|
||||||
|
{
|
||||||
|
struct intel_data *intel = hu->priv;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
if (!test_bit(STATE_LPM_ENABLED, &intel->flags) ||
|
||||||
|
!test_bit(STATE_SUSPENDED, &intel->flags))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bt_dev_dbg(hu->hdev, "Resuming");
|
||||||
|
|
||||||
|
hci_uart_set_flow_control(hu, false);
|
||||||
|
|
||||||
|
skb = bt_skb_alloc(0, GFP_KERNEL);
|
||||||
|
if (!skb) {
|
||||||
|
bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_cb(skb)->pkt_type = HCI_LPM_WAKE_PKT;
|
||||||
|
|
||||||
|
set_bit(STATE_LPM_TRANSACTION, &intel->flags);
|
||||||
|
|
||||||
|
/* LPM flow is a priority, enqueue packet at list head */
|
||||||
|
skb_queue_head(&intel->txq, skb);
|
||||||
|
hci_uart_tx_wakeup(hu);
|
||||||
|
|
||||||
|
intel_wait_lpm_transaction(hu);
|
||||||
|
/* Even in case of failure, continue and test the suspended flag */
|
||||||
|
|
||||||
|
clear_bit(STATE_LPM_TRANSACTION, &intel->flags);
|
||||||
|
|
||||||
|
if (test_bit(STATE_SUSPENDED, &intel->flags)) {
|
||||||
|
bt_dev_err(hu->hdev, "Device resume error");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_dev_dbg(hu->hdev, "Resumed");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
static int intel_lpm_host_wake(struct hci_uart *hu)
|
||||||
|
{
|
||||||
|
static const u8 lpm_resume_ack[] = { LPM_OP_RESUME_ACK, 0x00 };
|
||||||
|
struct intel_data *intel = hu->priv;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
hci_uart_set_flow_control(hu, false);
|
||||||
|
|
||||||
|
clear_bit(STATE_SUSPENDED, &intel->flags);
|
||||||
|
|
||||||
|
skb = bt_skb_alloc(sizeof(lpm_resume_ack), GFP_KERNEL);
|
||||||
|
if (!skb) {
|
||||||
|
bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(skb_put(skb, sizeof(lpm_resume_ack)), lpm_resume_ack,
|
||||||
|
sizeof(lpm_resume_ack));
|
||||||
|
bt_cb(skb)->pkt_type = HCI_LPM_PKT;
|
||||||
|
|
||||||
|
/* LPM flow is a priority, enqueue packet at list head */
|
||||||
|
skb_queue_head(&intel->txq, skb);
|
||||||
|
hci_uart_tx_wakeup(hu);
|
||||||
|
|
||||||
|
bt_dev_dbg(hu->hdev, "Resumed by controller");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t intel_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct intel_device *idev = dev_id;
|
||||||
|
|
||||||
|
dev_info(&idev->pdev->dev, "hci_intel irq\n");
|
||||||
|
|
||||||
|
mutex_lock(&idev->hu_lock);
|
||||||
|
if (idev->hu)
|
||||||
|
intel_lpm_host_wake(idev->hu);
|
||||||
|
mutex_unlock(&idev->hu_lock);
|
||||||
|
|
||||||
|
/* Host/Controller are now LPM resumed, trigger a new delayed suspend */
|
||||||
|
pm_runtime_get(&idev->pdev->dev);
|
||||||
|
pm_runtime_mark_last_busy(&idev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&idev->pdev->dev);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static int intel_set_power(struct hci_uart *hu, bool powered)
|
static int intel_set_power(struct hci_uart *hu, bool powered)
|
||||||
{
|
{
|
||||||
struct list_head *p;
|
struct list_head *p;
|
||||||
int err = -ENODEV;
|
int err = -ENODEV;
|
||||||
|
|
||||||
spin_lock(&intel_device_list_lock);
|
mutex_lock(&intel_device_list_lock);
|
||||||
|
|
||||||
list_for_each(p, &intel_device_list) {
|
list_for_each(p, &intel_device_list) {
|
||||||
struct intel_device *idev = list_entry(p, struct intel_device,
|
struct intel_device *idev = list_entry(p, struct intel_device,
|
||||||
@ -139,13 +328,73 @@ static int intel_set_power(struct hci_uart *hu, bool powered)
|
|||||||
hu, dev_name(&idev->pdev->dev), powered);
|
hu, dev_name(&idev->pdev->dev), powered);
|
||||||
|
|
||||||
gpiod_set_value(idev->reset, powered);
|
gpiod_set_value(idev->reset, powered);
|
||||||
|
|
||||||
|
/* Provide to idev a hu reference which is used to run LPM
|
||||||
|
* transactions (lpm suspend/resume) from PM callbacks.
|
||||||
|
* hu needs to be protected against concurrent removing during
|
||||||
|
* these PM ops.
|
||||||
|
*/
|
||||||
|
mutex_lock(&idev->hu_lock);
|
||||||
|
idev->hu = powered ? hu : NULL;
|
||||||
|
mutex_unlock(&idev->hu_lock);
|
||||||
|
|
||||||
|
if (idev->irq < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (powered && device_can_wakeup(&idev->pdev->dev)) {
|
||||||
|
err = devm_request_threaded_irq(&idev->pdev->dev,
|
||||||
|
idev->irq, NULL,
|
||||||
|
intel_irq,
|
||||||
|
IRQF_ONESHOT,
|
||||||
|
"bt-host-wake", idev);
|
||||||
|
if (err) {
|
||||||
|
BT_ERR("hu %p, unable to allocate irq-%d",
|
||||||
|
hu, idev->irq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_wakeup_enable(&idev->pdev->dev);
|
||||||
|
|
||||||
|
pm_runtime_set_active(&idev->pdev->dev);
|
||||||
|
pm_runtime_use_autosuspend(&idev->pdev->dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(&idev->pdev->dev,
|
||||||
|
LPM_SUSPEND_DELAY_MS);
|
||||||
|
pm_runtime_enable(&idev->pdev->dev);
|
||||||
|
} else if (!powered && device_may_wakeup(&idev->pdev->dev)) {
|
||||||
|
devm_free_irq(&idev->pdev->dev, idev->irq, idev);
|
||||||
|
device_wakeup_disable(&idev->pdev->dev);
|
||||||
|
|
||||||
|
pm_runtime_disable(&idev->pdev->dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock(&intel_device_list_lock);
|
mutex_unlock(&intel_device_list_lock);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void intel_busy_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct list_head *p;
|
||||||
|
struct intel_data *intel = container_of(work, struct intel_data,
|
||||||
|
busy_work);
|
||||||
|
|
||||||
|
/* Link is busy, delay the suspend */
|
||||||
|
mutex_lock(&intel_device_list_lock);
|
||||||
|
list_for_each(p, &intel_device_list) {
|
||||||
|
struct intel_device *idev = list_entry(p, struct intel_device,
|
||||||
|
list);
|
||||||
|
|
||||||
|
if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) {
|
||||||
|
pm_runtime_get(&idev->pdev->dev);
|
||||||
|
pm_runtime_mark_last_busy(&idev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&idev->pdev->dev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&intel_device_list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static int intel_open(struct hci_uart *hu)
|
static int intel_open(struct hci_uart *hu)
|
||||||
{
|
{
|
||||||
struct intel_data *intel;
|
struct intel_data *intel;
|
||||||
@ -157,6 +406,9 @@ static int intel_open(struct hci_uart *hu)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
skb_queue_head_init(&intel->txq);
|
skb_queue_head_init(&intel->txq);
|
||||||
|
INIT_WORK(&intel->busy_work, intel_busy_work);
|
||||||
|
|
||||||
|
intel->hu = hu;
|
||||||
|
|
||||||
hu->priv = intel;
|
hu->priv = intel;
|
||||||
|
|
||||||
@ -172,6 +424,8 @@ static int intel_close(struct hci_uart *hu)
|
|||||||
|
|
||||||
BT_DBG("hu %p", hu);
|
BT_DBG("hu %p", hu);
|
||||||
|
|
||||||
|
cancel_work_sync(&intel->busy_work);
|
||||||
|
|
||||||
intel_set_power(hu, false);
|
intel_set_power(hu, false);
|
||||||
|
|
||||||
skb_queue_purge(&intel->txq);
|
skb_queue_purge(&intel->txq);
|
||||||
@ -237,11 +491,11 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
|||||||
if (err && err != ETIMEDOUT)
|
if (err && err != ETIMEDOUT)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
BT_INFO("%s: Change controller speed to %d", hdev->name, speed);
|
bt_dev_info(hdev, "Change controller speed to %d", speed);
|
||||||
|
|
||||||
speed_cmd[3] = intel_convert_speed(speed);
|
speed_cmd[3] = intel_convert_speed(speed);
|
||||||
if (speed_cmd[3] == 0xff) {
|
if (speed_cmd[3] == 0xff) {
|
||||||
BT_ERR("%s: Unsupported speed", hdev->name);
|
bt_dev_err(hdev, "Unsupported speed");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,16 +504,15 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
|||||||
*/
|
*/
|
||||||
skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
|
skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
|
||||||
if (IS_ERR(skb)) {
|
if (IS_ERR(skb)) {
|
||||||
BT_ERR("%s: Reading Intel version information failed (%ld)",
|
bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
|
||||||
hdev->name, PTR_ERR(skb));
|
PTR_ERR(skb));
|
||||||
return PTR_ERR(skb);
|
return PTR_ERR(skb);
|
||||||
}
|
}
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
||||||
skb = bt_skb_alloc(sizeof(speed_cmd), GFP_KERNEL);
|
skb = bt_skb_alloc(sizeof(speed_cmd), GFP_KERNEL);
|
||||||
if (!skb) {
|
if (!skb) {
|
||||||
BT_ERR("%s: Failed to allocate memory for baudrate packet",
|
bt_dev_err(hdev, "Failed to alloc memory for baudrate packet");
|
||||||
hdev->name);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,11 +537,14 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
{
|
{
|
||||||
static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01,
|
static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01,
|
||||||
0x00, 0x08, 0x04, 0x00 };
|
0x00, 0x08, 0x04, 0x00 };
|
||||||
|
static const u8 lpm_param[] = { 0x03, 0x07, 0x01, 0x0b };
|
||||||
struct intel_data *intel = hu->priv;
|
struct intel_data *intel = hu->priv;
|
||||||
|
struct intel_device *idev = NULL;
|
||||||
struct hci_dev *hdev = hu->hdev;
|
struct hci_dev *hdev = hu->hdev;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
struct intel_version *ver;
|
struct intel_version *ver;
|
||||||
struct intel_boot_params *params;
|
struct intel_boot_params *params;
|
||||||
|
struct list_head *p;
|
||||||
const struct firmware *fw;
|
const struct firmware *fw;
|
||||||
const u8 *fw_ptr;
|
const u8 *fw_ptr;
|
||||||
char fwname[64];
|
char fwname[64];
|
||||||
@ -299,8 +555,9 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
int speed_change = 0;
|
int speed_change = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
bt_dev_dbg(hdev, "start intel_setup");
|
||||||
|
|
||||||
|
hu->hdev->set_diag = btintel_set_diag;
|
||||||
hu->hdev->set_bdaddr = btintel_set_bdaddr;
|
hu->hdev->set_bdaddr = btintel_set_bdaddr;
|
||||||
|
|
||||||
calltime = ktime_get();
|
calltime = ktime_get();
|
||||||
@ -335,21 +592,21 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
*/
|
*/
|
||||||
skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
|
skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
|
||||||
if (IS_ERR(skb)) {
|
if (IS_ERR(skb)) {
|
||||||
BT_ERR("%s: Reading Intel version information failed (%ld)",
|
bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
|
||||||
hdev->name, PTR_ERR(skb));
|
PTR_ERR(skb));
|
||||||
return PTR_ERR(skb);
|
return PTR_ERR(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skb->len != sizeof(*ver)) {
|
if (skb->len != sizeof(*ver)) {
|
||||||
BT_ERR("%s: Intel version event size mismatch", hdev->name);
|
bt_dev_err(hdev, "Intel version event size mismatch");
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -EILSEQ;
|
return -EILSEQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
ver = (struct intel_version *)skb->data;
|
ver = (struct intel_version *)skb->data;
|
||||||
if (ver->status) {
|
if (ver->status) {
|
||||||
BT_ERR("%s: Intel version command failure (%02x)",
|
bt_dev_err(hdev, "Intel version command failure (%02x)",
|
||||||
hdev->name, ver->status);
|
ver->status);
|
||||||
err = -bt_to_errno(ver->status);
|
err = -bt_to_errno(ver->status);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return err;
|
return err;
|
||||||
@ -359,8 +616,8 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
* for now only accept this single value.
|
* for now only accept this single value.
|
||||||
*/
|
*/
|
||||||
if (ver->hw_platform != 0x37) {
|
if (ver->hw_platform != 0x37) {
|
||||||
BT_ERR("%s: Unsupported Intel hardware platform (%u)",
|
bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)",
|
||||||
hdev->name, ver->hw_platform);
|
ver->hw_platform);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -371,8 +628,8 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
* when newer hardware variants come along.
|
* when newer hardware variants come along.
|
||||||
*/
|
*/
|
||||||
if (ver->hw_variant != 0x0b) {
|
if (ver->hw_variant != 0x0b) {
|
||||||
BT_ERR("%s: Unsupported Intel hardware variant (%u)",
|
bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
|
||||||
hdev->name, ver->hw_variant);
|
ver->hw_variant);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -403,8 +660,8 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
* choice is to return an error and abort the device initialization.
|
* choice is to return an error and abort the device initialization.
|
||||||
*/
|
*/
|
||||||
if (ver->fw_variant != 0x06) {
|
if (ver->fw_variant != 0x06) {
|
||||||
BT_ERR("%s: Unsupported Intel firmware variant (%u)",
|
bt_dev_err(hdev, "Unsupported Intel firmware variant (%u)",
|
||||||
hdev->name, ver->fw_variant);
|
ver->fw_variant);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
@ -416,33 +673,33 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
*/
|
*/
|
||||||
skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT);
|
skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT);
|
||||||
if (IS_ERR(skb)) {
|
if (IS_ERR(skb)) {
|
||||||
BT_ERR("%s: Reading Intel boot parameters failed (%ld)",
|
bt_dev_err(hdev, "Reading Intel boot parameters failed (%ld)",
|
||||||
hdev->name, PTR_ERR(skb));
|
PTR_ERR(skb));
|
||||||
return PTR_ERR(skb);
|
return PTR_ERR(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skb->len != sizeof(*params)) {
|
if (skb->len != sizeof(*params)) {
|
||||||
BT_ERR("%s: Intel boot parameters size mismatch", hdev->name);
|
bt_dev_err(hdev, "Intel boot parameters size mismatch");
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -EILSEQ;
|
return -EILSEQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
params = (struct intel_boot_params *)skb->data;
|
params = (struct intel_boot_params *)skb->data;
|
||||||
if (params->status) {
|
if (params->status) {
|
||||||
BT_ERR("%s: Intel boot parameters command failure (%02x)",
|
bt_dev_err(hdev, "Intel boot parameters command failure (%02x)",
|
||||||
hdev->name, params->status);
|
params->status);
|
||||||
err = -bt_to_errno(params->status);
|
err = -bt_to_errno(params->status);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_INFO("%s: Device revision is %u", hdev->name,
|
bt_dev_info(hdev, "Device revision is %u",
|
||||||
le16_to_cpu(params->dev_revid));
|
le16_to_cpu(params->dev_revid));
|
||||||
|
|
||||||
BT_INFO("%s: Secure boot is %s", hdev->name,
|
bt_dev_info(hdev, "Secure boot is %s",
|
||||||
params->secure_boot ? "enabled" : "disabled");
|
params->secure_boot ? "enabled" : "disabled");
|
||||||
|
|
||||||
BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name,
|
bt_dev_info(hdev, "Minimum firmware build %u week %u %u",
|
||||||
params->min_fw_build_nn, params->min_fw_build_cw,
|
params->min_fw_build_nn, params->min_fw_build_cw,
|
||||||
2000 + params->min_fw_build_yy);
|
2000 + params->min_fw_build_yy);
|
||||||
|
|
||||||
@ -451,8 +708,8 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
* that this bootloader does not send them, then abort the setup.
|
* that this bootloader does not send them, then abort the setup.
|
||||||
*/
|
*/
|
||||||
if (params->limited_cce != 0x00) {
|
if (params->limited_cce != 0x00) {
|
||||||
BT_ERR("%s: Unsupported Intel firmware loading method (%u)",
|
bt_dev_err(hdev, "Unsupported Intel firmware loading method (%u)",
|
||||||
hdev->name, params->limited_cce);
|
params->limited_cce);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -461,7 +718,7 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
* also be no valid address for the operational firmware.
|
* also be no valid address for the operational firmware.
|
||||||
*/
|
*/
|
||||||
if (!bacmp(¶ms->otp_bdaddr, BDADDR_ANY)) {
|
if (!bacmp(¶ms->otp_bdaddr, BDADDR_ANY)) {
|
||||||
BT_INFO("%s: No device address configured", hdev->name);
|
bt_dev_info(hdev, "No device address configured");
|
||||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,19 +733,23 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
|
|
||||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
BT_ERR("%s: Failed to load Intel firmware file (%d)",
|
bt_dev_err(hdev, "Failed to load Intel firmware file (%d)",
|
||||||
hdev->name, err);
|
err);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_INFO("%s: Found device firmware: %s", hdev->name, fwname);
|
bt_dev_info(hdev, "Found device firmware: %s", fwname);
|
||||||
|
|
||||||
|
/* Save the DDC file name for later */
|
||||||
|
snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.ddc",
|
||||||
|
le16_to_cpu(params->dev_revid));
|
||||||
|
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
||||||
if (fw->size < 644) {
|
if (fw->size < 644) {
|
||||||
BT_ERR("%s: Invalid size of firmware file (%zu)",
|
bt_dev_err(hdev, "Invalid size of firmware file (%zu)",
|
||||||
hdev->name, fw->size);
|
fw->size);
|
||||||
err = -EBADF;
|
err = -EBADF;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
@ -500,8 +761,7 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
*/
|
*/
|
||||||
err = btintel_secure_send(hdev, 0x00, 128, fw->data);
|
err = btintel_secure_send(hdev, 0x00, 128, fw->data);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
BT_ERR("%s: Failed to send firmware header (%d)",
|
bt_dev_err(hdev, "Failed to send firmware header (%d)", err);
|
||||||
hdev->name, err);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,8 +770,8 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
*/
|
*/
|
||||||
err = btintel_secure_send(hdev, 0x03, 256, fw->data + 128);
|
err = btintel_secure_send(hdev, 0x03, 256, fw->data + 128);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
BT_ERR("%s: Failed to send firmware public key (%d)",
|
bt_dev_err(hdev, "Failed to send firmware public key (%d)",
|
||||||
hdev->name, err);
|
err);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,8 +780,8 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
*/
|
*/
|
||||||
err = btintel_secure_send(hdev, 0x02, 256, fw->data + 388);
|
err = btintel_secure_send(hdev, 0x02, 256, fw->data + 388);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
BT_ERR("%s: Failed to send firmware signature (%d)",
|
bt_dev_err(hdev, "Failed to send firmware signature (%d)",
|
||||||
hdev->name, err);
|
err);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,8 +793,8 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
|
|
||||||
frag_len += sizeof(*cmd) + cmd->plen;
|
frag_len += sizeof(*cmd) + cmd->plen;
|
||||||
|
|
||||||
BT_DBG("%s: patching %td/%zu", hdev->name,
|
bt_dev_dbg(hdev, "Patching %td/%zu", (fw_ptr - fw->data),
|
||||||
(fw_ptr - fw->data), fw->size);
|
fw->size);
|
||||||
|
|
||||||
/* The parameter length of the secure send command requires
|
/* The parameter length of the secure send command requires
|
||||||
* a 4 byte alignment. It happens so that the firmware file
|
* a 4 byte alignment. It happens so that the firmware file
|
||||||
@ -552,8 +812,8 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
*/
|
*/
|
||||||
err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr);
|
err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
BT_ERR("%s: Failed to send firmware data (%d)",
|
bt_dev_err(hdev, "Failed to send firmware data (%d)",
|
||||||
hdev->name, err);
|
err);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,7 +823,7 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
|
|
||||||
set_bit(STATE_FIRMWARE_LOADED, &intel->flags);
|
set_bit(STATE_FIRMWARE_LOADED, &intel->flags);
|
||||||
|
|
||||||
BT_INFO("%s: Waiting for firmware download to complete", hdev->name);
|
bt_dev_info(hdev, "Waiting for firmware download to complete");
|
||||||
|
|
||||||
/* Before switching the device into operational mode and with that
|
/* Before switching the device into operational mode and with that
|
||||||
* booting the loaded firmware, wait for the bootloader notification
|
* booting the loaded firmware, wait for the bootloader notification
|
||||||
@ -580,19 +840,19 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
TASK_INTERRUPTIBLE,
|
TASK_INTERRUPTIBLE,
|
||||||
msecs_to_jiffies(5000));
|
msecs_to_jiffies(5000));
|
||||||
if (err == 1) {
|
if (err == 1) {
|
||||||
BT_ERR("%s: Firmware loading interrupted", hdev->name);
|
bt_dev_err(hdev, "Firmware loading interrupted");
|
||||||
err = -EINTR;
|
err = -EINTR;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("%s: Firmware loading timeout", hdev->name);
|
bt_dev_err(hdev, "Firmware loading timeout");
|
||||||
err = -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_bit(STATE_FIRMWARE_FAILED, &intel->flags)) {
|
if (test_bit(STATE_FIRMWARE_FAILED, &intel->flags)) {
|
||||||
BT_ERR("%s: Firmware loading failed", hdev->name);
|
bt_dev_err(hdev, "Firmware loading failed");
|
||||||
err = -ENOEXEC;
|
err = -ENOEXEC;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
@ -601,7 +861,7 @@ static int intel_setup(struct hci_uart *hu)
|
|||||||
delta = ktime_sub(rettime, calltime);
|
delta = ktime_sub(rettime, calltime);
|
||||||
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
||||||
|
|
||||||
BT_INFO("%s: Firmware loaded in %llu usecs", hdev->name, duration);
|
bt_dev_info(hdev, "Firmware loaded in %llu usecs", duration);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
release_firmware(fw);
|
release_firmware(fw);
|
||||||
@ -634,7 +894,7 @@ done:
|
|||||||
* 1 second. However if that happens, then just fail the setup
|
* 1 second. However if that happens, then just fail the setup
|
||||||
* since something went wrong.
|
* since something went wrong.
|
||||||
*/
|
*/
|
||||||
BT_INFO("%s: Waiting for device to boot", hdev->name);
|
bt_dev_info(hdev, "Waiting for device to boot");
|
||||||
|
|
||||||
err = intel_wait_booting(hu);
|
err = intel_wait_booting(hu);
|
||||||
if (err)
|
if (err)
|
||||||
@ -646,7 +906,39 @@ done:
|
|||||||
delta = ktime_sub(rettime, calltime);
|
delta = ktime_sub(rettime, calltime);
|
||||||
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
||||||
|
|
||||||
BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration);
|
bt_dev_info(hdev, "Device booted in %llu usecs", duration);
|
||||||
|
|
||||||
|
/* Enable LPM if matching pdev with wakeup enabled */
|
||||||
|
mutex_lock(&intel_device_list_lock);
|
||||||
|
list_for_each(p, &intel_device_list) {
|
||||||
|
struct intel_device *dev = list_entry(p, struct intel_device,
|
||||||
|
list);
|
||||||
|
if (hu->tty->dev->parent == dev->pdev->dev.parent) {
|
||||||
|
if (device_may_wakeup(&dev->pdev->dev))
|
||||||
|
idev = dev;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&intel_device_list_lock);
|
||||||
|
|
||||||
|
if (!idev)
|
||||||
|
goto no_lpm;
|
||||||
|
|
||||||
|
bt_dev_info(hdev, "Enabling LPM");
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc8b, sizeof(lpm_param), lpm_param,
|
||||||
|
HCI_CMD_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
bt_dev_err(hdev, "Failed to enable LPM");
|
||||||
|
goto no_lpm;
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
set_bit(STATE_LPM_ENABLED, &intel->flags);
|
||||||
|
|
||||||
|
no_lpm:
|
||||||
|
/* Ignore errors, device can work without DDC parameters */
|
||||||
|
btintel_load_ddc_config(hdev, fwname);
|
||||||
|
|
||||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT);
|
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT);
|
||||||
if (IS_ERR(skb))
|
if (IS_ERR(skb))
|
||||||
@ -659,7 +951,7 @@ done:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_INFO("%s: Setup complete", hdev->name);
|
bt_dev_info(hdev, "Setup complete");
|
||||||
|
|
||||||
clear_bit(STATE_BOOTLOADER, &intel->flags);
|
clear_bit(STATE_BOOTLOADER, &intel->flags);
|
||||||
|
|
||||||
@ -708,10 +1000,71 @@ recv:
|
|||||||
return hci_recv_frame(hdev, skb);
|
return hci_recv_frame(hdev, skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void intel_recv_lpm_notify(struct hci_dev *hdev, int value)
|
||||||
|
{
|
||||||
|
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||||
|
struct intel_data *intel = hu->priv;
|
||||||
|
|
||||||
|
bt_dev_dbg(hdev, "TX idle notification (%d)", value);
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
set_bit(STATE_TX_ACTIVE, &intel->flags);
|
||||||
|
schedule_work(&intel->busy_work);
|
||||||
|
} else {
|
||||||
|
clear_bit(STATE_TX_ACTIVE, &intel->flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intel_recv_lpm(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_lpm_pkt *lpm = (void *)skb->data;
|
||||||
|
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||||
|
struct intel_data *intel = hu->priv;
|
||||||
|
|
||||||
|
switch (lpm->opcode) {
|
||||||
|
case LPM_OP_TX_NOTIFY:
|
||||||
|
if (lpm->dlen < 1) {
|
||||||
|
bt_dev_err(hu->hdev, "Invalid LPM notification packet");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
intel_recv_lpm_notify(hdev, lpm->data[0]);
|
||||||
|
break;
|
||||||
|
case LPM_OP_SUSPEND_ACK:
|
||||||
|
set_bit(STATE_SUSPENDED, &intel->flags);
|
||||||
|
if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) {
|
||||||
|
smp_mb__after_atomic();
|
||||||
|
wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LPM_OP_RESUME_ACK:
|
||||||
|
clear_bit(STATE_SUSPENDED, &intel->flags);
|
||||||
|
if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) {
|
||||||
|
smp_mb__after_atomic();
|
||||||
|
wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bt_dev_err(hdev, "Unknown LPM opcode (%02x)", lpm->opcode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INTEL_RECV_LPM \
|
||||||
|
.type = HCI_LPM_PKT, \
|
||||||
|
.hlen = HCI_LPM_HDR_SIZE, \
|
||||||
|
.loff = 1, \
|
||||||
|
.lsize = 1, \
|
||||||
|
.maxlen = HCI_LPM_MAX_SIZE
|
||||||
|
|
||||||
static const struct h4_recv_pkt intel_recv_pkts[] = {
|
static const struct h4_recv_pkt intel_recv_pkts[] = {
|
||||||
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||||
{ H4_RECV_EVENT, .recv = intel_recv_event },
|
{ H4_RECV_EVENT, .recv = intel_recv_event },
|
||||||
|
{ INTEL_RECV_LPM, .recv = intel_recv_lpm },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int intel_recv(struct hci_uart *hu, const void *data, int count)
|
static int intel_recv(struct hci_uart *hu, const void *data, int count)
|
||||||
@ -726,7 +1079,7 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count)
|
|||||||
ARRAY_SIZE(intel_recv_pkts));
|
ARRAY_SIZE(intel_recv_pkts));
|
||||||
if (IS_ERR(intel->rx_skb)) {
|
if (IS_ERR(intel->rx_skb)) {
|
||||||
int err = PTR_ERR(intel->rx_skb);
|
int err = PTR_ERR(intel->rx_skb);
|
||||||
BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
|
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
|
||||||
intel->rx_skb = NULL;
|
intel->rx_skb = NULL;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -737,9 +1090,27 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count)
|
|||||||
static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct intel_data *intel = hu->priv;
|
struct intel_data *intel = hu->priv;
|
||||||
|
struct list_head *p;
|
||||||
|
|
||||||
BT_DBG("hu %p skb %p", hu, skb);
|
BT_DBG("hu %p skb %p", hu, skb);
|
||||||
|
|
||||||
|
/* Be sure our controller is resumed and potential LPM transaction
|
||||||
|
* completed before enqueuing any packet.
|
||||||
|
*/
|
||||||
|
mutex_lock(&intel_device_list_lock);
|
||||||
|
list_for_each(p, &intel_device_list) {
|
||||||
|
struct intel_device *idev = list_entry(p, struct intel_device,
|
||||||
|
list);
|
||||||
|
|
||||||
|
if (hu->tty->dev->parent == idev->pdev->dev.parent) {
|
||||||
|
pm_runtime_get_sync(&idev->pdev->dev);
|
||||||
|
pm_runtime_mark_last_busy(&idev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&idev->pdev->dev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&intel_device_list_lock);
|
||||||
|
|
||||||
skb_queue_tail(&intel->txq, skb);
|
skb_queue_tail(&intel->txq, skb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -777,6 +1148,7 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu)
|
|||||||
static const struct hci_uart_proto intel_proto = {
|
static const struct hci_uart_proto intel_proto = {
|
||||||
.id = HCI_UART_INTEL,
|
.id = HCI_UART_INTEL,
|
||||||
.name = "Intel",
|
.name = "Intel",
|
||||||
|
.manufacturer = 2,
|
||||||
.init_speed = 115200,
|
.init_speed = 115200,
|
||||||
.oper_speed = 3000000,
|
.oper_speed = 3000000,
|
||||||
.open = intel_open,
|
.open = intel_open,
|
||||||
@ -795,24 +1167,61 @@ static const struct acpi_device_id intel_acpi_match[] = {
|
|||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(acpi, intel_acpi_match);
|
MODULE_DEVICE_TABLE(acpi, intel_acpi_match);
|
||||||
|
#endif
|
||||||
|
|
||||||
static int intel_acpi_probe(struct intel_device *idev)
|
#ifdef CONFIG_PM
|
||||||
|
static int intel_suspend_device(struct device *dev)
|
||||||
{
|
{
|
||||||
const struct acpi_device_id *id;
|
struct intel_device *idev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
id = acpi_match_device(intel_acpi_match, &idev->pdev->dev);
|
mutex_lock(&idev->hu_lock);
|
||||||
if (!id)
|
if (idev->hu)
|
||||||
return -ENODEV;
|
intel_lpm_suspend(idev->hu);
|
||||||
|
mutex_unlock(&idev->hu_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
static int intel_acpi_probe(struct intel_device *idev)
|
static int intel_resume_device(struct device *dev)
|
||||||
{
|
{
|
||||||
return -ENODEV;
|
struct intel_device *idev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
mutex_lock(&idev->hu_lock);
|
||||||
|
if (idev->hu)
|
||||||
|
intel_lpm_resume(idev->hu);
|
||||||
|
mutex_unlock(&idev->hu_lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int intel_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct intel_device *idev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (device_may_wakeup(dev))
|
||||||
|
enable_irq_wake(idev->irq);
|
||||||
|
|
||||||
|
return intel_suspend_device(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intel_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct intel_device *idev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (device_may_wakeup(dev))
|
||||||
|
disable_irq_wake(idev->irq);
|
||||||
|
|
||||||
|
return intel_resume_device(dev);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops intel_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(intel_suspend_device, intel_resume_device, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static int intel_probe(struct platform_device *pdev)
|
static int intel_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct intel_device *idev;
|
struct intel_device *idev;
|
||||||
@ -821,15 +1230,9 @@ static int intel_probe(struct platform_device *pdev)
|
|||||||
if (!idev)
|
if (!idev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
idev->pdev = pdev;
|
mutex_init(&idev->hu_lock);
|
||||||
|
|
||||||
if (ACPI_HANDLE(&pdev->dev)) {
|
idev->pdev = pdev;
|
||||||
int err = intel_acpi_probe(idev);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
} else {
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
idev->reset = devm_gpiod_get_optional(&pdev->dev, "reset",
|
idev->reset = devm_gpiod_get_optional(&pdev->dev, "reset",
|
||||||
GPIOD_OUT_LOW);
|
GPIOD_OUT_LOW);
|
||||||
@ -838,14 +1241,40 @@ static int intel_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(idev->reset);
|
return PTR_ERR(idev->reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idev->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (idev->irq < 0) {
|
||||||
|
struct gpio_desc *host_wake;
|
||||||
|
|
||||||
|
dev_err(&pdev->dev, "No IRQ, falling back to gpio-irq\n");
|
||||||
|
|
||||||
|
host_wake = devm_gpiod_get_optional(&pdev->dev, "host-wake",
|
||||||
|
GPIOD_IN);
|
||||||
|
if (IS_ERR(host_wake)) {
|
||||||
|
dev_err(&pdev->dev, "Unable to retrieve IRQ\n");
|
||||||
|
goto no_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
idev->irq = gpiod_to_irq(host_wake);
|
||||||
|
if (idev->irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "No corresponding irq for gpio\n");
|
||||||
|
goto no_irq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only enable wake-up/irq when controller is powered */
|
||||||
|
device_set_wakeup_capable(&pdev->dev, true);
|
||||||
|
device_wakeup_disable(&pdev->dev);
|
||||||
|
|
||||||
|
no_irq:
|
||||||
platform_set_drvdata(pdev, idev);
|
platform_set_drvdata(pdev, idev);
|
||||||
|
|
||||||
/* Place this instance on the device list */
|
/* Place this instance on the device list */
|
||||||
spin_lock(&intel_device_list_lock);
|
mutex_lock(&intel_device_list_lock);
|
||||||
list_add_tail(&idev->list, &intel_device_list);
|
list_add_tail(&idev->list, &intel_device_list);
|
||||||
spin_unlock(&intel_device_list_lock);
|
mutex_unlock(&intel_device_list_lock);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "registered.\n");
|
dev_info(&pdev->dev, "registered, gpio(%d)/irq(%d).\n",
|
||||||
|
desc_to_gpio(idev->reset), idev->irq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -854,9 +1283,11 @@ static int intel_remove(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct intel_device *idev = platform_get_drvdata(pdev);
|
struct intel_device *idev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
spin_lock(&intel_device_list_lock);
|
device_wakeup_disable(&pdev->dev);
|
||||||
|
|
||||||
|
mutex_lock(&intel_device_list_lock);
|
||||||
list_del(&idev->list);
|
list_del(&idev->list);
|
||||||
spin_unlock(&intel_device_list_lock);
|
mutex_unlock(&intel_device_list_lock);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "unregistered.\n");
|
dev_info(&pdev->dev, "unregistered.\n");
|
||||||
|
|
||||||
@ -869,6 +1300,7 @@ static struct platform_driver intel_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "hci_intel",
|
.name = "hci_intel",
|
||||||
.acpi_match_table = ACPI_PTR(intel_acpi_match),
|
.acpi_match_table = ACPI_PTR(intel_acpi_match),
|
||||||
|
.pm = &intel_pm_ops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,9 +208,6 @@ static int hci_uart_open(struct hci_dev *hdev)
|
|||||||
BT_DBG("%s %p", hdev->name, hdev);
|
BT_DBG("%s %p", hdev->name, hdev);
|
||||||
|
|
||||||
/* Nothing to do for UART driver */
|
/* Nothing to do for UART driver */
|
||||||
|
|
||||||
set_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,9 +238,6 @@ static int hci_uart_close(struct hci_dev *hdev)
|
|||||||
{
|
{
|
||||||
BT_DBG("hdev %p", hdev);
|
BT_DBG("hdev %p", hdev);
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
hci_uart_flush(hdev);
|
hci_uart_flush(hdev);
|
||||||
hdev->flush = NULL;
|
hdev->flush = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
@ -254,9 +248,6 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
{
|
{
|
||||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||||
|
|
||||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
|
BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
|
||||||
|
|
||||||
hu->proto->enqueue(hu, skb);
|
hu->proto->enqueue(hu, skb);
|
||||||
@ -470,8 +461,6 @@ static int hci_uart_tty_open(struct tty_struct *tty)
|
|||||||
INIT_WORK(&hu->init_ready, hci_uart_init_work);
|
INIT_WORK(&hu->init_ready, hci_uart_init_work);
|
||||||
INIT_WORK(&hu->write_work, hci_uart_write_work);
|
INIT_WORK(&hu->write_work, hci_uart_write_work);
|
||||||
|
|
||||||
spin_lock_init(&hu->rx_lock);
|
|
||||||
|
|
||||||
/* Flush any pending characters in the driver and line discipline. */
|
/* Flush any pending characters in the driver and line discipline. */
|
||||||
|
|
||||||
/* FIXME: why is this needed. Note don't use ldisc_ref here as the
|
/* FIXME: why is this needed. Note don't use ldisc_ref here as the
|
||||||
@ -569,14 +558,14 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
|
|||||||
if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
|
if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
spin_lock(&hu->rx_lock);
|
/* It does not need a lock here as it is already protected by a mutex in
|
||||||
|
* tty caller
|
||||||
|
*/
|
||||||
hu->proto->recv(hu, data, count);
|
hu->proto->recv(hu, data, count);
|
||||||
|
|
||||||
if (hu->hdev)
|
if (hu->hdev)
|
||||||
hu->hdev->stat.byte_rx += count;
|
hu->hdev->stat.byte_rx += count;
|
||||||
|
|
||||||
spin_unlock(&hu->rx_lock);
|
|
||||||
|
|
||||||
tty_unthrottle(tty);
|
tty_unthrottle(tty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,6 +587,13 @@ static int hci_uart_register_dev(struct hci_uart *hu)
|
|||||||
hdev->bus = HCI_UART;
|
hdev->bus = HCI_UART;
|
||||||
hci_set_drvdata(hdev, hu);
|
hci_set_drvdata(hdev, hu);
|
||||||
|
|
||||||
|
/* Only when vendor specific setup callback is provided, consider
|
||||||
|
* the manufacturer information valid. This avoids filling in the
|
||||||
|
* value for Ericsson when nothing is specified.
|
||||||
|
*/
|
||||||
|
if (hu->proto->setup)
|
||||||
|
hdev->manufacturer = hu->proto->manufacturer;
|
||||||
|
|
||||||
hdev->open = hci_uart_open;
|
hdev->open = hci_uart_open;
|
||||||
hdev->close = hci_uart_close;
|
hdev->close = hci_uart_close;
|
||||||
hdev->flush = hci_uart_flush;
|
hdev->flush = hci_uart_flush;
|
||||||
|
@ -41,13 +41,13 @@
|
|||||||
#define HCI_IBS_SLEEP_IND 0xFE
|
#define HCI_IBS_SLEEP_IND 0xFE
|
||||||
#define HCI_IBS_WAKE_IND 0xFD
|
#define HCI_IBS_WAKE_IND 0xFD
|
||||||
#define HCI_IBS_WAKE_ACK 0xFC
|
#define HCI_IBS_WAKE_ACK 0xFC
|
||||||
#define HCI_MAX_IBS_SIZE 10
|
#define HCI_MAX_IBS_SIZE 10
|
||||||
|
|
||||||
/* Controller states */
|
/* Controller states */
|
||||||
#define STATE_IN_BAND_SLEEP_ENABLED 1
|
#define STATE_IN_BAND_SLEEP_ENABLED 1
|
||||||
|
|
||||||
#define IBS_WAKE_RETRANS_TIMEOUT_MS 100
|
#define IBS_WAKE_RETRANS_TIMEOUT_MS 100
|
||||||
#define IBS_TX_IDLE_TIMEOUT_MS 2000
|
#define IBS_TX_IDLE_TIMEOUT_MS 2000
|
||||||
#define BAUDRATE_SETTLE_TIMEOUT_MS 300
|
#define BAUDRATE_SETTLE_TIMEOUT_MS 300
|
||||||
|
|
||||||
/* HCI_IBS transmit side sleep protocol states */
|
/* HCI_IBS transmit side sleep protocol states */
|
||||||
@ -181,8 +181,8 @@ static void serial_clock_vote(unsigned long vote, struct hci_uart *hu)
|
|||||||
else
|
else
|
||||||
__serial_clock_off(hu->tty);
|
__serial_clock_off(hu->tty);
|
||||||
|
|
||||||
BT_DBG("Vote serial clock %s(%s)", new_vote? "true" : "false",
|
BT_DBG("Vote serial clock %s(%s)", new_vote ? "true" : "false",
|
||||||
vote? "true" : "false");
|
vote ? "true" : "false");
|
||||||
|
|
||||||
diff = jiffies_to_msecs(jiffies - qca->vote_last_jif);
|
diff = jiffies_to_msecs(jiffies - qca->vote_last_jif);
|
||||||
|
|
||||||
@ -347,7 +347,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg)
|
|||||||
struct hci_uart *hu = (struct hci_uart *)arg;
|
struct hci_uart *hu = (struct hci_uart *)arg;
|
||||||
struct qca_data *qca = hu->priv;
|
struct qca_data *qca = hu->priv;
|
||||||
unsigned long flags, retrans_delay;
|
unsigned long flags, retrans_delay;
|
||||||
unsigned long retransmit = 0;
|
bool retransmit = false;
|
||||||
|
|
||||||
BT_DBG("hu %p wake retransmit timeout in %d state",
|
BT_DBG("hu %p wake retransmit timeout in %d state",
|
||||||
hu, qca->tx_ibs_state);
|
hu, qca->tx_ibs_state);
|
||||||
@ -358,7 +358,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg)
|
|||||||
switch (qca->tx_ibs_state) {
|
switch (qca->tx_ibs_state) {
|
||||||
case HCI_IBS_TX_WAKING:
|
case HCI_IBS_TX_WAKING:
|
||||||
/* No WAKE_ACK, retransmit WAKE */
|
/* No WAKE_ACK, retransmit WAKE */
|
||||||
retransmit = 1;
|
retransmit = true;
|
||||||
if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
|
if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
|
||||||
BT_ERR("Failed to acknowledge device wake up");
|
BT_ERR("Failed to acknowledge device wake up");
|
||||||
break;
|
break;
|
||||||
@ -821,7 +821,7 @@ static struct sk_buff *qca_dequeue(struct hci_uart *hu)
|
|||||||
|
|
||||||
static uint8_t qca_get_baudrate_value(int speed)
|
static uint8_t qca_get_baudrate_value(int speed)
|
||||||
{
|
{
|
||||||
switch(speed) {
|
switch (speed) {
|
||||||
case 9600:
|
case 9600:
|
||||||
return QCA_BAUDRATE_9600;
|
return QCA_BAUDRATE_9600;
|
||||||
case 19200:
|
case 19200:
|
||||||
@ -947,6 +947,7 @@ static int qca_setup(struct hci_uart *hu)
|
|||||||
static struct hci_uart_proto qca_proto = {
|
static struct hci_uart_proto qca_proto = {
|
||||||
.id = HCI_UART_QCA,
|
.id = HCI_UART_QCA,
|
||||||
.name = "QCA",
|
.name = "QCA",
|
||||||
|
.manufacturer = 29,
|
||||||
.init_speed = 115200,
|
.init_speed = 115200,
|
||||||
.oper_speed = 3000000,
|
.oper_speed = 3000000,
|
||||||
.open = qca_open,
|
.open = qca_open,
|
||||||
|
@ -59,6 +59,7 @@ struct hci_uart;
|
|||||||
struct hci_uart_proto {
|
struct hci_uart_proto {
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
unsigned int manufacturer;
|
||||||
unsigned int init_speed;
|
unsigned int init_speed;
|
||||||
unsigned int oper_speed;
|
unsigned int oper_speed;
|
||||||
int (*open)(struct hci_uart *hu);
|
int (*open)(struct hci_uart *hu);
|
||||||
@ -85,7 +86,6 @@ struct hci_uart {
|
|||||||
|
|
||||||
struct sk_buff *tx_skb;
|
struct sk_buff *tx_skb;
|
||||||
unsigned long tx_state;
|
unsigned long tx_state;
|
||||||
spinlock_t rx_lock;
|
|
||||||
|
|
||||||
unsigned int init_speed;
|
unsigned int init_speed;
|
||||||
unsigned int oper_speed;
|
unsigned int oper_speed;
|
||||||
|
@ -55,8 +55,6 @@ struct vhci_data {
|
|||||||
|
|
||||||
static int vhci_open_dev(struct hci_dev *hdev)
|
static int vhci_open_dev(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
set_bit(HCI_RUNNING, &hdev->flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,9 +62,6 @@ static int vhci_close_dev(struct hci_dev *hdev)
|
|||||||
{
|
{
|
||||||
struct vhci_data *data = hci_get_drvdata(hdev);
|
struct vhci_data *data = hci_get_drvdata(hdev);
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
skb_queue_purge(&data->readq);
|
skb_queue_purge(&data->readq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -85,9 +80,6 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
{
|
{
|
||||||
struct vhci_data *data = hci_get_drvdata(hdev);
|
struct vhci_data *data = hci_get_drvdata(hdev);
|
||||||
|
|
||||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
|
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
|
||||||
skb_queue_tail(&data->readq, skb);
|
skb_queue_tail(&data->readq, skb);
|
||||||
|
|
||||||
|
@ -1325,9 +1325,6 @@ static void nes_netdev_get_drvinfo(struct net_device *netdev,
|
|||||||
"%u.%u", nesadapter->firmware_version >> 16,
|
"%u.%u", nesadapter->firmware_version >> 16,
|
||||||
nesadapter->firmware_version & 0x000000ff);
|
nesadapter->firmware_version & 0x000000ff);
|
||||||
strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version));
|
strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version));
|
||||||
drvinfo->testinfo_len = 0;
|
|
||||||
drvinfo->eedump_len = 0;
|
|
||||||
drvinfo->regdump_len = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,9 +56,6 @@
|
|||||||
|
|
||||||
#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
|
#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
|
||||||
|
|
||||||
#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5)
|
|
||||||
#define ARMADA_370_XP_FABRIC_IRQ (3)
|
|
||||||
|
|
||||||
#define IPI_DOORBELL_START (0)
|
#define IPI_DOORBELL_START (0)
|
||||||
#define IPI_DOORBELL_END (8)
|
#define IPI_DOORBELL_END (8)
|
||||||
#define IPI_DOORBELL_MASK 0xFF
|
#define IPI_DOORBELL_MASK 0xFF
|
||||||
@ -81,13 +78,10 @@ static phys_addr_t msi_doorbell_addr;
|
|||||||
|
|
||||||
static inline bool is_percpu_irq(irq_hw_number_t irq)
|
static inline bool is_percpu_irq(irq_hw_number_t irq)
|
||||||
{
|
{
|
||||||
switch (irq) {
|
if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS)
|
||||||
case ARMADA_370_XP_TIMER0_PER_CPU_IRQ:
|
|
||||||
case ARMADA_370_XP_FABRIC_IRQ:
|
|
||||||
return true;
|
return true;
|
||||||
default:
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -550,7 +544,7 @@ static void armada_370_xp_mpic_resume(void)
|
|||||||
if (virq == 0)
|
if (virq == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
|
if (!is_percpu_irq(irq))
|
||||||
writel(irq, per_cpu_int_base +
|
writel(irq, per_cpu_int_base +
|
||||||
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||||
else
|
else
|
||||||
|
@ -646,14 +646,14 @@ rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
|
|||||||
|
|
||||||
f1 = Read_hfc8_stable(l1p->hw, A_F1);
|
f1 = Read_hfc8_stable(l1p->hw, A_F1);
|
||||||
f2 = Read_hfc8(l1p->hw, A_F2);
|
f2 = Read_hfc8(l1p->hw, A_F2);
|
||||||
df = f1 - f2;
|
|
||||||
if ((f1 - f2) < 0)
|
|
||||||
df = f1 - f2 + MAX_F_CNT + 1;
|
|
||||||
|
|
||||||
|
if (f1 < f2)
|
||||||
|
df = MAX_F_CNT + 1 + f1 - f2;
|
||||||
|
else
|
||||||
|
df = f1 - f2;
|
||||||
|
|
||||||
if (!df) {
|
if (!df)
|
||||||
return; /* no complete frame in fifo */
|
return; /* no complete frame in fifo */
|
||||||
}
|
|
||||||
|
|
||||||
z1 = Read_hfc16_stable(l1p->hw, A_Z1);
|
z1 = Read_hfc16_stable(l1p->hw, A_Z1);
|
||||||
z2 = Read_hfc16(l1p->hw, A_Z2);
|
z2 = Read_hfc16(l1p->hw, A_Z2);
|
||||||
|
@ -301,6 +301,8 @@ isdn_ppp_open(int min, struct file *file)
|
|||||||
is->compflags = 0;
|
is->compflags = 0;
|
||||||
|
|
||||||
is->reset = isdn_ppp_ccp_reset_alloc(is);
|
is->reset = isdn_ppp_ccp_reset_alloc(is);
|
||||||
|
if (!is->reset)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
is->lp = NULL;
|
is->lp = NULL;
|
||||||
is->mp_seqno = 0; /* MP sequence number */
|
is->mp_seqno = 0; /* MP sequence number */
|
||||||
@ -320,6 +322,10 @@ isdn_ppp_open(int min, struct file *file)
|
|||||||
* VJ header compression init
|
* VJ header compression init
|
||||||
*/
|
*/
|
||||||
is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */
|
is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */
|
||||||
|
if (IS_ERR(is->slcomp)) {
|
||||||
|
isdn_ppp_ccp_reset_free(is);
|
||||||
|
return PTR_ERR(is->slcomp);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_IPPP_FILTER
|
#ifdef CONFIG_IPPP_FILTER
|
||||||
is->pass_filter = NULL;
|
is->pass_filter = NULL;
|
||||||
@ -567,10 +573,8 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
|
|||||||
is->maxcid = val;
|
is->maxcid = val;
|
||||||
#ifdef CONFIG_ISDN_PPP_VJ
|
#ifdef CONFIG_ISDN_PPP_VJ
|
||||||
sltmp = slhc_init(16, val);
|
sltmp = slhc_init(16, val);
|
||||||
if (!sltmp) {
|
if (IS_ERR(sltmp))
|
||||||
printk(KERN_ERR "ippp, can't realloc slhc struct\n");
|
return PTR_ERR(sltmp);
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
if (is->slcomp)
|
if (is->slcomp)
|
||||||
slhc_free(is->slcomp);
|
slhc_free(is->slcomp);
|
||||||
is->slcomp = sltmp;
|
is->slcomp = sltmp;
|
||||||
|
@ -235,7 +235,7 @@ void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
|
|||||||
|
|
||||||
int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
|
int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
|
||||||
{
|
{
|
||||||
int len, incomplete = 0, found = 0;
|
int incomplete = 0, found = 0;
|
||||||
char *dup, *tok, *name, *args;
|
char *dup, *tok, *name, *args;
|
||||||
struct dsp_element_entry *entry, *n;
|
struct dsp_element_entry *entry, *n;
|
||||||
struct dsp_pipeline_entry *pipeline_entry;
|
struct dsp_pipeline_entry *pipeline_entry;
|
||||||
@ -247,17 +247,9 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
|
|||||||
if (!list_empty(&pipeline->list))
|
if (!list_empty(&pipeline->list))
|
||||||
_dsp_pipeline_destroy(pipeline);
|
_dsp_pipeline_destroy(pipeline);
|
||||||
|
|
||||||
if (!cfg)
|
dup = kstrdup(cfg, GFP_ATOMIC);
|
||||||
return 0;
|
|
||||||
|
|
||||||
len = strlen(cfg);
|
|
||||||
if (!len)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
dup = kmalloc(len + 1, GFP_ATOMIC);
|
|
||||||
if (!dup)
|
if (!dup)
|
||||||
return 0;
|
return 0;
|
||||||
strcpy(dup, cfg);
|
|
||||||
while ((tok = strsep(&dup, "|"))) {
|
while ((tok = strsep(&dup, "|"))) {
|
||||||
if (!strlen(tok))
|
if (!strlen(tok))
|
||||||
continue;
|
continue;
|
||||||
|
@ -298,7 +298,10 @@ config NLMON
|
|||||||
|
|
||||||
config NET_VRF
|
config NET_VRF
|
||||||
tristate "Virtual Routing and Forwarding (Lite)"
|
tristate "Virtual Routing and Forwarding (Lite)"
|
||||||
depends on IP_MULTIPLE_TABLES && IPV6_MULTIPLE_TABLES
|
depends on IP_MULTIPLE_TABLES
|
||||||
|
depends on NET_L3_MASTER_DEV
|
||||||
|
depends on IPV6 || IPV6=n
|
||||||
|
depends on IPV6_MULTIPLE_TABLES || IPV6=n
|
||||||
---help---
|
---help---
|
||||||
This option enables the support for mapping interfaces into VRF's. The
|
This option enables the support for mapping interfaces into VRF's. The
|
||||||
support enables VRF devices.
|
support enables VRF devices.
|
||||||
|
@ -103,6 +103,7 @@ config ARCNET_RIM_I
|
|||||||
|
|
||||||
config ARCNET_COM20020
|
config ARCNET_COM20020
|
||||||
tristate "ARCnet COM20020 chipset driver"
|
tristate "ARCnet COM20020 chipset driver"
|
||||||
|
depends on LEDS_CLASS
|
||||||
help
|
help
|
||||||
This is the driver for the new COM20020 chipset. It supports such
|
This is the driver for the new COM20020 chipset. It supports such
|
||||||
things as promiscuous mode, so packet sniffing is possible, and
|
things as promiscuous mode, so packet sniffing is possible, and
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - "raw mode" packet encapsulation (no soft headers)
|
* Linux ARCnet driver - "raw mode" packet encapsulation (no soft headers)
|
||||||
*
|
*
|
||||||
* Written 1994-1999 by Avery Pennarun.
|
* Written 1994-1999 by Avery Pennarun.
|
||||||
* Derived from skeleton.c by Donald Becker.
|
* Derived from skeleton.c by Donald Becker.
|
||||||
*
|
*
|
||||||
@ -24,6 +24,8 @@
|
|||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -31,20 +33,124 @@
|
|||||||
#include <net/arp.h>
|
#include <net/arp.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/arcdevice.h>
|
#include "arcdevice.h"
|
||||||
|
|
||||||
#define VERSION "arcnet: raw mode (`r') encapsulation support loaded.\n"
|
|
||||||
|
|
||||||
|
|
||||||
|
/* packet receiver */
|
||||||
static void rx(struct net_device *dev, int bufnum,
|
static void rx(struct net_device *dev, int bufnum,
|
||||||
struct archdr *pkthdr, int length);
|
struct archdr *pkthdr, int length)
|
||||||
static int build_header(struct sk_buff *skb, struct net_device *dev,
|
|
||||||
unsigned short type, uint8_t daddr);
|
|
||||||
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|
||||||
int bufnum);
|
|
||||||
|
|
||||||
static struct ArcProto rawmode_proto =
|
|
||||||
{
|
{
|
||||||
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct archdr *pkt = pkthdr;
|
||||||
|
int ofs;
|
||||||
|
|
||||||
|
arc_printk(D_DURING, dev, "it's a raw packet (length=%d)\n", length);
|
||||||
|
|
||||||
|
if (length > MTU)
|
||||||
|
ofs = 512 - length;
|
||||||
|
else
|
||||||
|
ofs = 256 - length;
|
||||||
|
|
||||||
|
skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
|
||||||
|
if (!skb) {
|
||||||
|
dev->stats.rx_dropped++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skb_put(skb, length + ARC_HDR_SIZE);
|
||||||
|
skb->dev = dev;
|
||||||
|
|
||||||
|
pkt = (struct archdr *)skb->data;
|
||||||
|
|
||||||
|
skb_reset_mac_header(skb);
|
||||||
|
skb_pull(skb, ARC_HDR_SIZE);
|
||||||
|
|
||||||
|
/* up to sizeof(pkt->soft) has already been copied from the card */
|
||||||
|
memcpy(pkt, pkthdr, sizeof(struct archdr));
|
||||||
|
if (length > sizeof(pkt->soft))
|
||||||
|
lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
|
||||||
|
pkt->soft.raw + sizeof(pkt->soft),
|
||||||
|
length - sizeof(pkt->soft));
|
||||||
|
|
||||||
|
if (BUGLVL(D_SKB))
|
||||||
|
arcnet_dump_skb(dev, skb, "rx");
|
||||||
|
|
||||||
|
skb->protocol = cpu_to_be16(ETH_P_ARCNET);
|
||||||
|
netif_rx(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the ARCnet hard/soft headers for raw mode.
|
||||||
|
* There aren't any soft headers in raw mode - not even the protocol id.
|
||||||
|
*/
|
||||||
|
static int build_header(struct sk_buff *skb, struct net_device *dev,
|
||||||
|
unsigned short type, uint8_t daddr)
|
||||||
|
{
|
||||||
|
int hdr_size = ARC_HDR_SIZE;
|
||||||
|
struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size);
|
||||||
|
|
||||||
|
/* Set the source hardware address.
|
||||||
|
*
|
||||||
|
* This is pretty pointless for most purposes, but it can help in
|
||||||
|
* debugging. ARCnet does not allow us to change the source address
|
||||||
|
* in the actual packet sent.
|
||||||
|
*/
|
||||||
|
pkt->hard.source = *dev->dev_addr;
|
||||||
|
|
||||||
|
/* see linux/net/ethernet/eth.c to see where I got the following */
|
||||||
|
|
||||||
|
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
|
||||||
|
/* FIXME: fill in the last byte of the dest ipaddr here
|
||||||
|
* to better comply with RFC1051 in "noarp" mode.
|
||||||
|
*/
|
||||||
|
pkt->hard.dest = 0;
|
||||||
|
return hdr_size;
|
||||||
|
}
|
||||||
|
/* otherwise, just fill it in and go! */
|
||||||
|
pkt->hard.dest = daddr;
|
||||||
|
|
||||||
|
return hdr_size; /* success */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
||||||
|
int bufnum)
|
||||||
|
{
|
||||||
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
|
struct arc_hardware *hard = &pkt->hard;
|
||||||
|
int ofs;
|
||||||
|
|
||||||
|
arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n",
|
||||||
|
lp->next_tx, lp->cur_tx, bufnum);
|
||||||
|
|
||||||
|
/* hard header is not included in packet length */
|
||||||
|
length -= ARC_HDR_SIZE;
|
||||||
|
|
||||||
|
if (length > XMTU) {
|
||||||
|
/* should never happen! other people already check for this. */
|
||||||
|
arc_printk(D_NORMAL, dev, "Bug! prepare_tx with size %d (> %d)\n",
|
||||||
|
length, XMTU);
|
||||||
|
length = XMTU;
|
||||||
|
}
|
||||||
|
if (length >= MinTU) {
|
||||||
|
hard->offset[0] = 0;
|
||||||
|
hard->offset[1] = ofs = 512 - length;
|
||||||
|
} else if (length > MTU) {
|
||||||
|
hard->offset[0] = 0;
|
||||||
|
hard->offset[1] = ofs = 512 - length - 3;
|
||||||
|
} else {
|
||||||
|
hard->offset[0] = ofs = 256 - length;
|
||||||
|
}
|
||||||
|
|
||||||
|
arc_printk(D_DURING, dev, "prepare_tx: length=%d ofs=%d\n",
|
||||||
|
length, ofs);
|
||||||
|
|
||||||
|
lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
|
||||||
|
lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length);
|
||||||
|
|
||||||
|
lp->lastload_dest = hard->dest;
|
||||||
|
|
||||||
|
return 1; /* done */
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ArcProto rawmode_proto = {
|
||||||
.suffix = 'r',
|
.suffix = 'r',
|
||||||
.mtu = XMTU,
|
.mtu = XMTU,
|
||||||
.rx = rx,
|
.rx = rx,
|
||||||
@ -54,12 +160,11 @@ static struct ArcProto rawmode_proto =
|
|||||||
.ack_tx = NULL
|
.ack_tx = NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int __init arcnet_raw_init(void)
|
static int __init arcnet_raw_init(void)
|
||||||
{
|
{
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
printk(VERSION);
|
pr_info("raw mode (`r') encapsulation support loaded\n");
|
||||||
|
|
||||||
for (count = 0; count < 256; count++)
|
for (count = 0; count < 256; count++)
|
||||||
if (arc_proto_map[count] == arc_proto_default)
|
if (arc_proto_map[count] == arc_proto_default)
|
||||||
@ -82,122 +187,3 @@ module_init(arcnet_raw_init);
|
|||||||
module_exit(arcnet_raw_exit);
|
module_exit(arcnet_raw_exit);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
|
||||||
/* packet receiver */
|
|
||||||
static void rx(struct net_device *dev, int bufnum,
|
|
||||||
struct archdr *pkthdr, int length)
|
|
||||||
{
|
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
|
||||||
struct sk_buff *skb;
|
|
||||||
struct archdr *pkt = pkthdr;
|
|
||||||
int ofs;
|
|
||||||
|
|
||||||
BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length);
|
|
||||||
|
|
||||||
if (length > MTU)
|
|
||||||
ofs = 512 - length;
|
|
||||||
else
|
|
||||||
ofs = 256 - length;
|
|
||||||
|
|
||||||
skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
|
|
||||||
if (skb == NULL) {
|
|
||||||
BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
|
|
||||||
dev->stats.rx_dropped++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
skb_put(skb, length + ARC_HDR_SIZE);
|
|
||||||
skb->dev = dev;
|
|
||||||
|
|
||||||
pkt = (struct archdr *) skb->data;
|
|
||||||
|
|
||||||
skb_reset_mac_header(skb);
|
|
||||||
skb_pull(skb, ARC_HDR_SIZE);
|
|
||||||
|
|
||||||
/* up to sizeof(pkt->soft) has already been copied from the card */
|
|
||||||
memcpy(pkt, pkthdr, sizeof(struct archdr));
|
|
||||||
if (length > sizeof(pkt->soft))
|
|
||||||
lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
|
|
||||||
pkt->soft.raw + sizeof(pkt->soft),
|
|
||||||
length - sizeof(pkt->soft));
|
|
||||||
|
|
||||||
BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
|
|
||||||
|
|
||||||
skb->protocol = cpu_to_be16(ETH_P_ARCNET);
|
|
||||||
netif_rx(skb);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create the ARCnet hard/soft headers for raw mode.
|
|
||||||
* There aren't any soft headers in raw mode - not even the protocol id.
|
|
||||||
*/
|
|
||||||
static int build_header(struct sk_buff *skb, struct net_device *dev,
|
|
||||||
unsigned short type, uint8_t daddr)
|
|
||||||
{
|
|
||||||
int hdr_size = ARC_HDR_SIZE;
|
|
||||||
struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the source hardware address.
|
|
||||||
*
|
|
||||||
* This is pretty pointless for most purposes, but it can help in
|
|
||||||
* debugging. ARCnet does not allow us to change the source address in
|
|
||||||
* the actual packet sent)
|
|
||||||
*/
|
|
||||||
pkt->hard.source = *dev->dev_addr;
|
|
||||||
|
|
||||||
/* see linux/net/ethernet/eth.c to see where I got the following */
|
|
||||||
|
|
||||||
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
|
|
||||||
/*
|
|
||||||
* FIXME: fill in the last byte of the dest ipaddr here to better
|
|
||||||
* comply with RFC1051 in "noarp" mode.
|
|
||||||
*/
|
|
||||||
pkt->hard.dest = 0;
|
|
||||||
return hdr_size;
|
|
||||||
}
|
|
||||||
/* otherwise, just fill it in and go! */
|
|
||||||
pkt->hard.dest = daddr;
|
|
||||||
|
|
||||||
return hdr_size; /* success */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|
||||||
int bufnum)
|
|
||||||
{
|
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
|
||||||
struct arc_hardware *hard = &pkt->hard;
|
|
||||||
int ofs;
|
|
||||||
|
|
||||||
BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
|
|
||||||
lp->next_tx, lp->cur_tx, bufnum);
|
|
||||||
|
|
||||||
length -= ARC_HDR_SIZE; /* hard header is not included in packet length */
|
|
||||||
|
|
||||||
if (length > XMTU) {
|
|
||||||
/* should never happen! other people already check for this. */
|
|
||||||
BUGMSG(D_NORMAL, "Bug! prepare_tx with size %d (> %d)\n",
|
|
||||||
length, XMTU);
|
|
||||||
length = XMTU;
|
|
||||||
}
|
|
||||||
if (length >= MinTU) {
|
|
||||||
hard->offset[0] = 0;
|
|
||||||
hard->offset[1] = ofs = 512 - length;
|
|
||||||
} else if (length > MTU) {
|
|
||||||
hard->offset[0] = 0;
|
|
||||||
hard->offset[1] = ofs = 512 - length - 3;
|
|
||||||
} else
|
|
||||||
hard->offset[0] = ofs = 256 - length;
|
|
||||||
|
|
||||||
BUGMSG(D_DURING, "prepare_tx: length=%d ofs=%d\n",
|
|
||||||
length,ofs);
|
|
||||||
|
|
||||||
lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
|
|
||||||
lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length);
|
|
||||||
|
|
||||||
lp->lastload_dest = hard->dest;
|
|
||||||
|
|
||||||
return 1; /* done */
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - "RIM I" (entirely mem-mapped) cards
|
* Linux ARCnet driver - "RIM I" (entirely mem-mapped) cards
|
||||||
*
|
*
|
||||||
* Written 1994-1999 by Avery Pennarun.
|
* Written 1994-1999 by Avery Pennarun.
|
||||||
* Written 1999-2000 by Martin Mares <mj@ucw.cz>.
|
* Written 1999-2000 by Martin Mares <mj@ucw.cz>.
|
||||||
* Derived from skeleton.c by Donald Becker.
|
* Derived from skeleton.c by Donald Becker.
|
||||||
@ -24,6 +24,9 @@
|
|||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
@ -33,12 +36,10 @@
|
|||||||
#include <linux/bootmem.h>
|
#include <linux/bootmem.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <asm/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/arcdevice.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define VERSION "arcnet: RIM I (entirely mem-mapped) support\n"
|
|
||||||
|
|
||||||
|
#include "arcdevice.h"
|
||||||
|
#include "com9026.h"
|
||||||
|
|
||||||
/* Internal function declarations */
|
/* Internal function declarations */
|
||||||
|
|
||||||
@ -50,66 +51,46 @@ static void arcrimi_setmask(struct net_device *dev, int mask);
|
|||||||
static int arcrimi_reset(struct net_device *dev, int really_reset);
|
static int arcrimi_reset(struct net_device *dev, int really_reset);
|
||||||
static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
||||||
void *buf, int count);
|
void *buf, int count);
|
||||||
static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offset,
|
static void arcrimi_copy_from_card(struct net_device *dev, int bufnum,
|
||||||
void *buf, int count);
|
int offset, void *buf, int count);
|
||||||
|
|
||||||
/* Handy defines for ARCnet specific stuff */
|
/* Handy defines for ARCnet specific stuff */
|
||||||
|
|
||||||
/* Amount of I/O memory used by the card */
|
/* Amount of I/O memory used by the card */
|
||||||
#define BUFFER_SIZE (512)
|
#define BUFFER_SIZE (512)
|
||||||
#define MIRROR_SIZE (BUFFER_SIZE*4)
|
#define MIRROR_SIZE (BUFFER_SIZE * 4)
|
||||||
|
|
||||||
/* COM 9026 controller chip --> ARCnet register addresses */
|
/* We cannot probe for a RIM I card; one reason is I don't know how to reset
|
||||||
#define _INTMASK (ioaddr+0) /* writable */
|
|
||||||
#define _STATUS (ioaddr+0) /* readable */
|
|
||||||
#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
|
|
||||||
#define _RESET (ioaddr+8) /* software reset (on read) */
|
|
||||||
#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */
|
|
||||||
#define _ADDR_HI (ioaddr+15) /* Control registers for said */
|
|
||||||
#define _ADDR_LO (ioaddr+14)
|
|
||||||
#define _CONFIG (ioaddr+2) /* Configuration register */
|
|
||||||
|
|
||||||
#undef ASTATUS
|
|
||||||
#undef ACOMMAND
|
|
||||||
#undef AINTMASK
|
|
||||||
|
|
||||||
#define ASTATUS() readb(_STATUS)
|
|
||||||
#define ACOMMAND(cmd) writeb((cmd),_COMMAND)
|
|
||||||
#define AINTMASK(msk) writeb((msk),_INTMASK)
|
|
||||||
#define SETCONF() writeb(lp->config,_CONFIG)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We cannot probe for a RIM I card; one reason is I don't know how to reset
|
|
||||||
* them. In fact, we can't even get their node ID automatically. So, we
|
* them. In fact, we can't even get their node ID automatically. So, we
|
||||||
* need to be passed a specific shmem address, IRQ, and node ID.
|
* need to be passed a specific shmem address, IRQ, and node ID.
|
||||||
*/
|
*/
|
||||||
static int __init arcrimi_probe(struct net_device *dev)
|
static int __init arcrimi_probe(struct net_device *dev)
|
||||||
{
|
{
|
||||||
BUGLVL(D_NORMAL) printk(VERSION);
|
if (BUGLVL(D_NORMAL)) {
|
||||||
BUGLVL(D_NORMAL) printk("E-mail me if you actually test the RIM I driver, please!\n");
|
pr_info("%s\n", "RIM I (entirely mem-mapped) support");
|
||||||
|
pr_info("E-mail me if you actually test the RIM I driver, please!\n");
|
||||||
BUGLVL(D_NORMAL) printk("Given: node %02Xh, shmem %lXh, irq %d\n",
|
pr_info("Given: node %02Xh, shmem %lXh, irq %d\n",
|
||||||
dev->dev_addr[0], dev->mem_start, dev->irq);
|
dev->dev_addr[0], dev->mem_start, dev->irq);
|
||||||
|
}
|
||||||
|
|
||||||
if (dev->mem_start <= 0 || dev->irq <= 0) {
|
if (dev->mem_start <= 0 || dev->irq <= 0) {
|
||||||
BUGLVL(D_NORMAL) printk("No autoprobe for RIM I; you "
|
if (BUGLVL(D_NORMAL))
|
||||||
"must specify the shmem and irq!\n");
|
pr_err("No autoprobe for RIM I; you must specify the shmem and irq!\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
if (dev->dev_addr[0] == 0) {
|
if (dev->dev_addr[0] == 0) {
|
||||||
BUGLVL(D_NORMAL) printk("You need to specify your card's station "
|
if (BUGLVL(D_NORMAL))
|
||||||
"ID!\n");
|
pr_err("You need to specify your card's station ID!\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
/*
|
/* Grab the memory region at mem_start for MIRROR_SIZE bytes.
|
||||||
* Grab the memory region at mem_start for MIRROR_SIZE bytes.
|
|
||||||
* Later in arcrimi_found() the real size will be determined
|
* Later in arcrimi_found() the real size will be determined
|
||||||
* and this reserve will be released and the correct size
|
* and this reserve will be released and the correct size
|
||||||
* will be taken.
|
* will be taken.
|
||||||
*/
|
*/
|
||||||
if (!request_mem_region(dev->mem_start, MIRROR_SIZE, "arcnet (90xx)")) {
|
if (!request_mem_region(dev->mem_start, MIRROR_SIZE, "arcnet (90xx)")) {
|
||||||
BUGLVL(D_NORMAL) printk("Card memory already allocated\n");
|
if (BUGLVL(D_NORMAL))
|
||||||
|
pr_notice("Card memory already allocated\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
return arcrimi_found(dev);
|
return arcrimi_found(dev);
|
||||||
@ -125,7 +106,7 @@ static int check_mirror(unsigned long addr, size_t size)
|
|||||||
|
|
||||||
p = ioremap(addr, size);
|
p = ioremap(addr, size);
|
||||||
if (p) {
|
if (p) {
|
||||||
if (readb(p) == TESTvalue)
|
if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue)
|
||||||
res = 1;
|
res = 1;
|
||||||
else
|
else
|
||||||
res = 0;
|
res = 0;
|
||||||
@ -136,9 +117,8 @@ static int check_mirror(unsigned long addr, size_t size)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Set up the struct net_device associated with this card.
|
||||||
* Set up the struct net_device associated with this card. Called after
|
* Called after probing succeeds.
|
||||||
* probing succeeds.
|
|
||||||
*/
|
*/
|
||||||
static int __init arcrimi_found(struct net_device *dev)
|
static int __init arcrimi_found(struct net_device *dev)
|
||||||
{
|
{
|
||||||
@ -151,7 +131,7 @@ static int __init arcrimi_found(struct net_device *dev)
|
|||||||
p = ioremap(dev->mem_start, MIRROR_SIZE);
|
p = ioremap(dev->mem_start, MIRROR_SIZE);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
release_mem_region(dev->mem_start, MIRROR_SIZE);
|
release_mem_region(dev->mem_start, MIRROR_SIZE);
|
||||||
BUGMSG(D_NORMAL, "Can't ioremap\n");
|
arc_printk(D_NORMAL, dev, "Can't ioremap\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,13 +139,14 @@ static int __init arcrimi_found(struct net_device *dev)
|
|||||||
if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) {
|
if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) {
|
||||||
iounmap(p);
|
iounmap(p);
|
||||||
release_mem_region(dev->mem_start, MIRROR_SIZE);
|
release_mem_region(dev->mem_start, MIRROR_SIZE);
|
||||||
BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
|
arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
shmem = dev->mem_start;
|
shmem = dev->mem_start;
|
||||||
writeb(TESTvalue, p);
|
arcnet_writeb(TESTvalue, p, COM9026_REG_W_INTMASK);
|
||||||
writeb(dev->dev_addr[0], p + 1); /* actually the node ID */
|
arcnet_writeb(TESTvalue, p, COM9026_REG_W_COMMAND);
|
||||||
|
/* actually the station/node ID */
|
||||||
|
|
||||||
/* find the real shared memory start/end points, including mirrors */
|
/* find the real shared memory start/end points, including mirrors */
|
||||||
|
|
||||||
@ -174,7 +155,7 @@ static int __init arcrimi_found(struct net_device *dev)
|
|||||||
* 2k (or there are no mirrors at all) but on some, it's 4k.
|
* 2k (or there are no mirrors at all) but on some, it's 4k.
|
||||||
*/
|
*/
|
||||||
mirror_size = MIRROR_SIZE;
|
mirror_size = MIRROR_SIZE;
|
||||||
if (readb(p) == TESTvalue &&
|
if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue &&
|
||||||
check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
|
check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
|
||||||
check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
|
check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
|
||||||
mirror_size = 2 * MIRROR_SIZE;
|
mirror_size = 2 * MIRROR_SIZE;
|
||||||
@ -204,8 +185,7 @@ static int __init arcrimi_found(struct net_device *dev)
|
|||||||
lp->hw.copy_to_card = arcrimi_copy_to_card;
|
lp->hw.copy_to_card = arcrimi_copy_to_card;
|
||||||
lp->hw.copy_from_card = arcrimi_copy_from_card;
|
lp->hw.copy_from_card = arcrimi_copy_from_card;
|
||||||
|
|
||||||
/*
|
/* re-reserve the memory region - arcrimi_probe() alloced this reqion
|
||||||
* re-reserve the memory region - arcrimi_probe() alloced this reqion
|
|
||||||
* but didn't know the real size. Free that region and then re-get
|
* but didn't know the real size. Free that region and then re-get
|
||||||
* with the correct size. There is a VERY slim chance this could
|
* with the correct size. There is a VERY slim chance this could
|
||||||
* fail.
|
* fail.
|
||||||
@ -215,24 +195,25 @@ static int __init arcrimi_found(struct net_device *dev)
|
|||||||
if (!request_mem_region(dev->mem_start,
|
if (!request_mem_region(dev->mem_start,
|
||||||
dev->mem_end - dev->mem_start + 1,
|
dev->mem_end - dev->mem_start + 1,
|
||||||
"arcnet (90xx)")) {
|
"arcnet (90xx)")) {
|
||||||
BUGMSG(D_NORMAL, "Card memory already allocated\n");
|
arc_printk(D_NORMAL, dev, "Card memory already allocated\n");
|
||||||
goto err_free_irq;
|
goto err_free_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
lp->mem_start = ioremap(dev->mem_start, dev->mem_end - dev->mem_start + 1);
|
lp->mem_start = ioremap(dev->mem_start,
|
||||||
|
dev->mem_end - dev->mem_start + 1);
|
||||||
if (!lp->mem_start) {
|
if (!lp->mem_start) {
|
||||||
BUGMSG(D_NORMAL, "Can't remap device memory!\n");
|
arc_printk(D_NORMAL, dev, "Can't remap device memory!\n");
|
||||||
goto err_release_mem;
|
goto err_release_mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get and check the station ID from offset 1 in shmem */
|
/* get and check the station ID from offset 1 in shmem */
|
||||||
dev->dev_addr[0] = readb(lp->mem_start + 1);
|
dev->dev_addr[0] = arcnet_readb(lp->mem_start, COM9026_REG_R_STATION);
|
||||||
|
|
||||||
BUGMSG(D_NORMAL, "ARCnet RIM I: station %02Xh found at IRQ %d, "
|
arc_printk(D_NORMAL, dev, "ARCnet RIM I: station %02Xh found at IRQ %d, ShMem %lXh (%ld*%d bytes)\n",
|
||||||
"ShMem %lXh (%ld*%d bytes).\n",
|
dev->dev_addr[0],
|
||||||
dev->dev_addr[0],
|
dev->irq, dev->mem_start,
|
||||||
dev->irq, dev->mem_start,
|
(dev->mem_end - dev->mem_start + 1) / mirror_size,
|
||||||
(dev->mem_end - dev->mem_start + 1) / mirror_size, mirror_size);
|
mirror_size);
|
||||||
|
|
||||||
err = register_netdev(dev);
|
err = register_netdev(dev);
|
||||||
if (err)
|
if (err)
|
||||||
@ -249,9 +230,7 @@ err_free_irq:
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Do a hardware reset on the card, and set up necessary registers.
|
||||||
/*
|
|
||||||
* Do a hardware reset on the card, and set up necessary registers.
|
|
||||||
*
|
*
|
||||||
* This should be called as little as possible, because it disrupts the
|
* This should be called as little as possible, because it disrupts the
|
||||||
* token on the network (causes a RECON) and requires a significant delay.
|
* token on the network (causes a RECON) and requires a significant delay.
|
||||||
@ -263,17 +242,19 @@ static int arcrimi_reset(struct net_device *dev, int really_reset)
|
|||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
void __iomem *ioaddr = lp->mem_start + 0x800;
|
void __iomem *ioaddr = lp->mem_start + 0x800;
|
||||||
|
|
||||||
BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS());
|
arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",
|
||||||
|
dev->name, arcnet_readb(ioaddr, COM9026_REG_R_STATUS));
|
||||||
|
|
||||||
if (really_reset) {
|
if (really_reset) {
|
||||||
writeb(TESTvalue, ioaddr - 0x800); /* fake reset */
|
arcnet_writeb(TESTvalue, ioaddr, -0x800); /* fake reset */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ACOMMAND(CFLAGScmd | RESETclear); /* clear flags & end reset */
|
/* clear flags & end reset */
|
||||||
ACOMMAND(CFLAGScmd | CONFIGclear);
|
arcnet_writeb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
arcnet_writeb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
|
||||||
/* enable extended (512-byte) packets */
|
/* enable extended (512-byte) packets */
|
||||||
ACOMMAND(CONFIGcmd | EXTconf);
|
arcnet_writeb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
|
||||||
/* done! return success. */
|
/* done! return success. */
|
||||||
return 0;
|
return 0;
|
||||||
@ -284,7 +265,7 @@ static void arcrimi_setmask(struct net_device *dev, int mask)
|
|||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
void __iomem *ioaddr = lp->mem_start + 0x800;
|
void __iomem *ioaddr = lp->mem_start + 0x800;
|
||||||
|
|
||||||
AINTMASK(mask);
|
arcnet_writeb(mask, ioaddr, COM9026_REG_W_INTMASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int arcrimi_status(struct net_device *dev)
|
static int arcrimi_status(struct net_device *dev)
|
||||||
@ -292,7 +273,7 @@ static int arcrimi_status(struct net_device *dev)
|
|||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
void __iomem *ioaddr = lp->mem_start + 0x800;
|
void __iomem *ioaddr = lp->mem_start + 0x800;
|
||||||
|
|
||||||
return ASTATUS();
|
return arcnet_readb(ioaddr, COM9026_REG_R_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void arcrimi_command(struct net_device *dev, int cmd)
|
static void arcrimi_command(struct net_device *dev, int cmd)
|
||||||
@ -300,7 +281,7 @@ static void arcrimi_command(struct net_device *dev, int cmd)
|
|||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
void __iomem *ioaddr = lp->mem_start + 0x800;
|
void __iomem *ioaddr = lp->mem_start + 0x800;
|
||||||
|
|
||||||
ACOMMAND(cmd);
|
arcnet_writeb(cmd, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
||||||
@ -308,16 +289,17 @@ static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
|||||||
{
|
{
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
|
void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
|
||||||
TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count));
|
|
||||||
|
TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void arcrimi_copy_from_card(struct net_device *dev, int bufnum,
|
||||||
static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offset,
|
int offset, void *buf, int count)
|
||||||
void *buf, int count)
|
|
||||||
{
|
{
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
|
void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
|
||||||
TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
|
|
||||||
|
TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int node;
|
static int node;
|
||||||
@ -374,12 +356,13 @@ static void __exit arc_rimi_exit(void)
|
|||||||
static int __init arcrimi_setup(char *s)
|
static int __init arcrimi_setup(char *s)
|
||||||
{
|
{
|
||||||
int ints[8];
|
int ints[8];
|
||||||
|
|
||||||
s = get_options(s, 8, ints);
|
s = get_options(s, 8, ints);
|
||||||
if (!ints[0])
|
if (!ints[0])
|
||||||
return 1;
|
return 1;
|
||||||
switch (ints[0]) {
|
switch (ints[0]) {
|
||||||
default: /* ERROR */
|
default: /* ERROR */
|
||||||
printk("arcrimi: Too many arguments.\n");
|
pr_err("Too many arguments\n");
|
||||||
case 3: /* Node ID */
|
case 3: /* Node ID */
|
||||||
node = ints[3];
|
node = ints[3];
|
||||||
case 2: /* IRQ */
|
case 2: /* IRQ */
|
||||||
|
@ -34,7 +34,6 @@
|
|||||||
*/
|
*/
|
||||||
#define RECON_THRESHOLD 30
|
#define RECON_THRESHOLD 30
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define this to the minimum "timeout" value. If a transmit takes longer
|
* Define this to the minimum "timeout" value. If a transmit takes longer
|
||||||
* than TX_TIMEOUT jiffies, Linux will abort the TX and retry. On a large
|
* than TX_TIMEOUT jiffies, Linux will abort the TX and retry. On a large
|
||||||
@ -44,14 +43,12 @@
|
|||||||
*/
|
*/
|
||||||
#define TX_TIMEOUT (HZ * 200 / 1000)
|
#define TX_TIMEOUT (HZ * 200 / 1000)
|
||||||
|
|
||||||
|
|
||||||
/* Display warnings about the driver being an ALPHA version. */
|
/* Display warnings about the driver being an ALPHA version. */
|
||||||
#undef ALPHA_WARNING
|
#undef ALPHA_WARNING
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Debugging bitflags: each option can be enabled individually.
|
* Debugging bitflags: each option can be enabled individually.
|
||||||
*
|
*
|
||||||
* Note: only debug flags included in the ARCNET_DEBUG_MAX define will
|
* Note: only debug flags included in the ARCNET_DEBUG_MAX define will
|
||||||
* actually be available. GCC will (at least, GCC 2.7.0 will) notice
|
* actually be available. GCC will (at least, GCC 2.7.0 will) notice
|
||||||
* lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize
|
* lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize
|
||||||
@ -77,35 +74,47 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ARCNET_DEBUG
|
#ifndef ARCNET_DEBUG
|
||||||
#define ARCNET_DEBUG (D_NORMAL|D_EXTRA)
|
#define ARCNET_DEBUG (D_NORMAL | D_EXTRA)
|
||||||
#endif
|
#endif
|
||||||
extern int arcnet_debug;
|
extern int arcnet_debug;
|
||||||
|
|
||||||
|
#define BUGLVL(x) ((x) & ARCNET_DEBUG_MAX & arcnet_debug)
|
||||||
|
|
||||||
/* macros to simplify debug checking */
|
/* macros to simplify debug checking */
|
||||||
#define BUGLVL(x) if ((ARCNET_DEBUG_MAX)&arcnet_debug&(x))
|
#define arc_printk(x, dev, fmt, ...) \
|
||||||
#define BUGMSG2(x,msg,args...) do { BUGLVL(x) printk(msg, ## args); } while (0)
|
do { \
|
||||||
#define BUGMSG(x,msg,args...) \
|
if (BUGLVL(x)) { \
|
||||||
BUGMSG2(x, "%s%6s: " msg, \
|
if ((x) == D_NORMAL) \
|
||||||
x==D_NORMAL ? KERN_WARNING \
|
netdev_warn(dev, fmt, ##__VA_ARGS__); \
|
||||||
: x < D_DURING ? KERN_INFO : KERN_DEBUG, \
|
else if ((x) < D_DURING) \
|
||||||
dev->name , ## args)
|
netdev_info(dev, fmt, ##__VA_ARGS__); \
|
||||||
|
else \
|
||||||
|
netdev_dbg(dev, fmt, ##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define arc_cont(x, fmt, ...) \
|
||||||
|
do { \
|
||||||
|
if (BUGLVL(x)) \
|
||||||
|
pr_cont(fmt, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/* see how long a function call takes to run, expressed in CPU cycles */
|
/* see how long a function call takes to run, expressed in CPU cycles */
|
||||||
#define TIME(name, bytes, call) BUGLVL(D_TIMING) { \
|
#define TIME(dev, name, bytes, call) \
|
||||||
unsigned long _x, _y; \
|
do { \
|
||||||
_x = get_cycles(); \
|
if (BUGLVL(D_TIMING)) { \
|
||||||
call; \
|
unsigned long _x, _y; \
|
||||||
_y = get_cycles(); \
|
_x = get_cycles(); \
|
||||||
BUGMSG(D_TIMING, \
|
call; \
|
||||||
"%s: %d bytes in %lu cycles == " \
|
_y = get_cycles(); \
|
||||||
"%lu Kbytes/100Mcycle\n",\
|
arc_printk(D_TIMING, dev, \
|
||||||
name, bytes, _y - _x, \
|
"%s: %d bytes in %lu cycles == %lu Kbytes/100Mcycle\n", \
|
||||||
100000000 / 1024 * bytes / (_y - _x + 1));\
|
name, bytes, _y - _x, \
|
||||||
} \
|
100000000 / 1024 * bytes / (_y - _x + 1)); \
|
||||||
else { \
|
} else { \
|
||||||
call;\
|
call; \
|
||||||
}
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Time needed to reset the card - in ms (milliseconds). This works on my
|
* Time needed to reset the card - in ms (milliseconds). This works on my
|
||||||
@ -155,6 +164,7 @@ extern int arcnet_debug;
|
|||||||
#define CONFIGcmd 0x05 /* define configuration */
|
#define CONFIGcmd 0x05 /* define configuration */
|
||||||
#define CFLAGScmd 0x06 /* clear flags */
|
#define CFLAGScmd 0x06 /* clear flags */
|
||||||
#define TESTcmd 0x07 /* load test flags */
|
#define TESTcmd 0x07 /* load test flags */
|
||||||
|
#define STARTIOcmd 0x18 /* start internal operation */
|
||||||
|
|
||||||
/* flags for "clear flags" command */
|
/* flags for "clear flags" command */
|
||||||
#define RESETclear 0x08 /* power-on-reset */
|
#define RESETclear 0x08 /* power-on-reset */
|
||||||
@ -182,29 +192,27 @@ extern int arcnet_debug;
|
|||||||
#define ARC_CAN_10MBIT 2 /* card uses COM20022, supporting 10MBit,
|
#define ARC_CAN_10MBIT 2 /* card uses COM20022, supporting 10MBit,
|
||||||
but default is 2.5MBit. */
|
but default is 2.5MBit. */
|
||||||
|
|
||||||
|
|
||||||
/* information needed to define an encapsulation driver */
|
/* information needed to define an encapsulation driver */
|
||||||
struct ArcProto {
|
struct ArcProto {
|
||||||
char suffix; /* a for RFC1201, e for ether-encap, etc. */
|
char suffix; /* a for RFC1201, e for ether-encap, etc. */
|
||||||
int mtu; /* largest possible packet */
|
int mtu; /* largest possible packet */
|
||||||
int is_ip; /* This is a ip plugin - not a raw thing */
|
int is_ip; /* This is a ip plugin - not a raw thing */
|
||||||
|
|
||||||
void (*rx) (struct net_device * dev, int bufnum,
|
void (*rx)(struct net_device *dev, int bufnum,
|
||||||
struct archdr * pkthdr, int length);
|
struct archdr *pkthdr, int length);
|
||||||
int (*build_header) (struct sk_buff * skb, struct net_device *dev,
|
int (*build_header)(struct sk_buff *skb, struct net_device *dev,
|
||||||
unsigned short ethproto, uint8_t daddr);
|
unsigned short ethproto, uint8_t daddr);
|
||||||
|
|
||||||
/* these functions return '1' if the skb can now be freed */
|
/* these functions return '1' if the skb can now be freed */
|
||||||
int (*prepare_tx) (struct net_device * dev, struct archdr * pkt, int length,
|
int (*prepare_tx)(struct net_device *dev, struct archdr *pkt,
|
||||||
int bufnum);
|
int length, int bufnum);
|
||||||
int (*continue_tx) (struct net_device * dev, int bufnum);
|
int (*continue_tx)(struct net_device *dev, int bufnum);
|
||||||
int (*ack_tx) (struct net_device * dev, int acked);
|
int (*ack_tx)(struct net_device *dev, int acked);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct ArcProto *arc_proto_map[256], *arc_proto_default,
|
extern struct ArcProto *arc_proto_map[256], *arc_proto_default,
|
||||||
*arc_bcast_proto, *arc_raw_proto;
|
*arc_bcast_proto, *arc_raw_proto;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Incoming" is information needed for each address that could be sending
|
* "Incoming" is information needed for each address that could be sending
|
||||||
* to us. Mostly for partially-received split packets.
|
* to us. Mostly for partially-received split packets.
|
||||||
@ -216,7 +224,6 @@ struct Incoming {
|
|||||||
numpackets; /* number of packets in split */
|
numpackets; /* number of packets in split */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* only needed for RFC1201 */
|
/* only needed for RFC1201 */
|
||||||
struct Outgoing {
|
struct Outgoing {
|
||||||
struct ArcProto *proto; /* protocol driver that owns this:
|
struct ArcProto *proto; /* protocol driver that owns this:
|
||||||
@ -230,6 +237,7 @@ struct Outgoing {
|
|||||||
numsegs; /* number of segments */
|
numsegs; /* number of segments */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define ARCNET_LED_NAME_SZ (IFNAMSIZ + 6)
|
||||||
|
|
||||||
struct arcnet_local {
|
struct arcnet_local {
|
||||||
uint8_t config, /* current value of CONFIG register */
|
uint8_t config, /* current value of CONFIG register */
|
||||||
@ -251,10 +259,16 @@ struct arcnet_local {
|
|||||||
char *card_name; /* card ident string */
|
char *card_name; /* card ident string */
|
||||||
int card_flags; /* special card features */
|
int card_flags; /* special card features */
|
||||||
|
|
||||||
|
|
||||||
/* On preemtive and SMB a lock is needed */
|
/* On preemtive and SMB a lock is needed */
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
|
struct led_trigger *tx_led_trig;
|
||||||
|
char tx_led_trig_name[ARCNET_LED_NAME_SZ];
|
||||||
|
struct led_trigger *recon_led_trig;
|
||||||
|
char recon_led_trig_name[ARCNET_LED_NAME_SZ];
|
||||||
|
|
||||||
|
struct timer_list timer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Buffer management: an ARCnet card has 4 x 512-byte buffers, each of
|
* Buffer management: an ARCnet card has 4 x 512-byte buffers, each of
|
||||||
* which can be used for either sending or receiving. The new dynamic
|
* which can be used for either sending or receiving. The new dynamic
|
||||||
@ -263,13 +277,13 @@ struct arcnet_local {
|
|||||||
* situations in which we (for example) want to pre-load a transmit
|
* situations in which we (for example) want to pre-load a transmit
|
||||||
* buffer, or start receiving while we copy a received packet to
|
* buffer, or start receiving while we copy a received packet to
|
||||||
* memory.
|
* memory.
|
||||||
*
|
*
|
||||||
* The rules: only the interrupt handler is allowed to _add_ buffers to
|
* The rules: only the interrupt handler is allowed to _add_ buffers to
|
||||||
* the queue; thus, this doesn't require a lock. Both the interrupt
|
* the queue; thus, this doesn't require a lock. Both the interrupt
|
||||||
* handler and the transmit function will want to _remove_ buffers, so
|
* handler and the transmit function will want to _remove_ buffers, so
|
||||||
* we need to handle the situation where they try to do it at the same
|
* we need to handle the situation where they try to do it at the same
|
||||||
* time.
|
* time.
|
||||||
*
|
*
|
||||||
* If next_buf == first_free_buf, the queue is empty. Since there are
|
* If next_buf == first_free_buf, the queue is empty. Since there are
|
||||||
* only four possible buffers, the queue should never be full.
|
* only four possible buffers, the queue should never be full.
|
||||||
*/
|
*/
|
||||||
@ -298,34 +312,41 @@ struct arcnet_local {
|
|||||||
/* hardware-specific functions */
|
/* hardware-specific functions */
|
||||||
struct {
|
struct {
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
void (*command) (struct net_device * dev, int cmd);
|
void (*command)(struct net_device *dev, int cmd);
|
||||||
int (*status) (struct net_device * dev);
|
int (*status)(struct net_device *dev);
|
||||||
void (*intmask) (struct net_device * dev, int mask);
|
void (*intmask)(struct net_device *dev, int mask);
|
||||||
int (*reset) (struct net_device * dev, int really_reset);
|
int (*reset)(struct net_device *dev, int really_reset);
|
||||||
void (*open) (struct net_device * dev);
|
void (*open)(struct net_device *dev);
|
||||||
void (*close) (struct net_device * dev);
|
void (*close)(struct net_device *dev);
|
||||||
|
void (*datatrigger) (struct net_device * dev, int enable);
|
||||||
|
void (*recontrigger) (struct net_device * dev, int enable);
|
||||||
|
|
||||||
void (*copy_to_card) (struct net_device * dev, int bufnum, int offset,
|
void (*copy_to_card)(struct net_device *dev, int bufnum,
|
||||||
void *buf, int count);
|
int offset, void *buf, int count);
|
||||||
void (*copy_from_card) (struct net_device * dev, int bufnum, int offset,
|
void (*copy_from_card)(struct net_device *dev, int bufnum,
|
||||||
void *buf, int count);
|
int offset, void *buf, int count);
|
||||||
} hw;
|
} hw;
|
||||||
|
|
||||||
void __iomem *mem_start; /* pointer to ioremap'ed MMIO */
|
void __iomem *mem_start; /* pointer to ioremap'ed MMIO */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum arcnet_led_event {
|
||||||
|
ARCNET_LED_EVENT_RECON,
|
||||||
|
ARCNET_LED_EVENT_OPEN,
|
||||||
|
ARCNET_LED_EVENT_STOP,
|
||||||
|
ARCNET_LED_EVENT_TX,
|
||||||
|
};
|
||||||
|
|
||||||
#define ARCRESET(x) (lp->hw.reset(dev, (x)))
|
void arcnet_led_event(struct net_device *netdev, enum arcnet_led_event event);
|
||||||
#define ACOMMAND(x) (lp->hw.command(dev, (x)))
|
void devm_arcnet_led_init(struct net_device *netdev, int index, int subid);
|
||||||
#define ASTATUS() (lp->hw.status(dev))
|
|
||||||
#define AINTMASK(x) (lp->hw.intmask(dev, (x)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if ARCNET_DEBUG_MAX & D_SKB
|
#if ARCNET_DEBUG_MAX & D_SKB
|
||||||
void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc);
|
void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc);
|
||||||
#else
|
#else
|
||||||
#define arcnet_dump_skb(dev,skb,desc) ;
|
static inline
|
||||||
|
void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc)
|
||||||
|
{
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void arcnet_unregister_proto(struct ArcProto *proto);
|
void arcnet_unregister_proto(struct ArcProto *proto);
|
||||||
@ -335,8 +356,34 @@ struct net_device *alloc_arcdev(const char *name);
|
|||||||
int arcnet_open(struct net_device *dev);
|
int arcnet_open(struct net_device *dev);
|
||||||
int arcnet_close(struct net_device *dev);
|
int arcnet_close(struct net_device *dev);
|
||||||
netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
|
netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
|
||||||
struct net_device *dev);
|
struct net_device *dev);
|
||||||
void arcnet_timeout(struct net_device *dev);
|
void arcnet_timeout(struct net_device *dev);
|
||||||
|
|
||||||
|
/* I/O equivalents */
|
||||||
|
|
||||||
|
#ifdef CONFIG_SA1100_CT6001
|
||||||
|
#define BUS_ALIGN 2 /* 8 bit device on a 16 bit bus - needs padding */
|
||||||
|
#else
|
||||||
|
#define BUS_ALIGN 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* addr and offset allow register like names to define the actual IO address.
|
||||||
|
* A configuration option multiplies the offset for alignment.
|
||||||
|
*/
|
||||||
|
#define arcnet_inb(addr, offset) \
|
||||||
|
inb((addr) + BUS_ALIGN * (offset))
|
||||||
|
#define arcnet_outb(value, addr, offset) \
|
||||||
|
outb(value, (addr) + BUS_ALIGN * (offset))
|
||||||
|
|
||||||
|
#define arcnet_insb(addr, offset, buffer, count) \
|
||||||
|
insb((addr) + BUS_ALIGN * (offset), buffer, count)
|
||||||
|
#define arcnet_outsb(addr, offset, buffer, count) \
|
||||||
|
outsb((addr) + BUS_ALIGN * (offset), buffer, count)
|
||||||
|
|
||||||
|
#define arcnet_readb(addr, offset) \
|
||||||
|
readb((addr) + (offset))
|
||||||
|
#define arcnet_writeb(value, addr, offset) \
|
||||||
|
writeb(value, (addr) + (offset))
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
#endif /* _LINUX_ARCDEVICE_H */
|
#endif /* _LINUX_ARCDEVICE_H */
|
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,8 @@
|
|||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -33,9 +35,8 @@
|
|||||||
#include <net/arp.h>
|
#include <net/arp.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/arcdevice.h>
|
|
||||||
|
|
||||||
#define VERSION "arcnet: cap mode (`c') encapsulation support loaded.\n"
|
#include "arcdevice.h"
|
||||||
|
|
||||||
/* packet receiver */
|
/* packet receiver */
|
||||||
static void rx(struct net_device *dev, int bufnum,
|
static void rx(struct net_device *dev, int bufnum,
|
||||||
@ -47,7 +48,8 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
char *pktbuf, *pkthdrbuf;
|
char *pktbuf, *pkthdrbuf;
|
||||||
int ofs;
|
int ofs;
|
||||||
|
|
||||||
BUGMSG(D_DURING, "it's a raw(cap) packet (length=%d)\n", length);
|
arc_printk(D_DURING, dev, "it's a raw(cap) packet (length=%d)\n",
|
||||||
|
length);
|
||||||
|
|
||||||
if (length >= MinTU)
|
if (length >= MinTU)
|
||||||
ofs = 512 - length;
|
ofs = 512 - length;
|
||||||
@ -55,8 +57,7 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
ofs = 256 - length;
|
ofs = 256 - length;
|
||||||
|
|
||||||
skb = alloc_skb(length + ARC_HDR_SIZE + sizeof(int), GFP_ATOMIC);
|
skb = alloc_skb(length + ARC_HDR_SIZE + sizeof(int), GFP_ATOMIC);
|
||||||
if (skb == NULL) {
|
if (!skb) {
|
||||||
BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
|
|
||||||
dev->stats.rx_dropped++;
|
dev->stats.rx_dropped++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -66,17 +67,17 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
pkt = (struct archdr *)skb_mac_header(skb);
|
pkt = (struct archdr *)skb_mac_header(skb);
|
||||||
skb_pull(skb, ARC_HDR_SIZE);
|
skb_pull(skb, ARC_HDR_SIZE);
|
||||||
|
|
||||||
/* up to sizeof(pkt->soft) has already been copied from the card */
|
/* up to sizeof(pkt->soft) has already been copied from the card
|
||||||
/* squeeze in an int for the cap encapsulation */
|
* squeeze in an int for the cap encapsulation
|
||||||
|
* use these variables to be sure we count in bytes, not in
|
||||||
/* use these variables to be sure we count in bytes, not in
|
* sizeof(struct archdr)
|
||||||
sizeof(struct archdr) */
|
*/
|
||||||
pktbuf=(char*)pkt;
|
pktbuf = (char *)pkt;
|
||||||
pkthdrbuf=(char*)pkthdr;
|
pkthdrbuf = (char *)pkthdr;
|
||||||
memcpy(pktbuf, pkthdrbuf, ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto));
|
memcpy(pktbuf, pkthdrbuf, ARC_HDR_SIZE + sizeof(pkt->soft.cap.proto));
|
||||||
memcpy(pktbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto)+sizeof(int),
|
memcpy(pktbuf + ARC_HDR_SIZE + sizeof(pkt->soft.cap.proto) + sizeof(int),
|
||||||
pkthdrbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto),
|
pkthdrbuf + ARC_HDR_SIZE + sizeof(pkt->soft.cap.proto),
|
||||||
sizeof(struct archdr)-ARC_HDR_SIZE-sizeof(pkt->soft.cap.proto));
|
sizeof(struct archdr) - ARC_HDR_SIZE - sizeof(pkt->soft.cap.proto));
|
||||||
|
|
||||||
if (length > sizeof(pkt->soft))
|
if (length > sizeof(pkt->soft))
|
||||||
lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
|
lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
|
||||||
@ -84,15 +85,14 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
+ sizeof(int),
|
+ sizeof(int),
|
||||||
length - sizeof(pkt->soft));
|
length - sizeof(pkt->soft));
|
||||||
|
|
||||||
BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
|
if (BUGLVL(D_SKB))
|
||||||
|
arcnet_dump_skb(dev, skb, "rx");
|
||||||
|
|
||||||
skb->protocol = cpu_to_be16(ETH_P_ARCNET);
|
skb->protocol = cpu_to_be16(ETH_P_ARCNET);
|
||||||
netif_rx(skb);
|
netif_rx(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Create the ARCnet hard/soft headers for cap mode.
|
||||||
/*
|
|
||||||
* Create the ARCnet hard/soft headers for cap mode.
|
|
||||||
* There aren't any soft headers in cap mode - not even the protocol id.
|
* There aren't any soft headers in cap mode - not even the protocol id.
|
||||||
*/
|
*/
|
||||||
static int build_header(struct sk_buff *skb,
|
static int build_header(struct sk_buff *skb,
|
||||||
@ -101,12 +101,12 @@ static int build_header(struct sk_buff *skb,
|
|||||||
uint8_t daddr)
|
uint8_t daddr)
|
||||||
{
|
{
|
||||||
int hdr_size = ARC_HDR_SIZE;
|
int hdr_size = ARC_HDR_SIZE;
|
||||||
struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
|
struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size);
|
||||||
|
|
||||||
BUGMSG(D_PROTO, "Preparing header for cap packet %x.\n",
|
arc_printk(D_PROTO, dev, "Preparing header for cap packet %x.\n",
|
||||||
*((int*)&pkt->soft.cap.cookie[0]));
|
*((int *)&pkt->soft.cap.cookie[0]));
|
||||||
/*
|
|
||||||
* Set the source hardware address.
|
/* Set the source hardware address.
|
||||||
*
|
*
|
||||||
* This is pretty pointless for most purposes, but it can help in
|
* This is pretty pointless for most purposes, but it can help in
|
||||||
* debugging. ARCnet does not allow us to change the source address in
|
* debugging. ARCnet does not allow us to change the source address in
|
||||||
@ -117,9 +117,8 @@ static int build_header(struct sk_buff *skb,
|
|||||||
/* see linux/net/ethernet/eth.c to see where I got the following */
|
/* see linux/net/ethernet/eth.c to see where I got the following */
|
||||||
|
|
||||||
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
|
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
|
||||||
/*
|
/* FIXME: fill in the last byte of the dest ipaddr here to
|
||||||
* FIXME: fill in the last byte of the dest ipaddr here to better
|
* better comply with RFC1051 in "noarp" mode.
|
||||||
* comply with RFC1051 in "noarp" mode.
|
|
||||||
*/
|
*/
|
||||||
pkt->hard.dest = 0;
|
pkt->hard.dest = 0;
|
||||||
return hdr_size;
|
return hdr_size;
|
||||||
@ -130,7 +129,6 @@ static int build_header(struct sk_buff *skb,
|
|||||||
return hdr_size; /* success */
|
return hdr_size; /* success */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
||||||
int bufnum)
|
int bufnum)
|
||||||
{
|
{
|
||||||
@ -138,22 +136,21 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|||||||
struct arc_hardware *hard = &pkt->hard;
|
struct arc_hardware *hard = &pkt->hard;
|
||||||
int ofs;
|
int ofs;
|
||||||
|
|
||||||
|
|
||||||
/* hard header is not included in packet length */
|
/* hard header is not included in packet length */
|
||||||
length -= ARC_HDR_SIZE;
|
length -= ARC_HDR_SIZE;
|
||||||
/* And neither is the cookie field */
|
/* And neither is the cookie field */
|
||||||
length -= sizeof(int);
|
length -= sizeof(int);
|
||||||
|
|
||||||
BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
|
arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n",
|
||||||
lp->next_tx, lp->cur_tx, bufnum);
|
lp->next_tx, lp->cur_tx, bufnum);
|
||||||
|
|
||||||
BUGMSG(D_PROTO, "Sending for cap packet %x.\n",
|
arc_printk(D_PROTO, dev, "Sending for cap packet %x.\n",
|
||||||
*((int*)&pkt->soft.cap.cookie[0]));
|
*((int *)&pkt->soft.cap.cookie[0]));
|
||||||
|
|
||||||
if (length > XMTU) {
|
if (length > XMTU) {
|
||||||
/* should never happen! other people already check for this. */
|
/* should never happen! other people already check for this. */
|
||||||
BUGMSG(D_NORMAL, "Bug! prepare_tx with size %d (> %d)\n",
|
arc_printk(D_NORMAL, dev, "Bug! prepare_tx with size %d (> %d)\n",
|
||||||
length, XMTU);
|
length, XMTU);
|
||||||
length = XMTU;
|
length = XMTU;
|
||||||
}
|
}
|
||||||
if (length > MinTU) {
|
if (length > MinTU) {
|
||||||
@ -162,11 +159,12 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|||||||
} else if (length > MTU) {
|
} else if (length > MTU) {
|
||||||
hard->offset[0] = 0;
|
hard->offset[0] = 0;
|
||||||
hard->offset[1] = ofs = 512 - length - 3;
|
hard->offset[1] = ofs = 512 - length - 3;
|
||||||
} else
|
} else {
|
||||||
hard->offset[0] = ofs = 256 - length;
|
hard->offset[0] = ofs = 256 - length;
|
||||||
|
}
|
||||||
|
|
||||||
BUGMSG(D_DURING, "prepare_tx: length=%d ofs=%d\n",
|
arc_printk(D_DURING, dev, "prepare_tx: length=%d ofs=%d\n",
|
||||||
length,ofs);
|
length, ofs);
|
||||||
|
|
||||||
/* Copy the arcnet-header + the protocol byte down: */
|
/* Copy the arcnet-header + the protocol byte down: */
|
||||||
lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
|
lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
|
||||||
@ -174,9 +172,10 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|||||||
sizeof(pkt->soft.cap.proto));
|
sizeof(pkt->soft.cap.proto));
|
||||||
|
|
||||||
/* Skip the extra integer we have written into it as a cookie
|
/* Skip the extra integer we have written into it as a cookie
|
||||||
but write the rest of the message: */
|
* but write the rest of the message:
|
||||||
lp->hw.copy_to_card(dev, bufnum, ofs+1,
|
*/
|
||||||
((unsigned char*)&pkt->soft.cap.mes),length-1);
|
lp->hw.copy_to_card(dev, bufnum, ofs + 1,
|
||||||
|
((unsigned char *)&pkt->soft.cap.mes), length - 1);
|
||||||
|
|
||||||
lp->lastload_dest = hard->dest;
|
lp->lastload_dest = hard->dest;
|
||||||
|
|
||||||
@ -188,21 +187,20 @@ static int ack_tx(struct net_device *dev, int acked)
|
|||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
struct sk_buff *ackskb;
|
struct sk_buff *ackskb;
|
||||||
struct archdr *ackpkt;
|
struct archdr *ackpkt;
|
||||||
int length=sizeof(struct arc_cap);
|
int length = sizeof(struct arc_cap);
|
||||||
|
|
||||||
BUGMSG(D_DURING, "capmode: ack_tx: protocol: %x: result: %d\n",
|
arc_printk(D_DURING, dev, "capmode: ack_tx: protocol: %x: result: %d\n",
|
||||||
lp->outgoing.skb->protocol, acked);
|
lp->outgoing.skb->protocol, acked);
|
||||||
|
|
||||||
BUGLVL(D_SKB) arcnet_dump_skb(dev, lp->outgoing.skb, "ack_tx");
|
if (BUGLVL(D_SKB))
|
||||||
|
arcnet_dump_skb(dev, lp->outgoing.skb, "ack_tx");
|
||||||
|
|
||||||
/* Now alloc a skb to send back up through the layers: */
|
/* Now alloc a skb to send back up through the layers: */
|
||||||
ackskb = alloc_skb(length + ARC_HDR_SIZE , GFP_ATOMIC);
|
ackskb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
|
||||||
if (ackskb == NULL) {
|
if (!ackskb)
|
||||||
BUGMSG(D_NORMAL, "Memory squeeze, can't acknowledge.\n");
|
|
||||||
goto free_outskb;
|
goto free_outskb;
|
||||||
}
|
|
||||||
|
|
||||||
skb_put(ackskb, length + ARC_HDR_SIZE );
|
skb_put(ackskb, length + ARC_HDR_SIZE);
|
||||||
ackskb->dev = dev;
|
ackskb->dev = dev;
|
||||||
|
|
||||||
skb_reset_mac_header(ackskb);
|
skb_reset_mac_header(ackskb);
|
||||||
@ -212,39 +210,40 @@ static int ack_tx(struct net_device *dev, int acked)
|
|||||||
skb_copy_from_linear_data(lp->outgoing.skb, ackpkt,
|
skb_copy_from_linear_data(lp->outgoing.skb, ackpkt,
|
||||||
ARC_HDR_SIZE + sizeof(struct arc_cap));
|
ARC_HDR_SIZE + sizeof(struct arc_cap));
|
||||||
ackpkt->soft.cap.proto = 0; /* using protocol 0 for acknowledge */
|
ackpkt->soft.cap.proto = 0; /* using protocol 0 for acknowledge */
|
||||||
ackpkt->soft.cap.mes.ack=acked;
|
ackpkt->soft.cap.mes.ack = acked;
|
||||||
|
|
||||||
BUGMSG(D_PROTO, "Ackknowledge for cap packet %x.\n",
|
arc_printk(D_PROTO, dev, "Ackknowledge for cap packet %x.\n",
|
||||||
*((int*)&ackpkt->soft.cap.cookie[0]));
|
*((int *)&ackpkt->soft.cap.cookie[0]));
|
||||||
|
|
||||||
ackskb->protocol = cpu_to_be16(ETH_P_ARCNET);
|
ackskb->protocol = cpu_to_be16(ETH_P_ARCNET);
|
||||||
|
|
||||||
BUGLVL(D_SKB) arcnet_dump_skb(dev, ackskb, "ack_tx_recv");
|
if (BUGLVL(D_SKB))
|
||||||
|
arcnet_dump_skb(dev, ackskb, "ack_tx_recv");
|
||||||
netif_rx(ackskb);
|
netif_rx(ackskb);
|
||||||
|
|
||||||
free_outskb:
|
free_outskb:
|
||||||
dev_kfree_skb_irq(lp->outgoing.skb);
|
dev_kfree_skb_irq(lp->outgoing.skb);
|
||||||
lp->outgoing.proto = NULL; /* We are always finished when in this protocol */
|
lp->outgoing.proto = NULL;
|
||||||
|
/* We are always finished when in this protocol */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ArcProto capmode_proto =
|
static struct ArcProto capmode_proto = {
|
||||||
{
|
.suffix = 'r',
|
||||||
'r',
|
.mtu = XMTU,
|
||||||
XMTU,
|
.rx = rx,
|
||||||
0,
|
.build_header = build_header,
|
||||||
rx,
|
.prepare_tx = prepare_tx,
|
||||||
build_header,
|
.ack_tx = ack_tx
|
||||||
prepare_tx,
|
|
||||||
NULL,
|
|
||||||
ack_tx
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void arcnet_cap_init(void)
|
static int __init capmode_module_init(void)
|
||||||
{
|
{
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
|
pr_info("cap mode (`c') encapsulation support loaded\n");
|
||||||
|
|
||||||
for (count = 1; count <= 8; count++)
|
for (count = 1; count <= 8; count++)
|
||||||
if (arc_proto_map[count] == arc_proto_default)
|
if (arc_proto_map[count] == arc_proto_default)
|
||||||
arc_proto_map[count] = &capmode_proto;
|
arc_proto_map[count] = &capmode_proto;
|
||||||
@ -255,12 +254,7 @@ static void arcnet_cap_init(void)
|
|||||||
|
|
||||||
arc_proto_default = &capmode_proto;
|
arc_proto_default = &capmode_proto;
|
||||||
arc_raw_proto = &capmode_proto;
|
arc_raw_proto = &capmode_proto;
|
||||||
}
|
|
||||||
|
|
||||||
static int __init capmode_module_init(void)
|
|
||||||
{
|
|
||||||
printk(VERSION);
|
|
||||||
arcnet_cap_init();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - COM20020 chipset support
|
* Linux ARCnet driver - COM20020 chipset support
|
||||||
*
|
*
|
||||||
* Written 1997 by David Woodhouse.
|
* Written 1997 by David Woodhouse.
|
||||||
* Written 1994-1999 by Avery Pennarun.
|
* Written 1994-1999 by Avery Pennarun.
|
||||||
* Written 1999-2000 by Martin Mares <mj@ucw.cz>.
|
* Written 1999-2000 by Martin Mares <mj@ucw.cz>.
|
||||||
@ -25,6 +25,9 @@
|
|||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@ -36,16 +39,12 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/bootmem.h>
|
#include <linux/bootmem.h>
|
||||||
#include <linux/arcdevice.h>
|
#include <linux/io.h>
|
||||||
#include <linux/com20020.h>
|
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include "arcdevice.h"
|
||||||
|
#include "com20020.h"
|
||||||
|
|
||||||
#define VERSION "arcnet: COM20020 ISA support (by David Woodhouse et al.)\n"
|
/* We cannot (yet) probe for an IO mapped card, although we can check that
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We cannot (yet) probe for an IO mapped card, although we can check that
|
|
||||||
* it's where we were told it was, and even do autoirq.
|
* it's where we were told it was, and even do autoirq.
|
||||||
*/
|
*/
|
||||||
static int __init com20020isa_probe(struct net_device *dev)
|
static int __init com20020isa_probe(struct net_device *dev)
|
||||||
@ -55,21 +54,21 @@ static int __init com20020isa_probe(struct net_device *dev)
|
|||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BUGLVL(D_NORMAL) printk(VERSION);
|
if (BUGLVL(D_NORMAL))
|
||||||
|
pr_info("%s\n", "COM20020 ISA support (by David Woodhouse et al.)");
|
||||||
|
|
||||||
ioaddr = dev->base_addr;
|
ioaddr = dev->base_addr;
|
||||||
if (!ioaddr) {
|
if (!ioaddr) {
|
||||||
BUGMSG(D_NORMAL, "No autoprobe (yet) for IO mapped cards; you "
|
arc_printk(D_NORMAL, dev, "No autoprobe (yet) for IO mapped cards; you must specify the base address!\n");
|
||||||
"must specify the base address!\n");
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "arcnet (COM20020)")) {
|
if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "arcnet (COM20020)")) {
|
||||||
BUGMSG(D_NORMAL, "IO region %xh-%xh already allocated.\n",
|
arc_printk(D_NORMAL, dev, "IO region %xh-%xh already allocated.\n",
|
||||||
ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
|
ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
if (ASTATUS() == 0xFF) {
|
if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
|
||||||
BUGMSG(D_NORMAL, "IO address %x empty\n", ioaddr);
|
arc_printk(D_NORMAL, dev, "IO address %x empty\n", ioaddr);
|
||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -83,23 +82,24 @@ static int __init com20020isa_probe(struct net_device *dev)
|
|||||||
* card has just reset and the NORXflag is on until
|
* card has just reset and the NORXflag is on until
|
||||||
* we tell it to start receiving.
|
* we tell it to start receiving.
|
||||||
*/
|
*/
|
||||||
BUGMSG(D_INIT_REASONS, "intmask was %02Xh\n", inb(_INTMASK));
|
arc_printk(D_INIT_REASONS, dev, "intmask was %02Xh\n",
|
||||||
outb(0, _INTMASK);
|
arcnet_inb(ioaddr, COM20020_REG_R_STATUS));
|
||||||
|
arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK);
|
||||||
airqmask = probe_irq_on();
|
airqmask = probe_irq_on();
|
||||||
outb(NORXflag, _INTMASK);
|
arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK);
|
||||||
udelay(1);
|
udelay(1);
|
||||||
outb(0, _INTMASK);
|
arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK);
|
||||||
dev->irq = probe_irq_off(airqmask);
|
dev->irq = probe_irq_off(airqmask);
|
||||||
|
|
||||||
if ((int)dev->irq <= 0) {
|
if ((int)dev->irq <= 0) {
|
||||||
BUGMSG(D_INIT_REASONS, "Autoprobe IRQ failed first time\n");
|
arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed first time\n");
|
||||||
airqmask = probe_irq_on();
|
airqmask = probe_irq_on();
|
||||||
outb(NORXflag, _INTMASK);
|
arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK);
|
||||||
udelay(5);
|
udelay(5);
|
||||||
outb(0, _INTMASK);
|
arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK);
|
||||||
dev->irq = probe_irq_off(airqmask);
|
dev->irq = probe_irq_off(airqmask);
|
||||||
if ((int)dev->irq <= 0) {
|
if ((int)dev->irq <= 0) {
|
||||||
BUGMSG(D_NORMAL, "Autoprobe IRQ failed.\n");
|
arc_printk(D_NORMAL, dev, "Autoprobe IRQ failed.\n");
|
||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -107,7 +107,9 @@ static int __init com20020isa_probe(struct net_device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
lp->card_name = "ISA COM20020";
|
lp->card_name = "ISA COM20020";
|
||||||
if ((err = com20020_found(dev, 0)) != 0)
|
|
||||||
|
err = com20020_found(dev, 0);
|
||||||
|
if (err != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -194,7 +196,7 @@ static int __init com20020isa_setup(char *s)
|
|||||||
|
|
||||||
switch (ints[0]) {
|
switch (ints[0]) {
|
||||||
default: /* ERROR */
|
default: /* ERROR */
|
||||||
printk("com90xx: Too many arguments.\n");
|
pr_info("Too many arguments\n");
|
||||||
case 6: /* Timeout */
|
case 6: /* Timeout */
|
||||||
timeout = ints[6];
|
timeout = ints[6];
|
||||||
case 5: /* CKP value */
|
case 5: /* CKP value */
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - COM20020 PCI support
|
* Linux ARCnet driver - COM20020 PCI support
|
||||||
* Contemporary Controls PCI20 and SOHARD SH-ARC PCI
|
* Contemporary Controls PCI20 and SOHARD SH-ARC PCI
|
||||||
*
|
*
|
||||||
* Written 1994-1999 by Avery Pennarun,
|
* Written 1994-1999 by Avery Pennarun,
|
||||||
* based on an ISA version by David Woodhouse.
|
* based on an ISA version by David Woodhouse.
|
||||||
* Written 1999-2000 by Martin Mares <mj@ucw.cz>.
|
* Written 1999-2000 by Martin Mares <mj@ucw.cz>.
|
||||||
@ -26,6 +26,9 @@
|
|||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@ -36,14 +39,12 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/arcdevice.h>
|
|
||||||
#include <linux/com20020.h>
|
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include "arcdevice.h"
|
||||||
|
#include "com20020.h"
|
||||||
|
|
||||||
#define VERSION "arcnet: COM20020 PCI support\n"
|
|
||||||
|
|
||||||
/* Module parameters */
|
/* Module parameters */
|
||||||
|
|
||||||
@ -62,11 +63,43 @@ module_param(clockp, int, 0);
|
|||||||
module_param(clockm, int, 0);
|
module_param(clockm, int, 0);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
static void led_tx_set(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
struct com20020_dev *card;
|
||||||
|
struct com20020_priv *priv;
|
||||||
|
struct com20020_pci_card_info *ci;
|
||||||
|
|
||||||
|
card = container_of(led_cdev, struct com20020_dev, tx_led);
|
||||||
|
|
||||||
|
priv = card->pci_priv;
|
||||||
|
ci = priv->ci;
|
||||||
|
|
||||||
|
outb(!!value, priv->misc + ci->leds[card->index].green);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void led_recon_set(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
struct com20020_dev *card;
|
||||||
|
struct com20020_priv *priv;
|
||||||
|
struct com20020_pci_card_info *ci;
|
||||||
|
|
||||||
|
card = container_of(led_cdev, struct com20020_dev, recon_led);
|
||||||
|
|
||||||
|
priv = card->pci_priv;
|
||||||
|
ci = priv->ci;
|
||||||
|
|
||||||
|
outb(!!value, priv->misc + ci->leds[card->index].red);
|
||||||
|
}
|
||||||
|
|
||||||
static void com20020pci_remove(struct pci_dev *pdev);
|
static void com20020pci_remove(struct pci_dev *pdev);
|
||||||
|
|
||||||
static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
static int com20020pci_probe(struct pci_dev *pdev,
|
||||||
|
const struct pci_device_id *id)
|
||||||
{
|
{
|
||||||
struct com20020_pci_card_info *ci;
|
struct com20020_pci_card_info *ci;
|
||||||
|
struct com20020_pci_channel_map *mm;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
struct arcnet_local *lp;
|
struct arcnet_local *lp;
|
||||||
struct com20020_priv *priv;
|
struct com20020_priv *priv;
|
||||||
@ -83,9 +116,21 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
|
|||||||
|
|
||||||
ci = (struct com20020_pci_card_info *)id->driver_data;
|
ci = (struct com20020_pci_card_info *)id->driver_data;
|
||||||
priv->ci = ci;
|
priv->ci = ci;
|
||||||
|
mm = &ci->misc_map;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&priv->list_dev);
|
INIT_LIST_HEAD(&priv->list_dev);
|
||||||
|
|
||||||
|
if (mm->size) {
|
||||||
|
ioaddr = pci_resource_start(pdev, mm->bar) + mm->offset;
|
||||||
|
r = devm_request_region(&pdev->dev, ioaddr, mm->size,
|
||||||
|
"com20020-pci");
|
||||||
|
if (!r) {
|
||||||
|
pr_err("IO region %xh-%xh already allocated.\n",
|
||||||
|
ioaddr, ioaddr + mm->size - 1);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
priv->misc = ioaddr;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ci->devcount; i++) {
|
for (i = 0; i < ci->devcount; i++) {
|
||||||
struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i];
|
struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i];
|
||||||
@ -96,18 +141,19 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
|
|||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_port;
|
goto out_port;
|
||||||
}
|
}
|
||||||
|
dev->dev_port = i;
|
||||||
|
|
||||||
dev->netdev_ops = &com20020_netdev_ops;
|
dev->netdev_ops = &com20020_netdev_ops;
|
||||||
|
|
||||||
lp = netdev_priv(dev);
|
lp = netdev_priv(dev);
|
||||||
|
|
||||||
BUGMSG(D_NORMAL, "%s Controls\n", ci->name);
|
arc_printk(D_NORMAL, dev, "%s Controls\n", ci->name);
|
||||||
ioaddr = pci_resource_start(pdev, cm->bar) + cm->offset;
|
ioaddr = pci_resource_start(pdev, cm->bar) + cm->offset;
|
||||||
|
|
||||||
r = devm_request_region(&pdev->dev, ioaddr, cm->size,
|
r = devm_request_region(&pdev->dev, ioaddr, cm->size,
|
||||||
"com20020-pci");
|
"com20020-pci");
|
||||||
if (!r) {
|
if (!r) {
|
||||||
pr_err("IO region %xh-%xh already allocated.\n",
|
pr_err("IO region %xh-%xh already allocated\n",
|
||||||
ioaddr, ioaddr + cm->size - 1);
|
ioaddr, ioaddr + cm->size - 1);
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto out_port;
|
goto out_port;
|
||||||
@ -117,8 +163,8 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
|
|||||||
* ARCNET controller needs
|
* ARCNET controller needs
|
||||||
* this access to detect bustype
|
* this access to detect bustype
|
||||||
*/
|
*/
|
||||||
outb(0x00, ioaddr + 1);
|
arcnet_outb(0x00, ioaddr, COM20020_REG_W_COMMAND);
|
||||||
inb(ioaddr + 1);
|
arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT);
|
||||||
|
|
||||||
dev->base_addr = ioaddr;
|
dev->base_addr = ioaddr;
|
||||||
dev->dev_addr[0] = node;
|
dev->dev_addr[0] = node;
|
||||||
@ -131,7 +177,14 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
|
|||||||
lp->timeout = timeout;
|
lp->timeout = timeout;
|
||||||
lp->hw.owner = THIS_MODULE;
|
lp->hw.owner = THIS_MODULE;
|
||||||
|
|
||||||
if (ASTATUS() == 0xFF) {
|
/* Get the dev_id from the PLX rotary coder */
|
||||||
|
if (!strncmp(ci->name, "EAE PLX-PCI MA1", 15))
|
||||||
|
dev->dev_id = 0xc;
|
||||||
|
dev->dev_id ^= inb(priv->misc + ci->rotary) >> 4;
|
||||||
|
|
||||||
|
snprintf(dev->name, sizeof(dev->name), "arc%d-%d", dev->dev_id, i);
|
||||||
|
|
||||||
|
if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
|
||||||
pr_err("IO address %Xh is empty!\n", ioaddr);
|
pr_err("IO address %Xh is empty!\n", ioaddr);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto out_port;
|
goto out_port;
|
||||||
@ -143,21 +196,46 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
|
|||||||
|
|
||||||
card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev),
|
card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!card) {
|
if (!card)
|
||||||
pr_err("%s out of memory!\n", __func__);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
card->index = i;
|
card->index = i;
|
||||||
card->pci_priv = priv;
|
card->pci_priv = priv;
|
||||||
|
card->tx_led.brightness_set = led_tx_set;
|
||||||
|
card->tx_led.default_trigger = devm_kasprintf(&pdev->dev,
|
||||||
|
GFP_KERNEL, "arc%d-%d-tx",
|
||||||
|
dev->dev_id, i);
|
||||||
|
card->tx_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
|
||||||
|
"pci:green:tx:%d-%d",
|
||||||
|
dev->dev_id, i);
|
||||||
|
|
||||||
|
card->tx_led.dev = &dev->dev;
|
||||||
|
card->recon_led.brightness_set = led_recon_set;
|
||||||
|
card->recon_led.default_trigger = devm_kasprintf(&pdev->dev,
|
||||||
|
GFP_KERNEL, "arc%d-%d-recon",
|
||||||
|
dev->dev_id, i);
|
||||||
|
card->recon_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
|
||||||
|
"pci:red:recon:%d-%d",
|
||||||
|
dev->dev_id, i);
|
||||||
|
card->recon_led.dev = &dev->dev;
|
||||||
card->dev = dev;
|
card->dev = dev;
|
||||||
|
|
||||||
|
ret = devm_led_classdev_register(&pdev->dev, &card->tx_led);
|
||||||
|
if (ret)
|
||||||
|
goto out_port;
|
||||||
|
|
||||||
|
ret = devm_led_classdev_register(&pdev->dev, &card->recon_led);
|
||||||
|
if (ret)
|
||||||
|
goto out_port;
|
||||||
|
|
||||||
dev_set_drvdata(&dev->dev, card);
|
dev_set_drvdata(&dev->dev, card);
|
||||||
|
|
||||||
ret = com20020_found(dev, IRQF_SHARED);
|
ret = com20020_found(dev, IRQF_SHARED);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_port;
|
goto out_port;
|
||||||
|
|
||||||
|
devm_arcnet_led_init(dev, dev->dev_id, i);
|
||||||
|
|
||||||
list_add(&card->list, &priv->list_dev);
|
list_add(&card->list, &priv->list_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +268,11 @@ static struct com20020_pci_card_info card_info_10mbit = {
|
|||||||
.name = "ARC-PCI",
|
.name = "ARC-PCI",
|
||||||
.devcount = 1,
|
.devcount = 1,
|
||||||
.chan_map_tbl = {
|
.chan_map_tbl = {
|
||||||
{ 2, 0x00, 0x08 },
|
{
|
||||||
|
.bar = 2,
|
||||||
|
.offset = 0x00,
|
||||||
|
.size = 0x08,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
.flags = ARC_CAN_10MBIT,
|
.flags = ARC_CAN_10MBIT,
|
||||||
};
|
};
|
||||||
@ -199,7 +281,11 @@ static struct com20020_pci_card_info card_info_5mbit = {
|
|||||||
.name = "ARC-PCI",
|
.name = "ARC-PCI",
|
||||||
.devcount = 1,
|
.devcount = 1,
|
||||||
.chan_map_tbl = {
|
.chan_map_tbl = {
|
||||||
{ 2, 0x00, 0x08 },
|
{
|
||||||
|
.bar = 2,
|
||||||
|
.offset = 0x00,
|
||||||
|
.size = 0x08,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
.flags = ARC_IS_5MBIT,
|
.flags = ARC_IS_5MBIT,
|
||||||
};
|
};
|
||||||
@ -209,7 +295,11 @@ static struct com20020_pci_card_info card_info_sohard = {
|
|||||||
.devcount = 1,
|
.devcount = 1,
|
||||||
/* SOHARD needs PCI base addr 4 */
|
/* SOHARD needs PCI base addr 4 */
|
||||||
.chan_map_tbl = {
|
.chan_map_tbl = {
|
||||||
{4, 0x00, 0x08},
|
{
|
||||||
|
.bar = 4,
|
||||||
|
.offset = 0x00,
|
||||||
|
.size = 0x08
|
||||||
|
},
|
||||||
},
|
},
|
||||||
.flags = ARC_CAN_10MBIT,
|
.flags = ARC_CAN_10MBIT,
|
||||||
};
|
};
|
||||||
@ -218,8 +308,24 @@ static struct com20020_pci_card_info card_info_eae_arc1 = {
|
|||||||
.name = "EAE PLX-PCI ARC1",
|
.name = "EAE PLX-PCI ARC1",
|
||||||
.devcount = 1,
|
.devcount = 1,
|
||||||
.chan_map_tbl = {
|
.chan_map_tbl = {
|
||||||
{ 2, 0x00, 0x08 },
|
{
|
||||||
|
.bar = 2,
|
||||||
|
.offset = 0x00,
|
||||||
|
.size = 0x08,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
.misc_map = {
|
||||||
|
.bar = 2,
|
||||||
|
.offset = 0x10,
|
||||||
|
.size = 0x04,
|
||||||
|
},
|
||||||
|
.leds = {
|
||||||
|
{
|
||||||
|
.green = 0x0,
|
||||||
|
.red = 0x1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.rotary = 0x0,
|
||||||
.flags = ARC_CAN_10MBIT,
|
.flags = ARC_CAN_10MBIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -227,9 +333,31 @@ static struct com20020_pci_card_info card_info_eae_ma1 = {
|
|||||||
.name = "EAE PLX-PCI MA1",
|
.name = "EAE PLX-PCI MA1",
|
||||||
.devcount = 2,
|
.devcount = 2,
|
||||||
.chan_map_tbl = {
|
.chan_map_tbl = {
|
||||||
{ 2, 0x00, 0x08 },
|
{
|
||||||
{ 2, 0x08, 0x08 }
|
.bar = 2,
|
||||||
|
.offset = 0x00,
|
||||||
|
.size = 0x08,
|
||||||
|
}, {
|
||||||
|
.bar = 2,
|
||||||
|
.offset = 0x08,
|
||||||
|
.size = 0x08,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
.misc_map = {
|
||||||
|
.bar = 2,
|
||||||
|
.offset = 0x10,
|
||||||
|
.size = 0x04,
|
||||||
|
},
|
||||||
|
.leds = {
|
||||||
|
{
|
||||||
|
.green = 0x0,
|
||||||
|
.red = 0x1,
|
||||||
|
}, {
|
||||||
|
.green = 0x2,
|
||||||
|
.red = 0x3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.rotary = 0x0,
|
||||||
.flags = ARC_CAN_10MBIT,
|
.flags = ARC_CAN_10MBIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -404,7 +532,8 @@ static struct pci_driver com20020pci_driver = {
|
|||||||
|
|
||||||
static int __init com20020pci_init(void)
|
static int __init com20020pci_init(void)
|
||||||
{
|
{
|
||||||
BUGLVL(D_NORMAL) printk(VERSION);
|
if (BUGLVL(D_NORMAL))
|
||||||
|
pr_info("%s\n", "COM20020 PCI support");
|
||||||
return pci_register_driver(&com20020pci_driver);
|
return pci_register_driver(&com20020pci_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - COM20020 chipset support
|
* Linux ARCnet driver - COM20020 chipset support
|
||||||
*
|
*
|
||||||
* Written 1997 by David Woodhouse.
|
* Written 1997 by David Woodhouse.
|
||||||
* Written 1994-1999 by Avery Pennarun.
|
* Written 1994-1999 by Avery Pennarun.
|
||||||
* Written 1999 by Martin Mares <mj@ucw.cz>.
|
* Written 1999 by Martin Mares <mj@ucw.cz>.
|
||||||
@ -25,6 +25,9 @@
|
|||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
@ -34,17 +37,16 @@
|
|||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/arcdevice.h>
|
#include <linux/io.h>
|
||||||
#include <linux/com20020.h>
|
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include "arcdevice.h"
|
||||||
|
#include "com20020.h"
|
||||||
|
|
||||||
#define VERSION "arcnet: COM20020 chipset support (by David Woodhouse et al.)\n"
|
static const char * const clockrates[] = {
|
||||||
|
"XXXXXXX", "XXXXXXXX", "XXXXXX", "2.5 Mb/s",
|
||||||
static char *clockrates[] =
|
"1.25Mb/s", "625 Kb/s", "312.5 Kb/s", "156.25 Kb/s",
|
||||||
{"10 Mb/s", "Reserved", "5 Mb/s",
|
"Reserved", "Reserved", "Reserved"
|
||||||
"2.5 Mb/s", "1.25Mb/s", "625 Kb/s", "312.5 Kb/s",
|
};
|
||||||
"156.25 Kb/s", "Reserved", "Reserved", "Reserved"};
|
|
||||||
|
|
||||||
static void com20020_command(struct net_device *dev, int command);
|
static void com20020_command(struct net_device *dev, int command);
|
||||||
static int com20020_status(struct net_device *dev);
|
static int com20020_status(struct net_device *dev);
|
||||||
@ -63,35 +65,38 @@ static void com20020_copy_from_card(struct net_device *dev, int bufnum,
|
|||||||
int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;
|
int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;
|
||||||
|
|
||||||
/* set up the address register */
|
/* set up the address register */
|
||||||
outb((ofs >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
|
arcnet_outb((ofs >> 8) | RDDATAflag | AUTOINCflag,
|
||||||
outb(ofs & 0xff, _ADDR_LO);
|
ioaddr, COM20020_REG_W_ADDR_HI);
|
||||||
|
arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
|
||||||
|
|
||||||
/* copy the data */
|
/* copy the data */
|
||||||
TIME("insb", count, insb(_MEMDATA, buf, count));
|
TIME(dev, "insb", count,
|
||||||
|
arcnet_insb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void com20020_copy_to_card(struct net_device *dev, int bufnum,
|
static void com20020_copy_to_card(struct net_device *dev, int bufnum,
|
||||||
int offset, void *buf, int count)
|
int offset, void *buf, int count)
|
||||||
{
|
{
|
||||||
int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;
|
int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;
|
||||||
|
|
||||||
/* set up the address register */
|
/* set up the address register */
|
||||||
outb((ofs >> 8) | AUTOINCflag, _ADDR_HI);
|
arcnet_outb((ofs >> 8) | AUTOINCflag, ioaddr, COM20020_REG_W_ADDR_HI);
|
||||||
outb(ofs & 0xff, _ADDR_LO);
|
arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
|
||||||
|
|
||||||
/* copy the data */
|
/* copy the data */
|
||||||
TIME("outsb", count, outsb(_MEMDATA, buf, count));
|
TIME(dev, "outsb", count,
|
||||||
|
arcnet_outsb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Reset the card and check some basic stuff during the detection stage. */
|
/* Reset the card and check some basic stuff during the detection stage. */
|
||||||
int com20020_check(struct net_device *dev)
|
int com20020_check(struct net_device *dev)
|
||||||
{
|
{
|
||||||
int ioaddr = dev->base_addr, status;
|
int ioaddr = dev->base_addr, status;
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
|
|
||||||
ARCRESET0;
|
arcnet_outb(XTOcfg(3) | RESETcfg, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
|
udelay(5);
|
||||||
|
arcnet_outb(XTOcfg(3), ioaddr, COM20020_REG_W_CONFIG);
|
||||||
mdelay(RESETtime);
|
mdelay(RESETtime);
|
||||||
|
|
||||||
lp->setup = lp->clockm ? 0 : (lp->clockp << 1);
|
lp->setup = lp->clockm ? 0 : (lp->clockp << 1);
|
||||||
@ -101,49 +106,46 @@ int com20020_check(struct net_device *dev)
|
|||||||
/* Enable P1Mode for backplane mode */
|
/* Enable P1Mode for backplane mode */
|
||||||
lp->setup = lp->setup | P1MODE;
|
lp->setup = lp->setup | P1MODE;
|
||||||
|
|
||||||
SET_SUBADR(SUB_SETUP1);
|
com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);
|
||||||
outb(lp->setup, _XREG);
|
arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);
|
||||||
|
|
||||||
|
if (lp->clockm != 0) {
|
||||||
|
com20020_set_subaddress(lp, ioaddr, SUB_SETUP2);
|
||||||
|
arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG);
|
||||||
|
|
||||||
if (lp->clockm != 0)
|
|
||||||
{
|
|
||||||
SET_SUBADR(SUB_SETUP2);
|
|
||||||
outb(lp->setup2, _XREG);
|
|
||||||
|
|
||||||
/* must now write the magic "restart operation" command */
|
/* must now write the magic "restart operation" command */
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
outb(0x18, _COMMAND);
|
arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
lp->config = 0x21 | (lp->timeout << 3) | (lp->backplane << 2);
|
lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;
|
||||||
/* set node ID to 0x42 (but transmitter is disabled, so it's okay) */
|
/* set node ID to 0x42 (but transmitter is disabled, so it's okay) */
|
||||||
SETCONF;
|
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
outb(0x42, ioaddr + BUS_ALIGN*7);
|
arcnet_outb(0x42, ioaddr, COM20020_REG_W_XREG);
|
||||||
|
|
||||||
status = ASTATUS();
|
status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS);
|
||||||
|
|
||||||
if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) {
|
if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) {
|
||||||
BUGMSG(D_NORMAL, "status invalid (%Xh).\n", status);
|
arc_printk(D_NORMAL, dev, "status invalid (%Xh).\n", status);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
BUGMSG(D_INIT_REASONS, "status after reset: %X\n", status);
|
arc_printk(D_INIT_REASONS, dev, "status after reset: %X\n", status);
|
||||||
|
|
||||||
/* Enable TX */
|
arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
|
||||||
outb(0x39, _CONFIG);
|
ioaddr, COM20020_REG_W_COMMAND);
|
||||||
outb(inb(ioaddr + BUS_ALIGN*8), ioaddr + BUS_ALIGN*7);
|
status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS);
|
||||||
|
arc_printk(D_INIT_REASONS, dev, "status after reset acknowledged: %X\n",
|
||||||
ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
|
status);
|
||||||
|
|
||||||
status = ASTATUS();
|
|
||||||
BUGMSG(D_INIT_REASONS, "status after reset acknowledged: %X\n",
|
|
||||||
status);
|
|
||||||
|
|
||||||
/* Read first location of memory */
|
/* Read first location of memory */
|
||||||
outb(0 | RDDATAflag | AUTOINCflag, _ADDR_HI);
|
arcnet_outb(0 | RDDATAflag | AUTOINCflag,
|
||||||
outb(0, _ADDR_LO);
|
ioaddr, COM20020_REG_W_ADDR_HI);
|
||||||
|
arcnet_outb(0, ioaddr, COM20020_REG_W_ADDR_LO);
|
||||||
|
|
||||||
if ((status = inb(_MEMDATA)) != TESTvalue) {
|
status = arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA);
|
||||||
BUGMSG(D_NORMAL, "Signature byte not found (%02Xh != D1h).\n",
|
if (status != TESTvalue) {
|
||||||
status);
|
arc_printk(D_NORMAL, dev, "Signature byte not found (%02Xh != D1h).\n",
|
||||||
|
status);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -156,15 +158,39 @@ static int com20020_set_hwaddr(struct net_device *dev, void *addr)
|
|||||||
struct sockaddr *hwaddr = addr;
|
struct sockaddr *hwaddr = addr;
|
||||||
|
|
||||||
memcpy(dev->dev_addr, hwaddr->sa_data, 1);
|
memcpy(dev->dev_addr, hwaddr->sa_data, 1);
|
||||||
SET_SUBADR(SUB_NODE);
|
com20020_set_subaddress(lp, ioaddr, SUB_NODE);
|
||||||
outb(dev->dev_addr[0], _XREG);
|
arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int com20020_netdev_open(struct net_device *dev)
|
||||||
|
{
|
||||||
|
int ioaddr = dev->base_addr;
|
||||||
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
|
|
||||||
|
lp->config |= TXENcfg;
|
||||||
|
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
|
|
||||||
|
return arcnet_open(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int com20020_netdev_close(struct net_device *dev)
|
||||||
|
{
|
||||||
|
int ioaddr = dev->base_addr;
|
||||||
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
|
|
||||||
|
arcnet_close(dev);
|
||||||
|
|
||||||
|
/* disable transmitter */
|
||||||
|
lp->config &= ~TXENcfg;
|
||||||
|
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const struct net_device_ops com20020_netdev_ops = {
|
const struct net_device_ops com20020_netdev_ops = {
|
||||||
.ndo_open = arcnet_open,
|
.ndo_open = com20020_netdev_open,
|
||||||
.ndo_stop = arcnet_close,
|
.ndo_stop = com20020_netdev_close,
|
||||||
.ndo_start_xmit = arcnet_send_packet,
|
.ndo_start_xmit = arcnet_send_packet,
|
||||||
.ndo_tx_timeout = arcnet_timeout,
|
.ndo_tx_timeout = arcnet_timeout,
|
||||||
.ndo_set_mac_address = com20020_set_hwaddr,
|
.ndo_set_mac_address = com20020_set_hwaddr,
|
||||||
@ -192,48 +218,54 @@ int com20020_found(struct net_device *dev, int shared)
|
|||||||
lp->hw.copy_from_card = com20020_copy_from_card;
|
lp->hw.copy_from_card = com20020_copy_from_card;
|
||||||
lp->hw.close = com20020_close;
|
lp->hw.close = com20020_close;
|
||||||
|
|
||||||
|
/* FIXME: do this some other way! */
|
||||||
if (!dev->dev_addr[0])
|
if (!dev->dev_addr[0])
|
||||||
dev->dev_addr[0] = inb(ioaddr + BUS_ALIGN*8); /* FIXME: do this some other way! */
|
dev->dev_addr[0] = arcnet_inb(ioaddr, 8);
|
||||||
|
|
||||||
SET_SUBADR(SUB_SETUP1);
|
com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);
|
||||||
outb(lp->setup, _XREG);
|
arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);
|
||||||
|
|
||||||
|
if (lp->card_flags & ARC_CAN_10MBIT) {
|
||||||
|
com20020_set_subaddress(lp, ioaddr, SUB_SETUP2);
|
||||||
|
arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG);
|
||||||
|
|
||||||
if (lp->card_flags & ARC_CAN_10MBIT)
|
|
||||||
{
|
|
||||||
SET_SUBADR(SUB_SETUP2);
|
|
||||||
outb(lp->setup2, _XREG);
|
|
||||||
|
|
||||||
/* must now write the magic "restart operation" command */
|
/* must now write the magic "restart operation" command */
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
outb(0x18, _COMMAND);
|
arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
lp->config = 0x20 | (lp->timeout << 3) | (lp->backplane << 2) | 1;
|
lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;
|
||||||
/* Default 0x38 + register: Node ID */
|
/* Default 0x38 + register: Node ID */
|
||||||
SETCONF;
|
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
outb(dev->dev_addr[0], _XREG);
|
arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG);
|
||||||
|
|
||||||
/* reserve the irq */
|
/* reserve the irq */
|
||||||
if (request_irq(dev->irq, arcnet_interrupt, shared,
|
if (request_irq(dev->irq, arcnet_interrupt, shared,
|
||||||
"arcnet (COM20020)", dev)) {
|
"arcnet (COM20020)", dev)) {
|
||||||
BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
|
arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->base_addr = ioaddr;
|
dev->base_addr = ioaddr;
|
||||||
|
|
||||||
BUGMSG(D_NORMAL, "%s: station %02Xh found at %03lXh, IRQ %d.\n",
|
arc_printk(D_NORMAL, dev, "%s: station %02Xh found at %03lXh, IRQ %d.\n",
|
||||||
lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq);
|
lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq);
|
||||||
|
|
||||||
if (lp->backplane)
|
if (lp->backplane)
|
||||||
BUGMSG(D_NORMAL, "Using backplane mode.\n");
|
arc_printk(D_NORMAL, dev, "Using backplane mode.\n");
|
||||||
|
|
||||||
if (lp->timeout != 3)
|
if (lp->timeout != 3)
|
||||||
BUGMSG(D_NORMAL, "Using extended timeout value of %d.\n", lp->timeout);
|
arc_printk(D_NORMAL, dev, "Using extended timeout value of %d\n",
|
||||||
|
lp->timeout);
|
||||||
|
|
||||||
BUGMSG(D_NORMAL, "Using CKP %d - data rate %s.\n",
|
arc_printk(D_NORMAL, dev, "Using CKP %d - data rate %s\n",
|
||||||
lp->setup >> 1,
|
lp->setup >> 1,
|
||||||
clockrates[3 - ((lp->setup2 & 0xF0) >> 4) + ((lp->setup & 0x0F) >> 1)]);
|
clockrates[3 -
|
||||||
|
((lp->setup2 & 0xF0) >> 4) +
|
||||||
|
((lp->setup & 0x0F) >> 1)]);
|
||||||
|
/* The clockrates array index looks very fragile.
|
||||||
|
* It seems like it could have negative indexing.
|
||||||
|
*/
|
||||||
|
|
||||||
if (register_netdev(dev)) {
|
if (register_netdev(dev)) {
|
||||||
free_irq(dev->irq, dev);
|
free_irq(dev->irq, dev);
|
||||||
@ -242,10 +274,8 @@ int com20020_found(struct net_device *dev, int shared)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Do a hardware reset on the card, and set up necessary registers.
|
||||||
/*
|
*
|
||||||
* Do a hardware reset on the card, and set up necessary registers.
|
|
||||||
*
|
|
||||||
* This should be called as little as possible, because it disrupts the
|
* This should be called as little as possible, because it disrupts the
|
||||||
* token on the network (causes a RECON) and requires a significant delay.
|
* token on the network (causes a RECON) and requires a significant delay.
|
||||||
*
|
*
|
||||||
@ -257,65 +287,71 @@ static int com20020_reset(struct net_device *dev, int really_reset)
|
|||||||
u_int ioaddr = dev->base_addr;
|
u_int ioaddr = dev->base_addr;
|
||||||
u_char inbyte;
|
u_char inbyte;
|
||||||
|
|
||||||
BUGMSG(D_DEBUG, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n",
|
arc_printk(D_DEBUG, dev, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n",
|
||||||
__FILE__,__LINE__,__func__,dev,lp,dev->name);
|
__FILE__, __LINE__, __func__, dev, lp, dev->name);
|
||||||
BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n",
|
arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",
|
||||||
dev->name, ASTATUS());
|
dev->name, arcnet_inb(ioaddr, COM20020_REG_R_STATUS));
|
||||||
|
|
||||||
BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
|
arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
|
||||||
lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2);
|
lp->config |= (lp->timeout << 3) | (lp->backplane << 2);
|
||||||
/* power-up defaults */
|
/* power-up defaults */
|
||||||
SETCONF;
|
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
|
arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
|
||||||
|
|
||||||
if (really_reset) {
|
if (really_reset) {
|
||||||
/* reset the card */
|
/* reset the card */
|
||||||
ARCRESET;
|
arcnet_outb(lp->config | RESETcfg, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
mdelay(RESETtime * 2); /* COM20020 seems to be slower sometimes */
|
udelay(5);
|
||||||
|
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
|
mdelay(RESETtime * 2);
|
||||||
|
/* COM20020 seems to be slower sometimes */
|
||||||
}
|
}
|
||||||
/* clear flags & end reset */
|
/* clear flags & end reset */
|
||||||
BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
|
arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
|
||||||
ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
|
arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
|
||||||
|
ioaddr, COM20020_REG_W_COMMAND);
|
||||||
|
|
||||||
/* verify that the ARCnet signature byte is present */
|
/* verify that the ARCnet signature byte is present */
|
||||||
BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
|
arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
|
||||||
|
|
||||||
com20020_copy_from_card(dev, 0, 0, &inbyte, 1);
|
com20020_copy_from_card(dev, 0, 0, &inbyte, 1);
|
||||||
BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
|
arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
|
||||||
if (inbyte != TESTvalue) {
|
if (inbyte != TESTvalue) {
|
||||||
BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
|
arc_printk(D_DEBUG, dev, "%s: %d: %s\n",
|
||||||
BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n");
|
__FILE__, __LINE__, __func__);
|
||||||
|
arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/* enable extended (512-byte) packets */
|
/* enable extended (512-byte) packets */
|
||||||
ACOMMAND(CONFIGcmd | EXTconf);
|
arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM20020_REG_W_COMMAND);
|
||||||
BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
|
|
||||||
|
arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
|
||||||
|
|
||||||
/* done! return success. */
|
/* done! return success. */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void com20020_setmask(struct net_device *dev, int mask)
|
static void com20020_setmask(struct net_device *dev, int mask)
|
||||||
{
|
{
|
||||||
u_int ioaddr = dev->base_addr;
|
u_int ioaddr = dev->base_addr;
|
||||||
BUGMSG(D_DURING, "Setting mask to %x at %x\n",mask,ioaddr);
|
|
||||||
AINTMASK(mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
arc_printk(D_DURING, dev, "Setting mask to %x at %x\n", mask, ioaddr);
|
||||||
|
arcnet_outb(mask, ioaddr, COM20020_REG_W_INTMASK);
|
||||||
|
}
|
||||||
|
|
||||||
static void com20020_command(struct net_device *dev, int cmd)
|
static void com20020_command(struct net_device *dev, int cmd)
|
||||||
{
|
{
|
||||||
u_int ioaddr = dev->base_addr;
|
u_int ioaddr = dev->base_addr;
|
||||||
ACOMMAND(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
arcnet_outb(cmd, ioaddr, COM20020_REG_W_COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
static int com20020_status(struct net_device *dev)
|
static int com20020_status(struct net_device *dev)
|
||||||
{
|
{
|
||||||
u_int ioaddr = dev->base_addr;
|
u_int ioaddr = dev->base_addr;
|
||||||
|
|
||||||
return ASTATUS() + (ADIAGSTATUS()<<8);
|
return arcnet_inb(ioaddr, COM20020_REG_R_STATUS) +
|
||||||
|
(arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT) << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void com20020_close(struct net_device *dev)
|
static void com20020_close(struct net_device *dev)
|
||||||
@ -325,7 +361,7 @@ static void com20020_close(struct net_device *dev)
|
|||||||
|
|
||||||
/* disable transmitter */
|
/* disable transmitter */
|
||||||
lp->config &= ~TXENcfg;
|
lp->config &= ~TXENcfg;
|
||||||
SETCONF;
|
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set or clear the multicast filter for this adaptor.
|
/* Set or clear the multicast filter for this adaptor.
|
||||||
@ -340,20 +376,20 @@ static void com20020_set_mc_list(struct net_device *dev)
|
|||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
int ioaddr = dev->base_addr;
|
int ioaddr = dev->base_addr;
|
||||||
|
|
||||||
if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) { /* Enable promiscuous mode */
|
if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) {
|
||||||
|
/* Enable promiscuous mode */
|
||||||
if (!(lp->setup & PROMISCset))
|
if (!(lp->setup & PROMISCset))
|
||||||
BUGMSG(D_NORMAL, "Setting promiscuous flag...\n");
|
arc_printk(D_NORMAL, dev, "Setting promiscuous flag...\n");
|
||||||
SET_SUBADR(SUB_SETUP1);
|
com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);
|
||||||
lp->setup |= PROMISCset;
|
lp->setup |= PROMISCset;
|
||||||
outb(lp->setup, _XREG);
|
arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);
|
||||||
} else
|
} else {
|
||||||
/* Disable promiscuous mode, use normal mode */
|
/* Disable promiscuous mode, use normal mode */
|
||||||
{
|
|
||||||
if ((lp->setup & PROMISCset))
|
if ((lp->setup & PROMISCset))
|
||||||
BUGMSG(D_NORMAL, "Resetting promiscuous flag...\n");
|
arc_printk(D_NORMAL, dev, "Resetting promiscuous flag...\n");
|
||||||
SET_SUBADR(SUB_SETUP1);
|
com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);
|
||||||
lp->setup &= ~PROMISCset;
|
lp->setup &= ~PROMISCset;
|
||||||
outb(lp->setup, _XREG);
|
arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,7 +407,8 @@ MODULE_LICENSE("GPL");
|
|||||||
|
|
||||||
static int __init com20020_module_init(void)
|
static int __init com20020_module_init(void)
|
||||||
{
|
{
|
||||||
BUGLVL(D_NORMAL) printk(VERSION);
|
if (BUGLVL(D_NORMAL))
|
||||||
|
pr_info("%s\n", "COM20020 chipset support (by David Woodhouse et al.)");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - COM20020 chipset support - function declarations
|
* Linux ARCnet driver - COM20020 chipset support - function declarations
|
||||||
*
|
*
|
||||||
* Written 1997 by David Woodhouse.
|
* Written 1997 by David Woodhouse.
|
||||||
* Written 1994-1999 by Avery Pennarun.
|
* Written 1994-1999 by Avery Pennarun.
|
||||||
* Derived from skeleton.c by Donald Becker.
|
* Derived from skeleton.c by Donald Becker.
|
||||||
@ -26,6 +26,7 @@
|
|||||||
*/
|
*/
|
||||||
#ifndef __COM20020_H
|
#ifndef __COM20020_H
|
||||||
#define __COM20020_H
|
#define __COM20020_H
|
||||||
|
#include <linux/leds.h>
|
||||||
|
|
||||||
int com20020_check(struct net_device *dev);
|
int com20020_check(struct net_device *dev);
|
||||||
int com20020_found(struct net_device *dev, int shared);
|
int com20020_found(struct net_device *dev, int shared);
|
||||||
@ -34,15 +35,13 @@ extern const struct net_device_ops com20020_netdev_ops;
|
|||||||
/* The number of low I/O ports used by the card. */
|
/* The number of low I/O ports used by the card. */
|
||||||
#define ARCNET_TOTAL_SIZE 8
|
#define ARCNET_TOTAL_SIZE 8
|
||||||
|
|
||||||
/* various register addresses */
|
|
||||||
#ifdef CONFIG_SA1100_CT6001
|
|
||||||
#define BUS_ALIGN 2 /* 8 bit device on a 16 bit bus - needs padding */
|
|
||||||
#else
|
|
||||||
#define BUS_ALIGN 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define PLX_PCI_MAX_CARDS 2
|
#define PLX_PCI_MAX_CARDS 2
|
||||||
|
|
||||||
|
struct ledoffsets {
|
||||||
|
int green;
|
||||||
|
int red;
|
||||||
|
};
|
||||||
|
|
||||||
struct com20020_pci_channel_map {
|
struct com20020_pci_channel_map {
|
||||||
u32 bar;
|
u32 bar;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
@ -54,6 +53,10 @@ struct com20020_pci_card_info {
|
|||||||
int devcount;
|
int devcount;
|
||||||
|
|
||||||
struct com20020_pci_channel_map chan_map_tbl[PLX_PCI_MAX_CARDS];
|
struct com20020_pci_channel_map chan_map_tbl[PLX_PCI_MAX_CARDS];
|
||||||
|
struct com20020_pci_channel_map misc_map;
|
||||||
|
|
||||||
|
struct ledoffsets leds[PLX_PCI_MAX_CARDS];
|
||||||
|
int rotary;
|
||||||
|
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
};
|
};
|
||||||
@ -61,27 +64,32 @@ struct com20020_pci_card_info {
|
|||||||
struct com20020_priv {
|
struct com20020_priv {
|
||||||
struct com20020_pci_card_info *ci;
|
struct com20020_pci_card_info *ci;
|
||||||
struct list_head list_dev;
|
struct list_head list_dev;
|
||||||
|
resource_size_t misc;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct com20020_dev {
|
struct com20020_dev {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
|
|
||||||
|
struct led_classdev tx_led;
|
||||||
|
struct led_classdev recon_led;
|
||||||
|
|
||||||
struct com20020_priv *pci_priv;
|
struct com20020_priv *pci_priv;
|
||||||
int index;
|
int index;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define _INTMASK (ioaddr+BUS_ALIGN*0) /* writable */
|
#define COM20020_REG_W_INTMASK 0 /* writable */
|
||||||
#define _STATUS (ioaddr+BUS_ALIGN*0) /* readable */
|
#define COM20020_REG_R_STATUS 0 /* readable */
|
||||||
#define _COMMAND (ioaddr+BUS_ALIGN*1) /* standard arcnet commands */
|
#define COM20020_REG_W_COMMAND 1 /* standard arcnet commands */
|
||||||
#define _DIAGSTAT (ioaddr+BUS_ALIGN*1) /* diagnostic status register */
|
#define COM20020_REG_R_DIAGSTAT 1 /* diagnostic status */
|
||||||
#define _ADDR_HI (ioaddr+BUS_ALIGN*2) /* control registers for IO-mapped memory */
|
#define COM20020_REG_W_ADDR_HI 2 /* control for IO-mapped memory */
|
||||||
#define _ADDR_LO (ioaddr+BUS_ALIGN*3)
|
#define COM20020_REG_W_ADDR_LO 3
|
||||||
#define _MEMDATA (ioaddr+BUS_ALIGN*4) /* data port for IO-mapped memory */
|
#define COM20020_REG_RW_MEMDATA 4 /* data port for IO-mapped memory */
|
||||||
#define _SUBADR (ioaddr+BUS_ALIGN*5) /* the extended port _XREG refers to */
|
#define COM20020_REG_W_SUBADR 5 /* the extended port _XREG refers to */
|
||||||
#define _CONFIG (ioaddr+BUS_ALIGN*6) /* configuration register */
|
#define COM20020_REG_W_CONFIG 6 /* configuration */
|
||||||
#define _XREG (ioaddr+BUS_ALIGN*7) /* extra registers (indexed by _CONFIG
|
#define COM20020_REG_W_XREG 7 /* extra
|
||||||
or _SUBADR) */
|
* (indexed by _CONFIG or _SUBADDR)
|
||||||
|
*/
|
||||||
|
|
||||||
/* in the ADDR_HI register */
|
/* in the ADDR_HI register */
|
||||||
#define RDDATAflag 0x80 /* next access is a read (not a write) */
|
#define RDDATAflag 0x80 /* next access is a read (not a write) */
|
||||||
@ -92,6 +100,7 @@ struct com20020_dev {
|
|||||||
/* in the CONFIG register */
|
/* in the CONFIG register */
|
||||||
#define RESETcfg 0x80 /* put card in reset state */
|
#define RESETcfg 0x80 /* put card in reset state */
|
||||||
#define TXENcfg 0x20 /* enable TX */
|
#define TXENcfg 0x20 /* enable TX */
|
||||||
|
#define XTOcfg(x) ((x) << 3) /* extended timeout */
|
||||||
|
|
||||||
/* in SETUP register */
|
/* in SETUP register */
|
||||||
#define PROMISCset 0x10 /* enable RCV_ALL */
|
#define PROMISCset 0x10 /* enable RCV_ALL */
|
||||||
@ -109,37 +118,15 @@ struct com20020_dev {
|
|||||||
#define SUB_BUSCTL 5 /* bus control options */
|
#define SUB_BUSCTL 5 /* bus control options */
|
||||||
#define SUB_DMACOUNT 6 /* DMA count options */
|
#define SUB_DMACOUNT 6 /* DMA count options */
|
||||||
|
|
||||||
#define SET_SUBADR(x) do { \
|
static inline void com20020_set_subaddress(struct arcnet_local *lp,
|
||||||
if ((x) < 4) \
|
int ioaddr, int val)
|
||||||
{ \
|
{
|
||||||
lp->config = (lp->config & ~0x03) | (x); \
|
if (val < 4) {
|
||||||
SETCONF; \
|
lp->config = (lp->config & ~0x03) | val;
|
||||||
} \
|
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
else \
|
} else {
|
||||||
{ \
|
arcnet_outb(val, ioaddr, COM20020_REG_W_SUBADR);
|
||||||
outb(x, _SUBADR); \
|
}
|
||||||
} \
|
}
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#undef ARCRESET
|
|
||||||
#undef ASTATUS
|
|
||||||
#undef ACOMMAND
|
|
||||||
#undef AINTMASK
|
|
||||||
|
|
||||||
#define ARCRESET { outb(lp->config | 0x80, _CONFIG); \
|
|
||||||
udelay(5); \
|
|
||||||
outb(lp->config , _CONFIG); \
|
|
||||||
}
|
|
||||||
#define ARCRESET0 { outb(0x18 | 0x80, _CONFIG); \
|
|
||||||
udelay(5); \
|
|
||||||
outb(0x18 , _CONFIG); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ASTATUS() inb(_STATUS)
|
|
||||||
#define ADIAGSTATUS() inb(_DIAGSTAT)
|
|
||||||
#define ACOMMAND(cmd) outb((cmd),_COMMAND)
|
|
||||||
#define AINTMASK(msk) outb((msk),_INTMASK)
|
|
||||||
|
|
||||||
#define SETCONF outb(lp->config, _CONFIG)
|
|
||||||
|
|
||||||
#endif /* __COM20020_H */
|
#endif /* __COM20020_H */
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - COM20020 PCMCIA support
|
* Linux ARCnet driver - COM20020 PCMCIA support
|
||||||
*
|
*
|
||||||
* Written 1994-1999 by Avery Pennarun,
|
* Written 1994-1999 by Avery Pennarun,
|
||||||
* based on an ISA version by David Woodhouse.
|
* based on an ISA version by David Woodhouse.
|
||||||
* Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4)
|
* Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4)
|
||||||
@ -19,18 +19,21 @@
|
|||||||
* Director, National Security Agency. This software may only be used
|
* Director, National Security Agency. This software may only be used
|
||||||
* and distributed according to the terms of the GNU General Public License as
|
* and distributed according to the terms of the GNU General Public License as
|
||||||
* modified by SRC, incorporated herein by reference.
|
* modified by SRC, incorporated herein by reference.
|
||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
* Changes:
|
* Changes:
|
||||||
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000
|
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000
|
||||||
* - reorganize kmallocs in com20020_attach, checking all for failure
|
* - reorganize kmallocs in com20020_attach, checking all for failure
|
||||||
* and releasing the previous allocations if one fails
|
* and releasing the previous allocations if one fails
|
||||||
* **********************
|
* **********************
|
||||||
*
|
*
|
||||||
* For more details, see drivers/net/arcnet.c
|
* For more details, see drivers/net/arcnet.c
|
||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@ -39,52 +42,45 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/arcdevice.h>
|
#include <linux/io.h>
|
||||||
#include <linux/com20020.h>
|
|
||||||
|
|
||||||
#include <pcmcia/cistpl.h>
|
#include <pcmcia/cistpl.h>
|
||||||
#include <pcmcia/ds.h>
|
#include <pcmcia/ds.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include "arcdevice.h"
|
||||||
|
#include "com20020.h"
|
||||||
#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n"
|
|
||||||
|
|
||||||
|
|
||||||
static void regdump(struct net_device *dev)
|
static void regdump(struct net_device *dev)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
int ioaddr = dev->base_addr;
|
int ioaddr = dev->base_addr;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
netdev_dbg(dev, "register dump:\n");
|
netdev_dbg(dev, "register dump:\n");
|
||||||
for (count = ioaddr; count < ioaddr + 16; count++)
|
for (count = 0; count < 16; count++) {
|
||||||
{
|
if (!(count % 16))
|
||||||
if (!(count % 16))
|
pr_cont("%04X:", ioaddr + count);
|
||||||
pr_cont("%04X:", count);
|
pr_cont(" %02X", arcnet_inb(ioaddr, count));
|
||||||
pr_cont(" %02X", inb(count));
|
}
|
||||||
}
|
pr_cont("\n");
|
||||||
pr_cont("\n");
|
|
||||||
|
netdev_dbg(dev, "buffer0 dump:\n");
|
||||||
netdev_dbg(dev, "buffer0 dump:\n");
|
|
||||||
/* set up the address register */
|
/* set up the address register */
|
||||||
count = 0;
|
count = 0;
|
||||||
outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
|
arcnet_outb((count >> 8) | RDDATAflag | AUTOINCflag,
|
||||||
outb(count & 0xff, _ADDR_LO);
|
ioaddr, com20020_REG_W_ADDR_HI);
|
||||||
|
arcnet_outb(count & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
|
||||||
for (count = 0; count < 256+32; count++)
|
|
||||||
{
|
for (count = 0; count < 256 + 32; count++) {
|
||||||
if (!(count % 16))
|
if (!(count % 16))
|
||||||
pr_cont("%04X:", count);
|
pr_cont("%04X:", count);
|
||||||
|
|
||||||
/* copy the data */
|
/* copy the data */
|
||||||
pr_cont(" %02X", inb(_MEMDATA));
|
pr_cont(" %02X", arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA));
|
||||||
}
|
}
|
||||||
pr_cont("\n");
|
pr_cont("\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*====================================================================*/
|
/*====================================================================*/
|
||||||
|
|
||||||
/* Parameters that can be set with 'insmod' */
|
/* Parameters that can be set with 'insmod' */
|
||||||
@ -114,169 +110,161 @@ static void com20020_detach(struct pcmcia_device *p_dev);
|
|||||||
|
|
||||||
static int com20020_probe(struct pcmcia_device *p_dev)
|
static int com20020_probe(struct pcmcia_device *p_dev)
|
||||||
{
|
{
|
||||||
struct com20020_dev *info;
|
struct com20020_dev *info;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
struct arcnet_local *lp;
|
struct arcnet_local *lp;
|
||||||
|
|
||||||
dev_dbg(&p_dev->dev, "com20020_attach()\n");
|
dev_dbg(&p_dev->dev, "com20020_attach()\n");
|
||||||
|
|
||||||
/* Create new network device */
|
/* Create new network device */
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||||
if (!info)
|
if (!info)
|
||||||
goto fail_alloc_info;
|
goto fail_alloc_info;
|
||||||
|
|
||||||
dev = alloc_arcdev("");
|
dev = alloc_arcdev("");
|
||||||
if (!dev)
|
if (!dev)
|
||||||
goto fail_alloc_dev;
|
goto fail_alloc_dev;
|
||||||
|
|
||||||
lp = netdev_priv(dev);
|
lp = netdev_priv(dev);
|
||||||
lp->timeout = timeout;
|
lp->timeout = timeout;
|
||||||
lp->backplane = backplane;
|
lp->backplane = backplane;
|
||||||
lp->clockp = clockp;
|
lp->clockp = clockp;
|
||||||
lp->clockm = clockm & 3;
|
lp->clockm = clockm & 3;
|
||||||
lp->hw.owner = THIS_MODULE;
|
lp->hw.owner = THIS_MODULE;
|
||||||
|
|
||||||
/* fill in our module parameters as defaults */
|
/* fill in our module parameters as defaults */
|
||||||
dev->dev_addr[0] = node;
|
dev->dev_addr[0] = node;
|
||||||
|
|
||||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||||
p_dev->resource[0]->end = 16;
|
p_dev->resource[0]->end = 16;
|
||||||
p_dev->config_flags |= CONF_ENABLE_IRQ;
|
p_dev->config_flags |= CONF_ENABLE_IRQ;
|
||||||
|
|
||||||
info->dev = dev;
|
info->dev = dev;
|
||||||
p_dev->priv = info;
|
p_dev->priv = info;
|
||||||
|
|
||||||
return com20020_config(p_dev);
|
return com20020_config(p_dev);
|
||||||
|
|
||||||
fail_alloc_dev:
|
fail_alloc_dev:
|
||||||
kfree(info);
|
kfree(info);
|
||||||
fail_alloc_info:
|
fail_alloc_info:
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
} /* com20020_attach */
|
} /* com20020_attach */
|
||||||
|
|
||||||
static void com20020_detach(struct pcmcia_device *link)
|
static void com20020_detach(struct pcmcia_device *link)
|
||||||
{
|
{
|
||||||
struct com20020_dev *info = link->priv;
|
struct com20020_dev *info = link->priv;
|
||||||
struct net_device *dev = info->dev;
|
struct net_device *dev = info->dev;
|
||||||
|
|
||||||
dev_dbg(&link->dev, "detach...\n");
|
dev_dbg(&link->dev, "detach...\n");
|
||||||
|
|
||||||
dev_dbg(&link->dev, "com20020_detach\n");
|
dev_dbg(&link->dev, "com20020_detach\n");
|
||||||
|
|
||||||
dev_dbg(&link->dev, "unregister...\n");
|
dev_dbg(&link->dev, "unregister...\n");
|
||||||
|
|
||||||
unregister_netdev(dev);
|
unregister_netdev(dev);
|
||||||
|
|
||||||
/*
|
/* this is necessary because we register our IRQ separately
|
||||||
* this is necessary because we register our IRQ separately
|
* from card services.
|
||||||
* from card services.
|
*/
|
||||||
*/
|
if (dev->irq)
|
||||||
if (dev->irq)
|
free_irq(dev->irq, dev);
|
||||||
free_irq(dev->irq, dev);
|
|
||||||
|
|
||||||
com20020_release(link);
|
com20020_release(link);
|
||||||
|
|
||||||
/* Unlink device structure, free bits */
|
/* Unlink device structure, free bits */
|
||||||
dev_dbg(&link->dev, "unlinking...\n");
|
dev_dbg(&link->dev, "unlinking...\n");
|
||||||
if (link->priv)
|
if (link->priv) {
|
||||||
{
|
dev = info->dev;
|
||||||
dev = info->dev;
|
if (dev) {
|
||||||
if (dev)
|
dev_dbg(&link->dev, "kfree...\n");
|
||||||
{
|
free_netdev(dev);
|
||||||
dev_dbg(&link->dev, "kfree...\n");
|
}
|
||||||
free_netdev(dev);
|
dev_dbg(&link->dev, "kfree2...\n");
|
||||||
|
kfree(info);
|
||||||
}
|
}
|
||||||
dev_dbg(&link->dev, "kfree2...\n");
|
|
||||||
kfree(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* com20020_detach */
|
} /* com20020_detach */
|
||||||
|
|
||||||
static int com20020_config(struct pcmcia_device *link)
|
static int com20020_config(struct pcmcia_device *link)
|
||||||
{
|
{
|
||||||
struct arcnet_local *lp;
|
struct arcnet_local *lp;
|
||||||
struct com20020_dev *info;
|
struct com20020_dev *info;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
int ioaddr;
|
int ioaddr;
|
||||||
|
|
||||||
info = link->priv;
|
info = link->priv;
|
||||||
dev = info->dev;
|
dev = info->dev;
|
||||||
|
|
||||||
dev_dbg(&link->dev, "config...\n");
|
dev_dbg(&link->dev, "config...\n");
|
||||||
|
|
||||||
dev_dbg(&link->dev, "com20020_config\n");
|
dev_dbg(&link->dev, "com20020_config\n");
|
||||||
|
|
||||||
dev_dbg(&link->dev, "baseport1 is %Xh\n",
|
dev_dbg(&link->dev, "baseport1 is %Xh\n",
|
||||||
(unsigned int) link->resource[0]->start);
|
(unsigned int)link->resource[0]->start);
|
||||||
|
|
||||||
i = -ENODEV;
|
i = -ENODEV;
|
||||||
link->io_lines = 16;
|
link->io_lines = 16;
|
||||||
|
|
||||||
if (!link->resource[0]->start)
|
if (!link->resource[0]->start) {
|
||||||
{
|
for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) {
|
||||||
for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
|
link->resource[0]->start = ioaddr;
|
||||||
{
|
i = pcmcia_request_io(link);
|
||||||
link->resource[0]->start = ioaddr;
|
if (i == 0)
|
||||||
i = pcmcia_request_io(link);
|
break;
|
||||||
if (i == 0)
|
}
|
||||||
break;
|
} else {
|
||||||
|
i = pcmcia_request_io(link);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
i = pcmcia_request_io(link);
|
|
||||||
|
|
||||||
if (i != 0)
|
|
||||||
{
|
|
||||||
dev_dbg(&link->dev, "requestIO failed totally!\n");
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
ioaddr = dev->base_addr = link->resource[0]->start;
|
|
||||||
dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
|
|
||||||
|
|
||||||
dev_dbg(&link->dev, "request IRQ %d\n",
|
if (i != 0) {
|
||||||
link->irq);
|
dev_dbg(&link->dev, "requestIO failed totally!\n");
|
||||||
if (!link->irq)
|
goto failed;
|
||||||
{
|
}
|
||||||
dev_dbg(&link->dev, "requestIRQ failed totally!\n");
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->irq = link->irq;
|
ioaddr = dev->base_addr = link->resource[0]->start;
|
||||||
|
dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
|
||||||
|
|
||||||
ret = pcmcia_enable_device(link);
|
dev_dbg(&link->dev, "request IRQ %d\n",
|
||||||
if (ret)
|
link->irq);
|
||||||
goto failed;
|
if (!link->irq) {
|
||||||
|
dev_dbg(&link->dev, "requestIRQ failed totally!\n");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
if (com20020_check(dev))
|
dev->irq = link->irq;
|
||||||
{
|
|
||||||
regdump(dev);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
lp = netdev_priv(dev);
|
|
||||||
lp->card_name = "PCMCIA COM20020";
|
|
||||||
lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
|
|
||||||
|
|
||||||
SET_NETDEV_DEV(dev, &link->dev);
|
ret = pcmcia_enable_device(link);
|
||||||
|
if (ret)
|
||||||
|
goto failed;
|
||||||
|
|
||||||
i = com20020_found(dev, 0); /* calls register_netdev */
|
if (com20020_check(dev)) {
|
||||||
|
regdump(dev);
|
||||||
if (i != 0) {
|
goto failed;
|
||||||
dev_notice(&link->dev,
|
}
|
||||||
"com20020_found() failed\n");
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
netdev_dbg(dev, "port %#3lx, irq %d\n",
|
lp = netdev_priv(dev);
|
||||||
dev->base_addr, dev->irq);
|
lp->card_name = "PCMCIA COM20020";
|
||||||
return 0;
|
lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
|
||||||
|
|
||||||
|
SET_NETDEV_DEV(dev, &link->dev);
|
||||||
|
|
||||||
|
i = com20020_found(dev, 0); /* calls register_netdev */
|
||||||
|
|
||||||
|
if (i != 0) {
|
||||||
|
dev_notice(&link->dev,
|
||||||
|
"com20020_found() failed\n");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
netdev_dbg(dev, "port %#3lx, irq %d\n",
|
||||||
|
dev->base_addr, dev->irq);
|
||||||
|
return 0;
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
dev_dbg(&link->dev, "com20020_config failed...\n");
|
dev_dbg(&link->dev, "com20020_config failed...\n");
|
||||||
com20020_release(link);
|
com20020_release(link);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
} /* com20020_config */
|
} /* com20020_config */
|
||||||
|
|
||||||
static void com20020_release(struct pcmcia_device *link)
|
static void com20020_release(struct pcmcia_device *link)
|
||||||
@ -304,7 +292,10 @@ static int com20020_resume(struct pcmcia_device *link)
|
|||||||
if (link->open) {
|
if (link->open) {
|
||||||
int ioaddr = dev->base_addr;
|
int ioaddr = dev->base_addr;
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
ARCRESET;
|
|
||||||
|
arcnet_outb(lp->config | 0x80, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
|
udelay(5);
|
||||||
|
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -312,9 +303,9 @@ static int com20020_resume(struct pcmcia_device *link)
|
|||||||
|
|
||||||
static const struct pcmcia_device_id com20020_ids[] = {
|
static const struct pcmcia_device_id com20020_ids[] = {
|
||||||
PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
|
PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
|
||||||
"PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
|
"PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
|
||||||
PCMCIA_DEVICE_PROD_ID12("SoHard AG",
|
PCMCIA_DEVICE_PROD_ID12("SoHard AG",
|
||||||
"SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
|
"SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
|
||||||
PCMCIA_DEVICE_NULL
|
PCMCIA_DEVICE_NULL
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
|
MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
|
||||||
|
17
drivers/net/arcnet/com9026.h
Normal file
17
drivers/net/arcnet/com9026.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef __COM9026_H
|
||||||
|
#define __COM9026_H
|
||||||
|
|
||||||
|
/* COM 9026 controller chip --> ARCnet register addresses */
|
||||||
|
|
||||||
|
#define COM9026_REG_W_INTMASK 0 /* writable */
|
||||||
|
#define COM9026_REG_R_STATUS 0 /* readable */
|
||||||
|
#define COM9026_REG_W_COMMAND 1 /* writable, returns random vals on read (?) */
|
||||||
|
#define COM9026_REG_RW_CONFIG 2 /* Configuration register */
|
||||||
|
#define COM9026_REG_R_RESET 8 /* software reset (on read) */
|
||||||
|
#define COM9026_REG_RW_MEMDATA 12 /* Data port for IO-mapped memory */
|
||||||
|
#define COM9026_REG_W_ADDR_LO 14 /* Control registers for said */
|
||||||
|
#define COM9026_REG_W_ADDR_HI 15
|
||||||
|
|
||||||
|
#define COM9026_REG_R_STATION 1 /* Station ID */
|
||||||
|
|
||||||
|
#endif
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - COM90xx chipset (IO-mapped buffers)
|
* Linux ARCnet driver - COM90xx chipset (IO-mapped buffers)
|
||||||
*
|
*
|
||||||
* Written 1997 by David Woodhouse.
|
* Written 1997 by David Woodhouse.
|
||||||
* Written 1994-1999 by Avery Pennarun.
|
* Written 1994-1999 by Avery Pennarun.
|
||||||
* Written 1999-2000 by Martin Mares <mj@ucw.cz>.
|
* Written 1999-2000 by Martin Mares <mj@ucw.cz>.
|
||||||
@ -25,6 +25,9 @@
|
|||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
@ -34,12 +37,10 @@
|
|||||||
#include <linux/bootmem.h>
|
#include <linux/bootmem.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <asm/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/arcdevice.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define VERSION "arcnet: COM90xx IO-mapped mode support (by David Woodhouse et el.)\n"
|
|
||||||
|
|
||||||
|
#include "arcdevice.h"
|
||||||
|
#include "com9026.h"
|
||||||
|
|
||||||
/* Internal function declarations */
|
/* Internal function declarations */
|
||||||
|
|
||||||
@ -50,35 +51,14 @@ static void com90io_setmask(struct net_device *dev, int mask);
|
|||||||
static int com90io_reset(struct net_device *dev, int really_reset);
|
static int com90io_reset(struct net_device *dev, int really_reset);
|
||||||
static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
||||||
void *buf, int count);
|
void *buf, int count);
|
||||||
static void com90io_copy_from_card(struct net_device *dev, int bufnum, int offset,
|
static void com90io_copy_from_card(struct net_device *dev, int bufnum,
|
||||||
void *buf, int count);
|
int offset, void *buf, int count);
|
||||||
|
|
||||||
|
|
||||||
/* Handy defines for ARCnet specific stuff */
|
/* Handy defines for ARCnet specific stuff */
|
||||||
|
|
||||||
/* The number of low I/O ports used by the card. */
|
/* The number of low I/O ports used by the card. */
|
||||||
#define ARCNET_TOTAL_SIZE 16
|
#define ARCNET_TOTAL_SIZE 16
|
||||||
|
|
||||||
/* COM 9026 controller chip --> ARCnet register addresses */
|
|
||||||
#define _INTMASK (ioaddr+0) /* writable */
|
|
||||||
#define _STATUS (ioaddr+0) /* readable */
|
|
||||||
#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
|
|
||||||
#define _RESET (ioaddr+8) /* software reset (on read) */
|
|
||||||
#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */
|
|
||||||
#define _ADDR_HI (ioaddr+15) /* Control registers for said */
|
|
||||||
#define _ADDR_LO (ioaddr+14)
|
|
||||||
#define _CONFIG (ioaddr+2) /* Configuration register */
|
|
||||||
|
|
||||||
#undef ASTATUS
|
|
||||||
#undef ACOMMAND
|
|
||||||
#undef AINTMASK
|
|
||||||
|
|
||||||
#define ASTATUS() inb(_STATUS)
|
|
||||||
#define ACOMMAND(cmd) outb((cmd),_COMMAND)
|
|
||||||
#define AINTMASK(msk) outb((msk),_INTMASK)
|
|
||||||
#define SETCONF() outb((lp->config),_CONFIG)
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* *
|
* *
|
||||||
* IO-mapped operation routines *
|
* IO-mapped operation routines *
|
||||||
@ -92,58 +72,59 @@ static u_char get_buffer_byte(struct net_device *dev, unsigned offset)
|
|||||||
{
|
{
|
||||||
int ioaddr = dev->base_addr;
|
int ioaddr = dev->base_addr;
|
||||||
|
|
||||||
outb(offset >> 8, _ADDR_HI);
|
arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);
|
||||||
outb(offset & 0xff, _ADDR_LO);
|
arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);
|
||||||
|
|
||||||
return inb(_MEMDATA);
|
return arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ONE_AT_A_TIME_TX
|
#ifdef ONE_AT_A_TIME_TX
|
||||||
static void put_buffer_byte(struct net_device *dev, unsigned offset, u_char datum)
|
static void put_buffer_byte(struct net_device *dev, unsigned offset,
|
||||||
|
u_char datum)
|
||||||
{
|
{
|
||||||
int ioaddr = dev->base_addr;
|
int ioaddr = dev->base_addr;
|
||||||
|
|
||||||
outb(offset >> 8, _ADDR_HI);
|
arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);
|
||||||
outb(offset & 0xff, _ADDR_LO);
|
arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);
|
||||||
|
|
||||||
outb(datum, _MEMDATA);
|
arcnet_outb(datum, ioaddr, COM9026_REG_RW_MEMDATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void get_whole_buffer(struct net_device *dev, unsigned offset,
|
||||||
static void get_whole_buffer(struct net_device *dev, unsigned offset, unsigned length, char *dest)
|
unsigned length, char *dest)
|
||||||
{
|
{
|
||||||
int ioaddr = dev->base_addr;
|
int ioaddr = dev->base_addr;
|
||||||
|
|
||||||
outb((offset >> 8) | AUTOINCflag, _ADDR_HI);
|
arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);
|
||||||
outb(offset & 0xff, _ADDR_LO);
|
arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);
|
||||||
|
|
||||||
while (length--)
|
while (length--)
|
||||||
#ifdef ONE_AT_A_TIME_RX
|
#ifdef ONE_AT_A_TIME_RX
|
||||||
*(dest++) = get_buffer_byte(dev, offset++);
|
*(dest++) = get_buffer_byte(dev, offset++);
|
||||||
#else
|
#else
|
||||||
*(dest++) = inb(_MEMDATA);
|
*(dest++) = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void put_whole_buffer(struct net_device *dev, unsigned offset, unsigned length, char *dest)
|
static void put_whole_buffer(struct net_device *dev, unsigned offset,
|
||||||
|
unsigned length, char *dest)
|
||||||
{
|
{
|
||||||
int ioaddr = dev->base_addr;
|
int ioaddr = dev->base_addr;
|
||||||
|
|
||||||
outb((offset >> 8) | AUTOINCflag, _ADDR_HI);
|
arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);
|
||||||
outb(offset & 0xff, _ADDR_LO);
|
arcnet_outb(offset & 0xff, ioaddr,COM9026_REG_W_ADDR_LO);
|
||||||
|
|
||||||
while (length--)
|
while (length--)
|
||||||
#ifdef ONE_AT_A_TIME_TX
|
#ifdef ONE_AT_A_TIME_TX
|
||||||
put_buffer_byte(dev, offset++, *(dest++));
|
put_buffer_byte(dev, offset++, *(dest++));
|
||||||
#else
|
#else
|
||||||
outb(*(dest++), _MEMDATA);
|
arcnet_outb(*(dest++), ioaddr, COM9026_REG_RW_MEMDATA);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* We cannot probe for an IO mapped card either, although we can check that
|
||||||
* We cannot probe for an IO mapped card either, although we can check that
|
|
||||||
* it's where we were told it was, and even autoirq
|
* it's where we were told it was, and even autoirq
|
||||||
*/
|
*/
|
||||||
static int __init com90io_probe(struct net_device *dev)
|
static int __init com90io_probe(struct net_device *dev)
|
||||||
@ -151,71 +132,78 @@ static int __init com90io_probe(struct net_device *dev)
|
|||||||
int ioaddr = dev->base_addr, status;
|
int ioaddr = dev->base_addr, status;
|
||||||
unsigned long airqmask;
|
unsigned long airqmask;
|
||||||
|
|
||||||
BUGLVL(D_NORMAL) printk(VERSION);
|
if (BUGLVL(D_NORMAL)) {
|
||||||
BUGLVL(D_NORMAL) printk("E-mail me if you actually test this driver, please!\n");
|
pr_info("%s\n", "COM90xx IO-mapped mode support (by David Woodhouse et el.)");
|
||||||
|
pr_info("E-mail me if you actually test this driver, please!\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (!ioaddr) {
|
if (!ioaddr) {
|
||||||
BUGMSG(D_NORMAL, "No autoprobe for IO mapped cards; you "
|
arc_printk(D_NORMAL, dev, "No autoprobe for IO mapped cards; you must specify the base address!\n");
|
||||||
"must specify the base address!\n");
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) {
|
if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) {
|
||||||
BUGMSG(D_INIT_REASONS, "IO request_region %x-%x failed.\n",
|
arc_printk(D_INIT_REASONS, dev, "IO request_region %x-%x failed\n",
|
||||||
ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
|
ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
if (ASTATUS() == 0xFF) {
|
if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {
|
||||||
BUGMSG(D_INIT_REASONS, "IO address %x empty\n", ioaddr);
|
arc_printk(D_INIT_REASONS, dev, "IO address %x empty\n",
|
||||||
|
ioaddr);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
inb(_RESET);
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
||||||
mdelay(RESETtime);
|
mdelay(RESETtime);
|
||||||
|
|
||||||
status = ASTATUS();
|
status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
|
||||||
|
|
||||||
if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
|
if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
|
||||||
BUGMSG(D_INIT_REASONS, "Status invalid (%Xh).\n", status);
|
arc_printk(D_INIT_REASONS, dev, "Status invalid (%Xh)\n",
|
||||||
|
status);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
BUGMSG(D_INIT_REASONS, "Status after reset: %X\n", status);
|
arc_printk(D_INIT_REASONS, dev, "Status after reset: %X\n", status);
|
||||||
|
|
||||||
ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
|
arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
|
||||||
|
ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
|
||||||
BUGMSG(D_INIT_REASONS, "Status after reset acknowledged: %X\n", status);
|
arc_printk(D_INIT_REASONS, dev, "Status after reset acknowledged: %X\n",
|
||||||
|
status);
|
||||||
|
|
||||||
status = ASTATUS();
|
status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
|
||||||
|
|
||||||
if (status & RESETflag) {
|
if (status & RESETflag) {
|
||||||
BUGMSG(D_INIT_REASONS, "Eternal reset (status=%Xh)\n", status);
|
arc_printk(D_INIT_REASONS, dev, "Eternal reset (status=%Xh)\n",
|
||||||
|
status);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
outb((0x16 | IOMAPflag) & ~ENABLE16flag, _CONFIG);
|
arcnet_outb((0x16 | IOMAPflag) & ~ENABLE16flag,
|
||||||
|
ioaddr, COM9026_REG_RW_CONFIG);
|
||||||
|
|
||||||
/* Read first loc'n of memory */
|
/* Read first loc'n of memory */
|
||||||
|
|
||||||
outb(AUTOINCflag, _ADDR_HI);
|
arcnet_outb(AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);
|
||||||
outb(0, _ADDR_LO);
|
arcnet_outb(0, ioaddr, COM9026_REG_W_ADDR_LO);
|
||||||
|
|
||||||
if ((status = inb(_MEMDATA)) != 0xd1) {
|
status = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);
|
||||||
BUGMSG(D_INIT_REASONS, "Signature byte not found"
|
if (status != 0xd1) {
|
||||||
" (%Xh instead).\n", status);
|
arc_printk(D_INIT_REASONS, dev, "Signature byte not found (%Xh instead).\n",
|
||||||
|
status);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
if (!dev->irq) {
|
if (!dev->irq) {
|
||||||
/*
|
/* if we do this, we're sure to get an IRQ since the
|
||||||
* if we do this, we're sure to get an IRQ since the
|
|
||||||
* card has just reset and the NORXflag is on until
|
* card has just reset and the NORXflag is on until
|
||||||
* we tell it to start receiving.
|
* we tell it to start receiving.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
airqmask = probe_irq_on();
|
airqmask = probe_irq_on();
|
||||||
outb(NORXflag, _INTMASK);
|
arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);
|
||||||
udelay(1);
|
udelay(1);
|
||||||
outb(0, _INTMASK);
|
arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);
|
||||||
dev->irq = probe_irq_off(airqmask);
|
dev->irq = probe_irq_off(airqmask);
|
||||||
|
|
||||||
if ((int)dev->irq <= 0) {
|
if ((int)dev->irq <= 0) {
|
||||||
BUGMSG(D_INIT_REASONS, "Autoprobe IRQ failed\n");
|
arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed\n");
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,7 +215,6 @@ err_out:
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Set up the struct net_device associated with this card. Called after
|
/* Set up the struct net_device associated with this card. Called after
|
||||||
* probing succeeds.
|
* probing succeeds.
|
||||||
*/
|
*/
|
||||||
@ -238,12 +225,14 @@ static int __init com90io_found(struct net_device *dev)
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* Reserve the irq */
|
/* Reserve the irq */
|
||||||
if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (COM90xx-IO)", dev)) {
|
if (request_irq(dev->irq, arcnet_interrupt, 0,
|
||||||
BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
|
"arcnet (COM90xx-IO)", dev)) {
|
||||||
|
arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
/* Reserve the I/O region */
|
/* Reserve the I/O region */
|
||||||
if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)")) {
|
if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE,
|
||||||
|
"arcnet (COM90xx-IO)")) {
|
||||||
free_irq(dev->irq, dev);
|
free_irq(dev->irq, dev);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
@ -259,7 +248,7 @@ static int __init com90io_found(struct net_device *dev)
|
|||||||
lp->hw.copy_from_card = com90io_copy_from_card;
|
lp->hw.copy_from_card = com90io_copy_from_card;
|
||||||
|
|
||||||
lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag;
|
lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag;
|
||||||
SETCONF();
|
arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);
|
||||||
|
|
||||||
/* get and check the station ID from offset 1 in shmem */
|
/* get and check the station ID from offset 1 in shmem */
|
||||||
|
|
||||||
@ -267,21 +256,20 @@ static int __init com90io_found(struct net_device *dev)
|
|||||||
|
|
||||||
err = register_netdev(dev);
|
err = register_netdev(dev);
|
||||||
if (err) {
|
if (err) {
|
||||||
outb((inb(_CONFIG) & ~IOMAPflag), _CONFIG);
|
arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag,
|
||||||
|
ioaddr, COM9026_REG_RW_CONFIG);
|
||||||
free_irq(dev->irq, dev);
|
free_irq(dev->irq, dev);
|
||||||
release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
|
release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
BUGMSG(D_NORMAL, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n",
|
arc_printk(D_NORMAL, dev, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n",
|
||||||
dev->dev_addr[0], dev->base_addr, dev->irq);
|
dev->dev_addr[0], dev->base_addr, dev->irq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Do a hardware reset on the card, and set up necessary registers.
|
||||||
/*
|
|
||||||
* Do a hardware reset on the card, and set up necessary registers.
|
|
||||||
*
|
*
|
||||||
* This should be called as little as possible, because it disrupts the
|
* This should be called as little as possible, because it disrupts the
|
||||||
* token on the network (causes a RECON) and requires a significant delay.
|
* token on the network (causes a RECON) and requires a significant delay.
|
||||||
@ -293,67 +281,66 @@ static int com90io_reset(struct net_device *dev, int really_reset)
|
|||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
short ioaddr = dev->base_addr;
|
short ioaddr = dev->base_addr;
|
||||||
|
|
||||||
BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS());
|
arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",
|
||||||
|
dev->name, arcnet_inb(ioaddr, COM9026_REG_R_STATUS));
|
||||||
|
|
||||||
if (really_reset) {
|
if (really_reset) {
|
||||||
/* reset the card */
|
/* reset the card */
|
||||||
inb(_RESET);
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
||||||
mdelay(RESETtime);
|
mdelay(RESETtime);
|
||||||
}
|
}
|
||||||
/* Set the thing to IO-mapped, 8-bit mode */
|
/* Set the thing to IO-mapped, 8-bit mode */
|
||||||
lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag;
|
lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag;
|
||||||
SETCONF();
|
arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);
|
||||||
|
|
||||||
ACOMMAND(CFLAGScmd | RESETclear); /* clear flags & end reset */
|
arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
ACOMMAND(CFLAGScmd | CONFIGclear);
|
/* clear flags & end reset */
|
||||||
|
arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
|
||||||
/* verify that the ARCnet signature byte is present */
|
/* verify that the ARCnet signature byte is present */
|
||||||
if (get_buffer_byte(dev, 0) != TESTvalue) {
|
if (get_buffer_byte(dev, 0) != TESTvalue) {
|
||||||
BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n");
|
arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/* enable extended (512-byte) packets */
|
/* enable extended (512-byte) packets */
|
||||||
ACOMMAND(CONFIGcmd | EXTconf);
|
arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
|
||||||
/* done! return success. */
|
/* done! return success. */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void com90io_command(struct net_device *dev, int cmd)
|
static void com90io_command(struct net_device *dev, int cmd)
|
||||||
{
|
{
|
||||||
short ioaddr = dev->base_addr;
|
short ioaddr = dev->base_addr;
|
||||||
|
|
||||||
ACOMMAND(cmd);
|
arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int com90io_status(struct net_device *dev)
|
static int com90io_status(struct net_device *dev)
|
||||||
{
|
{
|
||||||
short ioaddr = dev->base_addr;
|
short ioaddr = dev->base_addr;
|
||||||
|
|
||||||
return ASTATUS();
|
return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void com90io_setmask(struct net_device *dev, int mask)
|
static void com90io_setmask(struct net_device *dev, int mask)
|
||||||
{
|
{
|
||||||
short ioaddr = dev->base_addr;
|
short ioaddr = dev->base_addr;
|
||||||
|
|
||||||
AINTMASK(mask);
|
arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
static void com90io_copy_to_card(struct net_device *dev, int bufnum,
|
||||||
void *buf, int count)
|
int offset, void *buf, int count)
|
||||||
{
|
{
|
||||||
TIME("put_whole_buffer", count, put_whole_buffer(dev, bufnum * 512 + offset, count, buf));
|
TIME(dev, "put_whole_buffer", count,
|
||||||
|
put_whole_buffer(dev, bufnum * 512 + offset, count, buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void com90io_copy_from_card(struct net_device *dev, int bufnum,
|
||||||
static void com90io_copy_from_card(struct net_device *dev, int bufnum, int offset,
|
int offset, void *buf, int count)
|
||||||
void *buf, int count)
|
|
||||||
{
|
{
|
||||||
TIME("get_whole_buffer", count, get_whole_buffer(dev, bufnum * 512 + offset, count, buf));
|
TIME(dev, "get_whole_buffer", count,
|
||||||
|
get_whole_buffer(dev, bufnum * 512 + offset, count, buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int io; /* use the insmod io= irq= shmem= options */
|
static int io; /* use the insmod io= irq= shmem= options */
|
||||||
@ -369,12 +356,13 @@ MODULE_LICENSE("GPL");
|
|||||||
static int __init com90io_setup(char *s)
|
static int __init com90io_setup(char *s)
|
||||||
{
|
{
|
||||||
int ints[4];
|
int ints[4];
|
||||||
|
|
||||||
s = get_options(s, 4, ints);
|
s = get_options(s, 4, ints);
|
||||||
if (!ints[0])
|
if (!ints[0])
|
||||||
return 0;
|
return 0;
|
||||||
switch (ints[0]) {
|
switch (ints[0]) {
|
||||||
default: /* ERROR */
|
default: /* ERROR */
|
||||||
printk("com90io: Too many arguments.\n");
|
pr_err("Too many arguments\n");
|
||||||
case 2: /* IRQ */
|
case 2: /* IRQ */
|
||||||
irq = ints[2];
|
irq = ints[2];
|
||||||
case 1: /* IO address */
|
case 1: /* IO address */
|
||||||
@ -421,8 +409,11 @@ static void __exit com90io_exit(void)
|
|||||||
|
|
||||||
unregister_netdev(dev);
|
unregister_netdev(dev);
|
||||||
|
|
||||||
/* Set the thing back to MMAP mode, in case the old driver is loaded later */
|
/* In case the old driver is loaded later,
|
||||||
outb((inb(_CONFIG) & ~IOMAPflag), _CONFIG);
|
* set the thing back to MMAP mode
|
||||||
|
*/
|
||||||
|
arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag,
|
||||||
|
ioaddr, COM9026_REG_RW_CONFIG);
|
||||||
|
|
||||||
free_irq(dev->irq, dev);
|
free_irq(dev->irq, dev);
|
||||||
release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
|
release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - COM90xx chipset (memory-mapped buffers)
|
* Linux ARCnet driver - COM90xx chipset (memory-mapped buffers)
|
||||||
*
|
*
|
||||||
* Written 1994-1999 by Avery Pennarun.
|
* Written 1994-1999 by Avery Pennarun.
|
||||||
* Written 1999 by Martin Mares <mj@ucw.cz>.
|
* Written 1999 by Martin Mares <mj@ucw.cz>.
|
||||||
* Derived from skeleton.c by Donald Becker.
|
* Derived from skeleton.c by Donald Becker.
|
||||||
@ -24,6 +24,9 @@
|
|||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -32,12 +35,10 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <asm/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/arcdevice.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define VERSION "arcnet: COM90xx chipset support\n"
|
|
||||||
|
|
||||||
|
#include "arcdevice.h"
|
||||||
|
#include "com9026.h"
|
||||||
|
|
||||||
/* Define this to speed up the autoprobe by assuming if only one io port and
|
/* Define this to speed up the autoprobe by assuming if only one io port and
|
||||||
* shmem are left in the list at Stage 5, they must correspond to each
|
* shmem are left in the list at Stage 5, they must correspond to each
|
||||||
@ -53,7 +54,6 @@
|
|||||||
*/
|
*/
|
||||||
#undef FAST_PROBE
|
#undef FAST_PROBE
|
||||||
|
|
||||||
|
|
||||||
/* Internal function declarations */
|
/* Internal function declarations */
|
||||||
static int com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *);
|
static int com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *);
|
||||||
static void com90xx_command(struct net_device *dev, int command);
|
static void com90xx_command(struct net_device *dev, int command);
|
||||||
@ -62,8 +62,8 @@ static void com90xx_setmask(struct net_device *dev, int mask);
|
|||||||
static int com90xx_reset(struct net_device *dev, int really_reset);
|
static int com90xx_reset(struct net_device *dev, int really_reset);
|
||||||
static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
||||||
void *buf, int count);
|
void *buf, int count);
|
||||||
static void com90xx_copy_from_card(struct net_device *dev, int bufnum, int offset,
|
static void com90xx_copy_from_card(struct net_device *dev, int bufnum,
|
||||||
void *buf, int count);
|
int offset, void *buf, int count);
|
||||||
|
|
||||||
/* Known ARCnet cards */
|
/* Known ARCnet cards */
|
||||||
|
|
||||||
@ -77,26 +77,7 @@ static int numcards;
|
|||||||
|
|
||||||
/* Amount of I/O memory used by the card */
|
/* Amount of I/O memory used by the card */
|
||||||
#define BUFFER_SIZE (512)
|
#define BUFFER_SIZE (512)
|
||||||
#define MIRROR_SIZE (BUFFER_SIZE*4)
|
#define MIRROR_SIZE (BUFFER_SIZE * 4)
|
||||||
|
|
||||||
/* COM 9026 controller chip --> ARCnet register addresses */
|
|
||||||
#define _INTMASK (ioaddr+0) /* writable */
|
|
||||||
#define _STATUS (ioaddr+0) /* readable */
|
|
||||||
#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
|
|
||||||
#define _CONFIG (ioaddr+2) /* Configuration register */
|
|
||||||
#define _RESET (ioaddr+8) /* software reset (on read) */
|
|
||||||
#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */
|
|
||||||
#define _ADDR_HI (ioaddr+15) /* Control registers for said */
|
|
||||||
#define _ADDR_LO (ioaddr+14)
|
|
||||||
|
|
||||||
#undef ASTATUS
|
|
||||||
#undef ACOMMAND
|
|
||||||
#undef AINTMASK
|
|
||||||
|
|
||||||
#define ASTATUS() inb(_STATUS)
|
|
||||||
#define ACOMMAND(cmd) outb((cmd),_COMMAND)
|
|
||||||
#define AINTMASK(msk) outb((msk),_INTMASK)
|
|
||||||
|
|
||||||
|
|
||||||
static int com90xx_skip_probe __initdata = 0;
|
static int com90xx_skip_probe __initdata = 0;
|
||||||
|
|
||||||
@ -116,8 +97,7 @@ static void __init com90xx_probe(void)
|
|||||||
{
|
{
|
||||||
int count, status, ioaddr, numprint, airq, openparen = 0;
|
int count, status, ioaddr, numprint, airq, openparen = 0;
|
||||||
unsigned long airqmask;
|
unsigned long airqmask;
|
||||||
int ports[(0x3f0 - 0x200) / 16 + 1] =
|
int ports[(0x3f0 - 0x200) / 16 + 1] = { 0 };
|
||||||
{0};
|
|
||||||
unsigned long *shmems;
|
unsigned long *shmems;
|
||||||
void __iomem **iomem;
|
void __iomem **iomem;
|
||||||
int numports, numshmems, *port;
|
int numports, numshmems, *port;
|
||||||
@ -127,18 +107,19 @@ static void __init com90xx_probe(void)
|
|||||||
if (!io && !irq && !shmem && !*device && com90xx_skip_probe)
|
if (!io && !irq && !shmem && !*device && com90xx_skip_probe)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
shmems = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(unsigned long),
|
shmems = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(unsigned long),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!shmems)
|
if (!shmems)
|
||||||
return;
|
return;
|
||||||
iomem = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(void __iomem *),
|
iomem = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(void __iomem *),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!iomem) {
|
if (!iomem) {
|
||||||
kfree(shmems);
|
kfree(shmems);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BUGLVL(D_NORMAL) printk(VERSION);
|
if (BUGLVL(D_NORMAL))
|
||||||
|
pr_info("%s\n", "COM90xx chipset support");
|
||||||
|
|
||||||
/* set up the arrays where we'll store the possible probe addresses */
|
/* set up the arrays where we'll store the possible probe addresses */
|
||||||
numports = numshmems = 0;
|
numports = numshmems = 0;
|
||||||
@ -161,38 +142,43 @@ static void __init com90xx_probe(void)
|
|||||||
numprint++;
|
numprint++;
|
||||||
numprint %= 8;
|
numprint %= 8;
|
||||||
if (!numprint) {
|
if (!numprint) {
|
||||||
BUGMSG2(D_INIT, "\n");
|
arc_cont(D_INIT, "\n");
|
||||||
BUGMSG2(D_INIT, "S1: ");
|
arc_cont(D_INIT, "S1: ");
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT, "%Xh ", *port);
|
arc_cont(D_INIT, "%Xh ", *port);
|
||||||
|
|
||||||
ioaddr = *port;
|
ioaddr = *port;
|
||||||
|
|
||||||
if (!request_region(*port, ARCNET_TOTAL_SIZE, "arcnet (90xx)")) {
|
if (!request_region(*port, ARCNET_TOTAL_SIZE,
|
||||||
BUGMSG2(D_INIT_REASONS, "(request_region)\n");
|
"arcnet (90xx)")) {
|
||||||
BUGMSG2(D_INIT_REASONS, "S1: ");
|
arc_cont(D_INIT_REASONS, "(request_region)\n");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
arc_cont(D_INIT_REASONS, "S1: ");
|
||||||
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
*port-- = ports[--numports];
|
*port-- = ports[--numports];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ASTATUS() == 0xFF) {
|
if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {
|
||||||
BUGMSG2(D_INIT_REASONS, "(empty)\n");
|
arc_cont(D_INIT_REASONS, "(empty)\n");
|
||||||
BUGMSG2(D_INIT_REASONS, "S1: ");
|
arc_cont(D_INIT_REASONS, "S1: ");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
release_region(*port, ARCNET_TOTAL_SIZE);
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
||||||
*port-- = ports[--numports];
|
*port-- = ports[--numports];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
inb(_RESET); /* begin resetting card */
|
/* begin resetting card */
|
||||||
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
||||||
|
|
||||||
BUGMSG2(D_INIT_REASONS, "\n");
|
arc_cont(D_INIT_REASONS, "\n");
|
||||||
BUGMSG2(D_INIT_REASONS, "S1: ");
|
arc_cont(D_INIT_REASONS, "S1: ");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT, "\n");
|
arc_cont(D_INIT, "\n");
|
||||||
|
|
||||||
if (!numports) {
|
if (!numports) {
|
||||||
BUGMSG2(D_NORMAL, "S1: No ARCnet cards found.\n");
|
arc_cont(D_NORMAL, "S1: No ARCnet cards found.\n");
|
||||||
kfree(shmems);
|
kfree(shmems);
|
||||||
kfree(iomem);
|
kfree(iomem);
|
||||||
return;
|
return;
|
||||||
@ -206,12 +192,12 @@ static void __init com90xx_probe(void)
|
|||||||
numprint++;
|
numprint++;
|
||||||
numprint %= 8;
|
numprint %= 8;
|
||||||
if (!numprint) {
|
if (!numprint) {
|
||||||
BUGMSG2(D_INIT, "\n");
|
arc_cont(D_INIT, "\n");
|
||||||
BUGMSG2(D_INIT, "S2: ");
|
arc_cont(D_INIT, "S2: ");
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT, "%Xh ", *port);
|
arc_cont(D_INIT, "%Xh ", *port);
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT, "\n");
|
arc_cont(D_INIT, "\n");
|
||||||
mdelay(RESETtime);
|
mdelay(RESETtime);
|
||||||
|
|
||||||
/* Stage 3: abandon any shmem addresses that don't have the signature
|
/* Stage 3: abandon any shmem addresses that don't have the signature
|
||||||
@ -224,29 +210,33 @@ static void __init com90xx_probe(void)
|
|||||||
numprint++;
|
numprint++;
|
||||||
numprint %= 8;
|
numprint %= 8;
|
||||||
if (!numprint) {
|
if (!numprint) {
|
||||||
BUGMSG2(D_INIT, "\n");
|
arc_cont(D_INIT, "\n");
|
||||||
BUGMSG2(D_INIT, "S3: ");
|
arc_cont(D_INIT, "S3: ");
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT, "%lXh ", *p);
|
arc_cont(D_INIT, "%lXh ", *p);
|
||||||
|
|
||||||
if (!request_mem_region(*p, MIRROR_SIZE, "arcnet (90xx)")) {
|
if (!request_mem_region(*p, MIRROR_SIZE, "arcnet (90xx)")) {
|
||||||
BUGMSG2(D_INIT_REASONS, "(request_mem_region)\n");
|
arc_cont(D_INIT_REASONS, "(request_mem_region)\n");
|
||||||
BUGMSG2(D_INIT_REASONS, "Stage 3: ");
|
arc_cont(D_INIT_REASONS, "Stage 3: ");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
base = ioremap(*p, MIRROR_SIZE);
|
base = ioremap(*p, MIRROR_SIZE);
|
||||||
if (!base) {
|
if (!base) {
|
||||||
BUGMSG2(D_INIT_REASONS, "(ioremap)\n");
|
arc_cont(D_INIT_REASONS, "(ioremap)\n");
|
||||||
BUGMSG2(D_INIT_REASONS, "Stage 3: ");
|
arc_cont(D_INIT_REASONS, "Stage 3: ");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
goto out1;
|
goto out1;
|
||||||
}
|
}
|
||||||
if (readb(base) != TESTvalue) {
|
if (arcnet_readb(base, COM9026_REG_R_STATUS) != TESTvalue) {
|
||||||
BUGMSG2(D_INIT_REASONS, "(%02Xh != %02Xh)\n",
|
arc_cont(D_INIT_REASONS, "(%02Xh != %02Xh)\n",
|
||||||
readb(base), TESTvalue);
|
arcnet_readb(base, COM9026_REG_R_STATUS),
|
||||||
BUGMSG2(D_INIT_REASONS, "S3: ");
|
TESTvalue);
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
arc_cont(D_INIT_REASONS, "S3: ");
|
||||||
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
goto out2;
|
goto out2;
|
||||||
}
|
}
|
||||||
/* By writing 0x42 to the TESTvalue location, we also make
|
/* By writing 0x42 to the TESTvalue location, we also make
|
||||||
@ -254,15 +244,16 @@ static void __init com90xx_probe(void)
|
|||||||
* in another pass through this loop, they will be discarded
|
* in another pass through this loop, they will be discarded
|
||||||
* because *cptr != TESTvalue.
|
* because *cptr != TESTvalue.
|
||||||
*/
|
*/
|
||||||
writeb(0x42, base);
|
arcnet_writeb(0x42, base, COM9026_REG_W_INTMASK);
|
||||||
if (readb(base) != 0x42) {
|
if (arcnet_readb(base, COM9026_REG_R_STATUS) != 0x42) {
|
||||||
BUGMSG2(D_INIT_REASONS, "(read only)\n");
|
arc_cont(D_INIT_REASONS, "(read only)\n");
|
||||||
BUGMSG2(D_INIT_REASONS, "S3: ");
|
arc_cont(D_INIT_REASONS, "S3: ");
|
||||||
goto out2;
|
goto out2;
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT_REASONS, "\n");
|
arc_cont(D_INIT_REASONS, "\n");
|
||||||
BUGMSG2(D_INIT_REASONS, "S3: ");
|
arc_cont(D_INIT_REASONS, "S3: ");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
iomem[index] = base;
|
iomem[index] = base;
|
||||||
continue;
|
continue;
|
||||||
out2:
|
out2:
|
||||||
@ -273,10 +264,10 @@ static void __init com90xx_probe(void)
|
|||||||
*p-- = shmems[--numshmems];
|
*p-- = shmems[--numshmems];
|
||||||
index--;
|
index--;
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT, "\n");
|
arc_cont(D_INIT, "\n");
|
||||||
|
|
||||||
if (!numshmems) {
|
if (!numshmems) {
|
||||||
BUGMSG2(D_NORMAL, "S3: No ARCnet cards found.\n");
|
arc_cont(D_NORMAL, "S3: No ARCnet cards found.\n");
|
||||||
for (port = &ports[0]; port < ports + numports; port++)
|
for (port = &ports[0]; port < ports + numports; port++)
|
||||||
release_region(*port, ARCNET_TOTAL_SIZE);
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
||||||
kfree(shmems);
|
kfree(shmems);
|
||||||
@ -291,12 +282,12 @@ static void __init com90xx_probe(void)
|
|||||||
numprint++;
|
numprint++;
|
||||||
numprint %= 8;
|
numprint %= 8;
|
||||||
if (!numprint) {
|
if (!numprint) {
|
||||||
BUGMSG2(D_INIT, "\n");
|
arc_cont(D_INIT, "\n");
|
||||||
BUGMSG2(D_INIT, "S4: ");
|
arc_cont(D_INIT, "S4: ");
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT, "%lXh ", *p);
|
arc_cont(D_INIT, "%lXh ", *p);
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT, "\n");
|
arc_cont(D_INIT, "\n");
|
||||||
|
|
||||||
/* Stage 5: for any ports that have the correct status, can disable
|
/* Stage 5: for any ports that have the correct status, can disable
|
||||||
* the RESET flag, and (if no irq is given) generate an autoirq,
|
* the RESET flag, and (if no irq is given) generate an autoirq,
|
||||||
@ -308,33 +299,37 @@ static void __init com90xx_probe(void)
|
|||||||
numprint = -1;
|
numprint = -1;
|
||||||
for (port = &ports[0]; port < ports + numports; port++) {
|
for (port = &ports[0]; port < ports + numports; port++) {
|
||||||
int found = 0;
|
int found = 0;
|
||||||
|
|
||||||
numprint++;
|
numprint++;
|
||||||
numprint %= 8;
|
numprint %= 8;
|
||||||
if (!numprint) {
|
if (!numprint) {
|
||||||
BUGMSG2(D_INIT, "\n");
|
arc_cont(D_INIT, "\n");
|
||||||
BUGMSG2(D_INIT, "S5: ");
|
arc_cont(D_INIT, "S5: ");
|
||||||
}
|
}
|
||||||
BUGMSG2(D_INIT, "%Xh ", *port);
|
arc_cont(D_INIT, "%Xh ", *port);
|
||||||
|
|
||||||
ioaddr = *port;
|
ioaddr = *port;
|
||||||
status = ASTATUS();
|
status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
|
||||||
|
|
||||||
if ((status & 0x9D)
|
if ((status & 0x9D)
|
||||||
!= (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
|
!= (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
|
||||||
BUGMSG2(D_INIT_REASONS, "(status=%Xh)\n", status);
|
arc_cont(D_INIT_REASONS, "(status=%Xh)\n", status);
|
||||||
BUGMSG2(D_INIT_REASONS, "S5: ");
|
arc_cont(D_INIT_REASONS, "S5: ");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
release_region(*port, ARCNET_TOTAL_SIZE);
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
||||||
*port-- = ports[--numports];
|
*port-- = ports[--numports];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
|
arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
|
||||||
status = ASTATUS();
|
ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
|
||||||
if (status & RESETflag) {
|
if (status & RESETflag) {
|
||||||
BUGMSG2(D_INIT_REASONS, " (eternal reset, status=%Xh)\n",
|
arc_cont(D_INIT_REASONS, " (eternal reset, status=%Xh)\n",
|
||||||
status);
|
status);
|
||||||
BUGMSG2(D_INIT_REASONS, "S5: ");
|
arc_cont(D_INIT_REASONS, "S5: ");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
release_region(*port, ARCNET_TOTAL_SIZE);
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
||||||
*port-- = ports[--numports];
|
*port-- = ports[--numports];
|
||||||
continue;
|
continue;
|
||||||
@ -348,15 +343,16 @@ static void __init com90xx_probe(void)
|
|||||||
* we tell it to start receiving.
|
* we tell it to start receiving.
|
||||||
*/
|
*/
|
||||||
airqmask = probe_irq_on();
|
airqmask = probe_irq_on();
|
||||||
AINTMASK(NORXflag);
|
arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);
|
||||||
udelay(1);
|
udelay(1);
|
||||||
AINTMASK(0);
|
arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);
|
||||||
airq = probe_irq_off(airqmask);
|
airq = probe_irq_off(airqmask);
|
||||||
|
|
||||||
if (airq <= 0) {
|
if (airq <= 0) {
|
||||||
BUGMSG2(D_INIT_REASONS, "(airq=%d)\n", airq);
|
arc_cont(D_INIT_REASONS, "(airq=%d)\n", airq);
|
||||||
BUGMSG2(D_INIT_REASONS, "S5: ");
|
arc_cont(D_INIT_REASONS, "S5: ");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
numprint = 0;
|
||||||
release_region(*port, ARCNET_TOTAL_SIZE);
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
||||||
*port-- = ports[--numports];
|
*port-- = ports[--numports];
|
||||||
continue;
|
continue;
|
||||||
@ -365,7 +361,7 @@ static void __init com90xx_probe(void)
|
|||||||
airq = irq;
|
airq = irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
BUGMSG2(D_INIT, "(%d,", airq);
|
arc_cont(D_INIT, "(%d,", airq);
|
||||||
openparen = 1;
|
openparen = 1;
|
||||||
|
|
||||||
/* Everything seems okay. But which shmem, if any, puts
|
/* Everything seems okay. But which shmem, if any, puts
|
||||||
@ -376,14 +372,15 @@ static void __init com90xx_probe(void)
|
|||||||
*/
|
*/
|
||||||
#ifdef FAST_PROBE
|
#ifdef FAST_PROBE
|
||||||
if (numports > 1 || numshmems > 1) {
|
if (numports > 1 || numshmems > 1) {
|
||||||
inb(_RESET);
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
||||||
mdelay(RESETtime);
|
mdelay(RESETtime);
|
||||||
} else {
|
} else {
|
||||||
/* just one shmem and port, assume they match */
|
/* just one shmem and port, assume they match */
|
||||||
writeb(TESTvalue, iomem[0]);
|
arcnet_writeb(TESTvalue, iomem[0],
|
||||||
|
COM9026_REG_W_INTMASK);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
inb(_RESET);
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
||||||
mdelay(RESETtime);
|
mdelay(RESETtime);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -391,8 +388,8 @@ static void __init com90xx_probe(void)
|
|||||||
u_long ptr = shmems[index];
|
u_long ptr = shmems[index];
|
||||||
void __iomem *base = iomem[index];
|
void __iomem *base = iomem[index];
|
||||||
|
|
||||||
if (readb(base) == TESTvalue) { /* found one */
|
if (arcnet_readb(base, COM9026_REG_R_STATUS) == TESTvalue) { /* found one */
|
||||||
BUGMSG2(D_INIT, "%lXh)\n", *p);
|
arc_cont(D_INIT, "%lXh)\n", *p);
|
||||||
openparen = 0;
|
openparen = 0;
|
||||||
|
|
||||||
/* register the card */
|
/* register the card */
|
||||||
@ -405,25 +402,30 @@ static void __init com90xx_probe(void)
|
|||||||
iomem[index] = iomem[numshmems];
|
iomem[index] = iomem[numshmems];
|
||||||
break; /* go to the next I/O port */
|
break; /* go to the next I/O port */
|
||||||
} else {
|
} else {
|
||||||
BUGMSG2(D_INIT_REASONS, "%Xh-", readb(base));
|
arc_cont(D_INIT_REASONS, "%Xh-",
|
||||||
|
arcnet_readb(base, COM9026_REG_R_STATUS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openparen) {
|
if (openparen) {
|
||||||
BUGLVL(D_INIT) printk("no matching shmem)\n");
|
if (BUGLVL(D_INIT))
|
||||||
BUGLVL(D_INIT_REASONS) printk("S5: ");
|
pr_cont("no matching shmem)\n");
|
||||||
BUGLVL(D_INIT_REASONS) numprint = 0;
|
if (BUGLVL(D_INIT_REASONS)) {
|
||||||
|
pr_cont("S5: ");
|
||||||
|
numprint = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
release_region(*port, ARCNET_TOTAL_SIZE);
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
||||||
*port-- = ports[--numports];
|
*port-- = ports[--numports];
|
||||||
}
|
}
|
||||||
|
|
||||||
BUGLVL(D_INIT_REASONS) printk("\n");
|
if (BUGLVL(D_INIT_REASONS))
|
||||||
|
pr_cont("\n");
|
||||||
|
|
||||||
/* Now put back TESTvalue on all leftover shmems. */
|
/* Now put back TESTvalue on all leftover shmems. */
|
||||||
for (index = 0; index < numshmems; index++) {
|
for (index = 0; index < numshmems; index++) {
|
||||||
writeb(TESTvalue, iomem[index]);
|
arcnet_writeb(TESTvalue, iomem[index], COM9026_REG_W_INTMASK);
|
||||||
iounmap(iomem[index]);
|
iounmap(iomem[index]);
|
||||||
release_mem_region(shmems[index], MIRROR_SIZE);
|
release_mem_region(shmems[index], MIRROR_SIZE);
|
||||||
}
|
}
|
||||||
@ -441,7 +443,7 @@ static int check_mirror(unsigned long addr, size_t size)
|
|||||||
|
|
||||||
p = ioremap(addr, size);
|
p = ioremap(addr, size);
|
||||||
if (p) {
|
if (p) {
|
||||||
if (readb(p) == TESTvalue)
|
if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue)
|
||||||
res = 1;
|
res = 1;
|
||||||
else
|
else
|
||||||
res = 0;
|
res = 0;
|
||||||
@ -455,7 +457,8 @@ static int check_mirror(unsigned long addr, size_t size)
|
|||||||
/* Set up the struct net_device associated with this card. Called after
|
/* Set up the struct net_device associated with this card. Called after
|
||||||
* probing succeeds.
|
* probing succeeds.
|
||||||
*/
|
*/
|
||||||
static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *p)
|
static int __init com90xx_found(int ioaddr, int airq, u_long shmem,
|
||||||
|
void __iomem *p)
|
||||||
{
|
{
|
||||||
struct net_device *dev = NULL;
|
struct net_device *dev = NULL;
|
||||||
struct arcnet_local *lp;
|
struct arcnet_local *lp;
|
||||||
@ -465,7 +468,7 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
|
|||||||
/* allocate struct net_device */
|
/* allocate struct net_device */
|
||||||
dev = alloc_arcdev(device);
|
dev = alloc_arcdev(device);
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
BUGMSG2(D_NORMAL, "com90xx: Can't allocate device!\n");
|
arc_cont(D_NORMAL, "com90xx: Can't allocate device!\n");
|
||||||
iounmap(p);
|
iounmap(p);
|
||||||
release_mem_region(shmem, MIRROR_SIZE);
|
release_mem_region(shmem, MIRROR_SIZE);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -478,7 +481,7 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
|
|||||||
* 2k (or there are no mirrors at all) but on some, it's 4k.
|
* 2k (or there are no mirrors at all) but on some, it's 4k.
|
||||||
*/
|
*/
|
||||||
mirror_size = MIRROR_SIZE;
|
mirror_size = MIRROR_SIZE;
|
||||||
if (readb(p) == TESTvalue &&
|
if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue &&
|
||||||
check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
|
check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
|
||||||
check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
|
check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
|
||||||
mirror_size = 2 * MIRROR_SIZE;
|
mirror_size = 2 * MIRROR_SIZE;
|
||||||
@ -499,12 +502,14 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
|
|||||||
iounmap(p);
|
iounmap(p);
|
||||||
release_mem_region(shmem, MIRROR_SIZE);
|
release_mem_region(shmem, MIRROR_SIZE);
|
||||||
|
|
||||||
if (!request_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1, "arcnet (90xx)"))
|
if (!request_mem_region(dev->mem_start,
|
||||||
|
dev->mem_end - dev->mem_start + 1,
|
||||||
|
"arcnet (90xx)"))
|
||||||
goto err_free_dev;
|
goto err_free_dev;
|
||||||
|
|
||||||
/* reserve the irq */
|
/* reserve the irq */
|
||||||
if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) {
|
if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) {
|
||||||
BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", airq);
|
arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", airq);
|
||||||
goto err_release_mem;
|
goto err_release_mem;
|
||||||
}
|
}
|
||||||
dev->irq = airq;
|
dev->irq = airq;
|
||||||
@ -518,22 +523,23 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
|
|||||||
lp->hw.owner = THIS_MODULE;
|
lp->hw.owner = THIS_MODULE;
|
||||||
lp->hw.copy_to_card = com90xx_copy_to_card;
|
lp->hw.copy_to_card = com90xx_copy_to_card;
|
||||||
lp->hw.copy_from_card = com90xx_copy_from_card;
|
lp->hw.copy_from_card = com90xx_copy_from_card;
|
||||||
lp->mem_start = ioremap(dev->mem_start, dev->mem_end - dev->mem_start + 1);
|
lp->mem_start = ioremap(dev->mem_start,
|
||||||
|
dev->mem_end - dev->mem_start + 1);
|
||||||
if (!lp->mem_start) {
|
if (!lp->mem_start) {
|
||||||
BUGMSG(D_NORMAL, "Can't remap device memory!\n");
|
arc_printk(D_NORMAL, dev, "Can't remap device memory!\n");
|
||||||
goto err_free_irq;
|
goto err_free_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get and check the station ID from offset 1 in shmem */
|
/* get and check the station ID from offset 1 in shmem */
|
||||||
dev->dev_addr[0] = readb(lp->mem_start + 1);
|
dev->dev_addr[0] = arcnet_readb(lp->mem_start, COM9026_REG_R_STATION);
|
||||||
|
|
||||||
dev->base_addr = ioaddr;
|
dev->base_addr = ioaddr;
|
||||||
|
|
||||||
BUGMSG(D_NORMAL, "COM90xx station %02Xh found at %03lXh, IRQ %d, "
|
arc_printk(D_NORMAL, dev, "COM90xx station %02Xh found at %03lXh, IRQ %d, ShMem %lXh (%ld*%xh).\n",
|
||||||
"ShMem %lXh (%ld*%xh).\n",
|
dev->dev_addr[0],
|
||||||
dev->dev_addr[0],
|
dev->base_addr, dev->irq, dev->mem_start,
|
||||||
dev->base_addr, dev->irq, dev->mem_start,
|
(dev->mem_end - dev->mem_start + 1) / mirror_size,
|
||||||
(dev->mem_end - dev->mem_start + 1) / mirror_size, mirror_size);
|
mirror_size);
|
||||||
|
|
||||||
if (register_netdev(dev))
|
if (register_netdev(dev))
|
||||||
goto err_unmap;
|
goto err_unmap;
|
||||||
@ -552,34 +558,29 @@ err_free_dev:
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void com90xx_command(struct net_device *dev, int cmd)
|
static void com90xx_command(struct net_device *dev, int cmd)
|
||||||
{
|
{
|
||||||
short ioaddr = dev->base_addr;
|
short ioaddr = dev->base_addr;
|
||||||
|
|
||||||
ACOMMAND(cmd);
|
arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int com90xx_status(struct net_device *dev)
|
static int com90xx_status(struct net_device *dev)
|
||||||
{
|
{
|
||||||
short ioaddr = dev->base_addr;
|
short ioaddr = dev->base_addr;
|
||||||
|
|
||||||
return ASTATUS();
|
return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void com90xx_setmask(struct net_device *dev, int mask)
|
static void com90xx_setmask(struct net_device *dev, int mask)
|
||||||
{
|
{
|
||||||
short ioaddr = dev->base_addr;
|
short ioaddr = dev->base_addr;
|
||||||
|
|
||||||
AINTMASK(mask);
|
arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Do a hardware reset on the card, and set up necessary registers.
|
||||||
/*
|
*
|
||||||
* Do a hardware reset on the card, and set up necessary registers.
|
|
||||||
*
|
|
||||||
* This should be called as little as possible, because it disrupts the
|
* This should be called as little as possible, because it disrupts the
|
||||||
* token on the network (causes a RECON) and requires a significant delay.
|
* token on the network (causes a RECON) and requires a significant delay.
|
||||||
*
|
*
|
||||||
@ -590,53 +591,58 @@ static int com90xx_reset(struct net_device *dev, int really_reset)
|
|||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
short ioaddr = dev->base_addr;
|
short ioaddr = dev->base_addr;
|
||||||
|
|
||||||
BUGMSG(D_INIT, "Resetting (status=%02Xh)\n", ASTATUS());
|
arc_printk(D_INIT, dev, "Resetting (status=%02Xh)\n",
|
||||||
|
arcnet_inb(ioaddr, COM9026_REG_R_STATUS));
|
||||||
|
|
||||||
if (really_reset) {
|
if (really_reset) {
|
||||||
/* reset the card */
|
/* reset the card */
|
||||||
inb(_RESET);
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
||||||
mdelay(RESETtime);
|
mdelay(RESETtime);
|
||||||
}
|
}
|
||||||
ACOMMAND(CFLAGScmd | RESETclear); /* clear flags & end reset */
|
/* clear flags & end reset */
|
||||||
ACOMMAND(CFLAGScmd | CONFIGclear);
|
arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* don't do this until we verify that it doesn't hurt older cards! */
|
/* don't do this until we verify that it doesn't hurt older cards! */
|
||||||
/* outb(inb(_CONFIG) | ENABLE16flag, _CONFIG); */
|
arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) | ENABLE16flag,
|
||||||
|
ioaddr, COM9026_REG_RW_CONFIG);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* verify that the ARCnet signature byte is present */
|
/* verify that the ARCnet signature byte is present */
|
||||||
if (readb(lp->mem_start) != TESTvalue) {
|
if (arcnet_readb(lp->mem_start, COM9026_REG_R_STATUS) != TESTvalue) {
|
||||||
if (really_reset)
|
if (really_reset)
|
||||||
BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n");
|
arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/* enable extended (512-byte) packets */
|
/* enable extended (512-byte) packets */
|
||||||
ACOMMAND(CONFIGcmd | EXTconf);
|
arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
|
||||||
|
|
||||||
/* clean out all the memory to make debugging make more sense :) */
|
/* clean out all the memory to make debugging make more sense :) */
|
||||||
BUGLVL(D_DURING)
|
if (BUGLVL(D_DURING))
|
||||||
memset_io(lp->mem_start, 0x42, 2048);
|
memset_io(lp->mem_start, 0x42, 2048);
|
||||||
|
|
||||||
/* done! return success. */
|
/* done! return success. */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
static void com90xx_copy_to_card(struct net_device *dev, int bufnum,
|
||||||
void *buf, int count)
|
int offset, void *buf, int count)
|
||||||
{
|
{
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
|
void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
|
||||||
TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count));
|
|
||||||
|
TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void com90xx_copy_from_card(struct net_device *dev, int bufnum,
|
||||||
static void com90xx_copy_from_card(struct net_device *dev, int bufnum, int offset,
|
int offset, void *buf, int count)
|
||||||
void *buf, int count)
|
|
||||||
{
|
{
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
|
void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
|
||||||
TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
|
||||||
|
}
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
@ -664,7 +670,8 @@ static void __exit com90xx_exit(void)
|
|||||||
free_irq(dev->irq, dev);
|
free_irq(dev->irq, dev);
|
||||||
iounmap(lp->mem_start);
|
iounmap(lp->mem_start);
|
||||||
release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
|
release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
|
||||||
release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
|
release_mem_region(dev->mem_start,
|
||||||
|
dev->mem_end - dev->mem_start + 1);
|
||||||
free_netdev(dev);
|
free_netdev(dev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -679,13 +686,13 @@ static int __init com90xx_setup(char *s)
|
|||||||
|
|
||||||
s = get_options(s, 8, ints);
|
s = get_options(s, 8, ints);
|
||||||
if (!ints[0] && !*s) {
|
if (!ints[0] && !*s) {
|
||||||
printk("com90xx: Disabled.\n");
|
pr_notice("Disabled\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ints[0]) {
|
switch (ints[0]) {
|
||||||
default: /* ERROR */
|
default: /* ERROR */
|
||||||
printk("com90xx: Too many arguments.\n");
|
pr_err("Too many arguments\n");
|
||||||
case 3: /* Mem address */
|
case 3: /* Mem address */
|
||||||
shmem = ints[3];
|
shmem = ints[3];
|
||||||
case 2: /* IRQ */
|
case 2: /* IRQ */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - RFC1051 ("simple" standard) packet encapsulation
|
* Linux ARCnet driver - RFC1051 ("simple" standard) packet encapsulation
|
||||||
*
|
*
|
||||||
* Written 1994-1999 by Avery Pennarun.
|
* Written 1994-1999 by Avery Pennarun.
|
||||||
* Derived from skeleton.c by Donald Becker.
|
* Derived from skeleton.c by Donald Becker.
|
||||||
*
|
*
|
||||||
@ -23,6 +23,9 @@
|
|||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -30,10 +33,8 @@
|
|||||||
#include <net/arp.h>
|
#include <net/arp.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/arcdevice.h>
|
|
||||||
|
|
||||||
#define VERSION "arcnet: RFC1051 \"simple standard\" (`s') encapsulation support loaded.\n"
|
|
||||||
|
|
||||||
|
#include "arcdevice.h"
|
||||||
|
|
||||||
static __be16 type_trans(struct sk_buff *skb, struct net_device *dev);
|
static __be16 type_trans(struct sk_buff *skb, struct net_device *dev);
|
||||||
static void rx(struct net_device *dev, int bufnum,
|
static void rx(struct net_device *dev, int bufnum,
|
||||||
@ -43,9 +44,7 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
|
|||||||
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
||||||
int bufnum);
|
int bufnum);
|
||||||
|
|
||||||
|
static struct ArcProto rfc1051_proto = {
|
||||||
static struct ArcProto rfc1051_proto =
|
|
||||||
{
|
|
||||||
.suffix = 's',
|
.suffix = 's',
|
||||||
.mtu = XMTU - RFC1051_HDR_SIZE,
|
.mtu = XMTU - RFC1051_HDR_SIZE,
|
||||||
.is_ip = 1,
|
.is_ip = 1,
|
||||||
@ -56,10 +55,9 @@ static struct ArcProto rfc1051_proto =
|
|||||||
.ack_tx = NULL
|
.ack_tx = NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int __init arcnet_rfc1051_init(void)
|
static int __init arcnet_rfc1051_init(void)
|
||||||
{
|
{
|
||||||
printk(VERSION);
|
pr_info("%s\n", "RFC1051 \"simple standard\" (`s') encapsulation support loaded");
|
||||||
|
|
||||||
arc_proto_map[ARC_P_IP_RFC1051]
|
arc_proto_map[ARC_P_IP_RFC1051]
|
||||||
= arc_proto_map[ARC_P_ARP_RFC1051]
|
= arc_proto_map[ARC_P_ARP_RFC1051]
|
||||||
@ -82,14 +80,13 @@ module_exit(arcnet_rfc1051_exit);
|
|||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
/*
|
/* Determine a packet's protocol ID.
|
||||||
* Determine a packet's protocol ID.
|
*
|
||||||
*
|
|
||||||
* With ARCnet we have to convert everything to Ethernet-style stuff.
|
* With ARCnet we have to convert everything to Ethernet-style stuff.
|
||||||
*/
|
*/
|
||||||
static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
|
static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct archdr *pkt = (struct archdr *) skb->data;
|
struct archdr *pkt = (struct archdr *)skb->data;
|
||||||
struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
|
struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
|
||||||
int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
|
int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
|
||||||
|
|
||||||
@ -97,9 +94,9 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
|
|||||||
skb_reset_mac_header(skb);
|
skb_reset_mac_header(skb);
|
||||||
skb_pull(skb, hdr_size);
|
skb_pull(skb, hdr_size);
|
||||||
|
|
||||||
if (pkt->hard.dest == 0)
|
if (pkt->hard.dest == 0) {
|
||||||
skb->pkt_type = PACKET_BROADCAST;
|
skb->pkt_type = PACKET_BROADCAST;
|
||||||
else if (dev->flags & IFF_PROMISC) {
|
} else if (dev->flags & IFF_PROMISC) {
|
||||||
/* if we're not sending to ourselves :) */
|
/* if we're not sending to ourselves :) */
|
||||||
if (pkt->hard.dest != dev->dev_addr[0])
|
if (pkt->hard.dest != dev->dev_addr[0])
|
||||||
skb->pkt_type = PACKET_OTHERHOST;
|
skb->pkt_type = PACKET_OTHERHOST;
|
||||||
@ -120,7 +117,6 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
|
|||||||
return htons(ETH_P_IP);
|
return htons(ETH_P_IP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* packet receiver */
|
/* packet receiver */
|
||||||
static void rx(struct net_device *dev, int bufnum,
|
static void rx(struct net_device *dev, int bufnum,
|
||||||
struct archdr *pkthdr, int length)
|
struct archdr *pkthdr, int length)
|
||||||
@ -130,7 +126,7 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
struct archdr *pkt = pkthdr;
|
struct archdr *pkt = pkthdr;
|
||||||
int ofs;
|
int ofs;
|
||||||
|
|
||||||
BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length);
|
arc_printk(D_DURING, dev, "it's a raw packet (length=%d)\n", length);
|
||||||
|
|
||||||
if (length >= MinTU)
|
if (length >= MinTU)
|
||||||
ofs = 512 - length;
|
ofs = 512 - length;
|
||||||
@ -138,15 +134,14 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
ofs = 256 - length;
|
ofs = 256 - length;
|
||||||
|
|
||||||
skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
|
skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
|
||||||
if (skb == NULL) {
|
if (!skb) {
|
||||||
BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
|
|
||||||
dev->stats.rx_dropped++;
|
dev->stats.rx_dropped++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
skb_put(skb, length + ARC_HDR_SIZE);
|
skb_put(skb, length + ARC_HDR_SIZE);
|
||||||
skb->dev = dev;
|
skb->dev = dev;
|
||||||
|
|
||||||
pkt = (struct archdr *) skb->data;
|
pkt = (struct archdr *)skb->data;
|
||||||
|
|
||||||
/* up to sizeof(pkt->soft) has already been copied from the card */
|
/* up to sizeof(pkt->soft) has already been copied from the card */
|
||||||
memcpy(pkt, pkthdr, sizeof(struct archdr));
|
memcpy(pkt, pkthdr, sizeof(struct archdr));
|
||||||
@ -155,21 +150,19 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
pkt->soft.raw + sizeof(pkt->soft),
|
pkt->soft.raw + sizeof(pkt->soft),
|
||||||
length - sizeof(pkt->soft));
|
length - sizeof(pkt->soft));
|
||||||
|
|
||||||
BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
|
if (BUGLVL(D_SKB))
|
||||||
|
arcnet_dump_skb(dev, skb, "rx");
|
||||||
|
|
||||||
skb->protocol = type_trans(skb, dev);
|
skb->protocol = type_trans(skb, dev);
|
||||||
netif_rx(skb);
|
netif_rx(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Create the ARCnet hard/soft headers for RFC1051 */
|
||||||
/*
|
|
||||||
* Create the ARCnet hard/soft headers for RFC1051.
|
|
||||||
*/
|
|
||||||
static int build_header(struct sk_buff *skb, struct net_device *dev,
|
static int build_header(struct sk_buff *skb, struct net_device *dev,
|
||||||
unsigned short type, uint8_t daddr)
|
unsigned short type, uint8_t daddr)
|
||||||
{
|
{
|
||||||
int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
|
int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
|
||||||
struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
|
struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size);
|
||||||
struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
|
struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
|
||||||
|
|
||||||
/* set the protocol ID according to RFC1051 */
|
/* set the protocol ID according to RFC1051 */
|
||||||
@ -181,29 +174,26 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
|
|||||||
soft->proto = ARC_P_ARP_RFC1051;
|
soft->proto = ARC_P_ARP_RFC1051;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUGMSG(D_NORMAL, "RFC1051: I don't understand protocol %d (%Xh)\n",
|
arc_printk(D_NORMAL, dev, "RFC1051: I don't understand protocol %d (%Xh)\n",
|
||||||
type, type);
|
type, type);
|
||||||
dev->stats.tx_errors++;
|
dev->stats.tx_errors++;
|
||||||
dev->stats.tx_aborted_errors++;
|
dev->stats.tx_aborted_errors++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set the source hardware address.
|
||||||
/*
|
|
||||||
* Set the source hardware address.
|
|
||||||
*
|
*
|
||||||
* This is pretty pointless for most purposes, but it can help in
|
* This is pretty pointless for most purposes, but it can help in
|
||||||
* debugging. ARCnet does not allow us to change the source address in
|
* debugging. ARCnet does not allow us to change the source address
|
||||||
* the actual packet sent)
|
* in the actual packet sent.
|
||||||
*/
|
*/
|
||||||
pkt->hard.source = *dev->dev_addr;
|
pkt->hard.source = *dev->dev_addr;
|
||||||
|
|
||||||
/* see linux/net/ethernet/eth.c to see where I got the following */
|
/* see linux/net/ethernet/eth.c to see where I got the following */
|
||||||
|
|
||||||
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
|
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
|
||||||
/*
|
/* FIXME: fill in the last byte of the dest ipaddr here to
|
||||||
* FIXME: fill in the last byte of the dest ipaddr here to better
|
* better comply with RFC1051 in "noarp" mode.
|
||||||
* comply with RFC1051 in "noarp" mode.
|
|
||||||
*/
|
*/
|
||||||
pkt->hard.dest = 0;
|
pkt->hard.dest = 0;
|
||||||
return hdr_size;
|
return hdr_size;
|
||||||
@ -214,7 +204,6 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
|
|||||||
return hdr_size; /* success */
|
return hdr_size; /* success */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
||||||
int bufnum)
|
int bufnum)
|
||||||
{
|
{
|
||||||
@ -222,15 +211,16 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|||||||
struct arc_hardware *hard = &pkt->hard;
|
struct arc_hardware *hard = &pkt->hard;
|
||||||
int ofs;
|
int ofs;
|
||||||
|
|
||||||
BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
|
arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n",
|
||||||
lp->next_tx, lp->cur_tx, bufnum);
|
lp->next_tx, lp->cur_tx, bufnum);
|
||||||
|
|
||||||
length -= ARC_HDR_SIZE; /* hard header is not included in packet length */
|
/* hard header is not included in packet length */
|
||||||
|
length -= ARC_HDR_SIZE;
|
||||||
|
|
||||||
if (length > XMTU) {
|
if (length > XMTU) {
|
||||||
/* should never happen! other people already check for this. */
|
/* should never happen! other people already check for this. */
|
||||||
BUGMSG(D_NORMAL, "Bug! prepare_tx with size %d (> %d)\n",
|
arc_printk(D_NORMAL, dev, "Bug! prepare_tx with size %d (> %d)\n",
|
||||||
length, XMTU);
|
length, XMTU);
|
||||||
length = XMTU;
|
length = XMTU;
|
||||||
}
|
}
|
||||||
if (length > MinTU) {
|
if (length > MinTU) {
|
||||||
@ -239,8 +229,9 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|||||||
} else if (length > MTU) {
|
} else if (length > MTU) {
|
||||||
hard->offset[0] = 0;
|
hard->offset[0] = 0;
|
||||||
hard->offset[1] = ofs = 512 - length - 3;
|
hard->offset[1] = ofs = 512 - length - 3;
|
||||||
} else
|
} else {
|
||||||
hard->offset[0] = ofs = 256 - length;
|
hard->offset[0] = ofs = 256 - length;
|
||||||
|
}
|
||||||
|
|
||||||
lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
|
lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
|
||||||
lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length);
|
lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Linux ARCnet driver - RFC1201 (standard) packet encapsulation
|
* Linux ARCnet driver - RFC1201 (standard) packet encapsulation
|
||||||
*
|
*
|
||||||
* Written 1994-1999 by Avery Pennarun.
|
* Written 1994-1999 by Avery Pennarun.
|
||||||
* Derived from skeleton.c by Donald Becker.
|
* Derived from skeleton.c by Donald Becker.
|
||||||
*
|
*
|
||||||
@ -23,17 +23,19 @@
|
|||||||
*
|
*
|
||||||
* **********************
|
* **********************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/if_arp.h>
|
#include <linux/if_arp.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/arcdevice.h>
|
|
||||||
|
#include "arcdevice.h"
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
#define VERSION "arcnet: RFC1201 \"standard\" (`a') encapsulation support loaded.\n"
|
|
||||||
|
|
||||||
|
|
||||||
static __be16 type_trans(struct sk_buff *skb, struct net_device *dev);
|
static __be16 type_trans(struct sk_buff *skb, struct net_device *dev);
|
||||||
static void rx(struct net_device *dev, int bufnum,
|
static void rx(struct net_device *dev, int bufnum,
|
||||||
@ -44,8 +46,7 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|||||||
int bufnum);
|
int bufnum);
|
||||||
static int continue_tx(struct net_device *dev, int bufnum);
|
static int continue_tx(struct net_device *dev, int bufnum);
|
||||||
|
|
||||||
static struct ArcProto rfc1201_proto =
|
static struct ArcProto rfc1201_proto = {
|
||||||
{
|
|
||||||
.suffix = 'a',
|
.suffix = 'a',
|
||||||
.mtu = 1500, /* could be more, but some receivers can't handle it... */
|
.mtu = 1500, /* could be more, but some receivers can't handle it... */
|
||||||
.is_ip = 1, /* This is for sending IP and ARP packages */
|
.is_ip = 1, /* This is for sending IP and ARP packages */
|
||||||
@ -56,10 +57,9 @@ static struct ArcProto rfc1201_proto =
|
|||||||
.ack_tx = NULL
|
.ack_tx = NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int __init arcnet_rfc1201_init(void)
|
static int __init arcnet_rfc1201_init(void)
|
||||||
{
|
{
|
||||||
printk(VERSION);
|
pr_info("%s\n", "RFC1201 \"standard\" (`a') encapsulation support loaded");
|
||||||
|
|
||||||
arc_proto_map[ARC_P_IP]
|
arc_proto_map[ARC_P_IP]
|
||||||
= arc_proto_map[ARC_P_IPV6]
|
= arc_proto_map[ARC_P_IPV6]
|
||||||
@ -84,14 +84,13 @@ static void __exit arcnet_rfc1201_exit(void)
|
|||||||
module_init(arcnet_rfc1201_init);
|
module_init(arcnet_rfc1201_init);
|
||||||
module_exit(arcnet_rfc1201_exit);
|
module_exit(arcnet_rfc1201_exit);
|
||||||
|
|
||||||
/*
|
/* Determine a packet's protocol ID.
|
||||||
* Determine a packet's protocol ID.
|
*
|
||||||
*
|
|
||||||
* With ARCnet we have to convert everything to Ethernet-style stuff.
|
* With ARCnet we have to convert everything to Ethernet-style stuff.
|
||||||
*/
|
*/
|
||||||
static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
|
static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct archdr *pkt = (struct archdr *) skb->data;
|
struct archdr *pkt = (struct archdr *)skb->data;
|
||||||
struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
|
struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
|
||||||
int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
|
int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
|
||||||
|
|
||||||
@ -99,9 +98,9 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
|
|||||||
skb_reset_mac_header(skb);
|
skb_reset_mac_header(skb);
|
||||||
skb_pull(skb, hdr_size);
|
skb_pull(skb, hdr_size);
|
||||||
|
|
||||||
if (pkt->hard.dest == 0)
|
if (pkt->hard.dest == 0) {
|
||||||
skb->pkt_type = PACKET_BROADCAST;
|
skb->pkt_type = PACKET_BROADCAST;
|
||||||
else if (dev->flags & IFF_PROMISC) {
|
} else if (dev->flags & IFF_PROMISC) {
|
||||||
/* if we're not sending to ourselves :) */
|
/* if we're not sending to ourselves :) */
|
||||||
if (pkt->hard.dest != dev->dev_addr[0])
|
if (pkt->hard.dest != dev->dev_addr[0])
|
||||||
skb->pkt_type = PACKET_OTHERHOST;
|
skb->pkt_type = PACKET_OTHERHOST;
|
||||||
@ -129,7 +128,6 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
|
|||||||
return htons(ETH_P_IP);
|
return htons(ETH_P_IP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* packet receiver */
|
/* packet receiver */
|
||||||
static void rx(struct net_device *dev, int bufnum,
|
static void rx(struct net_device *dev, int bufnum,
|
||||||
struct archdr *pkthdr, int length)
|
struct archdr *pkthdr, int length)
|
||||||
@ -141,7 +139,8 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
int saddr = pkt->hard.source, ofs;
|
int saddr = pkt->hard.source, ofs;
|
||||||
struct Incoming *in = &lp->rfc1201.incoming[saddr];
|
struct Incoming *in = &lp->rfc1201.incoming[saddr];
|
||||||
|
|
||||||
BUGMSG(D_DURING, "it's an RFC1201 packet (length=%d)\n", length);
|
arc_printk(D_DURING, dev, "it's an RFC1201 packet (length=%d)\n",
|
||||||
|
length);
|
||||||
|
|
||||||
if (length >= MinTU)
|
if (length >= MinTU)
|
||||||
ofs = 512 - length;
|
ofs = 512 - length;
|
||||||
@ -149,11 +148,11 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
ofs = 256 - length;
|
ofs = 256 - length;
|
||||||
|
|
||||||
if (soft->split_flag == 0xFF) { /* Exception Packet */
|
if (soft->split_flag == 0xFF) { /* Exception Packet */
|
||||||
if (length >= 4 + RFC1201_HDR_SIZE)
|
if (length >= 4 + RFC1201_HDR_SIZE) {
|
||||||
BUGMSG(D_DURING, "compensating for exception packet\n");
|
arc_printk(D_DURING, dev, "compensating for exception packet\n");
|
||||||
else {
|
} else {
|
||||||
BUGMSG(D_EXTRA, "short RFC1201 exception packet from %02Xh",
|
arc_printk(D_EXTRA, dev, "short RFC1201 exception packet from %02Xh",
|
||||||
saddr);
|
saddr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,12 +163,13 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
soft, sizeof(pkt->soft));
|
soft, sizeof(pkt->soft));
|
||||||
}
|
}
|
||||||
if (!soft->split_flag) { /* not split */
|
if (!soft->split_flag) { /* not split */
|
||||||
BUGMSG(D_RX, "incoming is not split (splitflag=%d)\n",
|
arc_printk(D_RX, dev, "incoming is not split (splitflag=%d)\n",
|
||||||
soft->split_flag);
|
soft->split_flag);
|
||||||
|
|
||||||
if (in->skb) { /* already assembling one! */
|
if (in->skb) { /* already assembling one! */
|
||||||
BUGMSG(D_EXTRA, "aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
|
arc_printk(D_EXTRA, dev, "aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
|
||||||
in->sequence, soft->split_flag, soft->sequence);
|
in->sequence, soft->split_flag,
|
||||||
|
soft->sequence);
|
||||||
lp->rfc1201.aborted_seq = soft->sequence;
|
lp->rfc1201.aborted_seq = soft->sequence;
|
||||||
dev_kfree_skb_irq(in->skb);
|
dev_kfree_skb_irq(in->skb);
|
||||||
dev->stats.rx_errors++;
|
dev->stats.rx_errors++;
|
||||||
@ -179,82 +179,86 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
in->sequence = soft->sequence;
|
in->sequence = soft->sequence;
|
||||||
|
|
||||||
skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
|
skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
|
||||||
if (skb == NULL) {
|
if (!skb) {
|
||||||
BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
|
|
||||||
dev->stats.rx_dropped++;
|
dev->stats.rx_dropped++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
skb_put(skb, length + ARC_HDR_SIZE);
|
skb_put(skb, length + ARC_HDR_SIZE);
|
||||||
skb->dev = dev;
|
skb->dev = dev;
|
||||||
|
|
||||||
pkt = (struct archdr *) skb->data;
|
pkt = (struct archdr *)skb->data;
|
||||||
soft = &pkt->soft.rfc1201;
|
soft = &pkt->soft.rfc1201;
|
||||||
|
|
||||||
/* up to sizeof(pkt->soft) has already been copied from the card */
|
/* up to sizeof(pkt->soft) has already
|
||||||
|
* been copied from the card
|
||||||
|
*/
|
||||||
memcpy(pkt, pkthdr, sizeof(struct archdr));
|
memcpy(pkt, pkthdr, sizeof(struct archdr));
|
||||||
if (length > sizeof(pkt->soft))
|
if (length > sizeof(pkt->soft))
|
||||||
lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
|
lp->hw.copy_from_card(dev, bufnum,
|
||||||
pkt->soft.raw + sizeof(pkt->soft),
|
ofs + sizeof(pkt->soft),
|
||||||
|
pkt->soft.raw + sizeof(pkt->soft),
|
||||||
length - sizeof(pkt->soft));
|
length - sizeof(pkt->soft));
|
||||||
|
|
||||||
/*
|
/* ARP packets have problems when sent from some DOS systems:
|
||||||
* ARP packets have problems when sent from some DOS systems: the
|
* the source address is always 0!
|
||||||
* source address is always 0! So we take the hardware source addr
|
* So we take the hardware source addr (which is impossible
|
||||||
* (which is impossible to fumble) and insert it ourselves.
|
* to fumble) and insert it ourselves.
|
||||||
*/
|
*/
|
||||||
if (soft->proto == ARC_P_ARP) {
|
if (soft->proto == ARC_P_ARP) {
|
||||||
struct arphdr *arp = (struct arphdr *) soft->payload;
|
struct arphdr *arp = (struct arphdr *)soft->payload;
|
||||||
|
|
||||||
/* make sure addresses are the right length */
|
/* make sure addresses are the right length */
|
||||||
if (arp->ar_hln == 1 && arp->ar_pln == 4) {
|
if (arp->ar_hln == 1 && arp->ar_pln == 4) {
|
||||||
uint8_t *cptr = (uint8_t *) arp + sizeof(struct arphdr);
|
uint8_t *cptr = (uint8_t *)arp + sizeof(struct arphdr);
|
||||||
|
|
||||||
if (!*cptr) { /* is saddr = 00? */
|
if (!*cptr) { /* is saddr = 00? */
|
||||||
BUGMSG(D_EXTRA,
|
arc_printk(D_EXTRA, dev,
|
||||||
"ARP source address was 00h, set to %02Xh.\n",
|
"ARP source address was 00h, set to %02Xh\n",
|
||||||
saddr);
|
saddr);
|
||||||
dev->stats.rx_crc_errors++;
|
dev->stats.rx_crc_errors++;
|
||||||
*cptr = saddr;
|
*cptr = saddr;
|
||||||
} else {
|
} else {
|
||||||
BUGMSG(D_DURING, "ARP source address (%Xh) is fine.\n",
|
arc_printk(D_DURING, dev, "ARP source address (%Xh) is fine.\n",
|
||||||
*cptr);
|
*cptr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
BUGMSG(D_NORMAL, "funny-shaped ARP packet. (%Xh, %Xh)\n",
|
arc_printk(D_NORMAL, dev, "funny-shaped ARP packet. (%Xh, %Xh)\n",
|
||||||
arp->ar_hln, arp->ar_pln);
|
arp->ar_hln, arp->ar_pln);
|
||||||
dev->stats.rx_errors++;
|
dev->stats.rx_errors++;
|
||||||
dev->stats.rx_crc_errors++;
|
dev->stats.rx_crc_errors++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
|
if (BUGLVL(D_SKB))
|
||||||
|
arcnet_dump_skb(dev, skb, "rx");
|
||||||
|
|
||||||
skb->protocol = type_trans(skb, dev);
|
skb->protocol = type_trans(skb, dev);
|
||||||
netif_rx(skb);
|
netif_rx(skb);
|
||||||
} else { /* split packet */
|
} else { /* split packet */
|
||||||
/*
|
/* NOTE: MSDOS ARP packet correction should only need to
|
||||||
* NOTE: MSDOS ARP packet correction should only need to apply to
|
* apply to unsplit packets, since ARP packets are so short.
|
||||||
* unsplit packets, since ARP packets are so short.
|
|
||||||
*
|
*
|
||||||
* My interpretation of the RFC1201 document is that if a packet is
|
* My interpretation of the RFC1201 document is that if a
|
||||||
* received out of order, the entire assembly process should be
|
* packet is received out of order, the entire assembly
|
||||||
* aborted.
|
* process should be aborted.
|
||||||
*
|
*
|
||||||
* The RFC also mentions "it is possible for successfully received
|
* The RFC also mentions "it is possible for successfully
|
||||||
* packets to be retransmitted." As of 0.40 all previously received
|
* received packets to be retransmitted." As of 0.40 all
|
||||||
* packets are allowed, not just the most recent one.
|
* previously received packets are allowed, not just the
|
||||||
|
* most recent one.
|
||||||
*
|
*
|
||||||
* We allow multiple assembly processes, one for each ARCnet card
|
* We allow multiple assembly processes, one for each
|
||||||
* possible on the network. Seems rather like a waste of memory,
|
* ARCnet card possible on the network.
|
||||||
* but there's no other way to be reliable.
|
* Seems rather like a waste of memory, but there's no
|
||||||
|
* other way to be reliable.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
BUGMSG(D_RX, "packet is split (splitflag=%d, seq=%d)\n",
|
arc_printk(D_RX, dev, "packet is split (splitflag=%d, seq=%d)\n",
|
||||||
soft->split_flag, in->sequence);
|
soft->split_flag, in->sequence);
|
||||||
|
|
||||||
if (in->skb && in->sequence != soft->sequence) {
|
if (in->skb && in->sequence != soft->sequence) {
|
||||||
BUGMSG(D_EXTRA, "wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
|
arc_printk(D_EXTRA, dev, "wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
|
||||||
saddr, in->sequence, soft->sequence,
|
saddr, in->sequence, soft->sequence,
|
||||||
soft->split_flag);
|
soft->split_flag);
|
||||||
dev_kfree_skb_irq(in->skb);
|
dev_kfree_skb_irq(in->skb);
|
||||||
in->skb = NULL;
|
in->skb = NULL;
|
||||||
dev->stats.rx_errors++;
|
dev->stats.rx_errors++;
|
||||||
@ -262,24 +266,23 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
in->lastpacket = in->numpackets = 0;
|
in->lastpacket = in->numpackets = 0;
|
||||||
}
|
}
|
||||||
if (soft->split_flag & 1) { /* first packet in split */
|
if (soft->split_flag & 1) { /* first packet in split */
|
||||||
BUGMSG(D_RX, "brand new splitpacket (splitflag=%d)\n",
|
arc_printk(D_RX, dev, "brand new splitpacket (splitflag=%d)\n",
|
||||||
soft->split_flag);
|
soft->split_flag);
|
||||||
if (in->skb) { /* already assembling one! */
|
if (in->skb) { /* already assembling one! */
|
||||||
BUGMSG(D_EXTRA, "aborting previous (seq=%d) assembly "
|
arc_printk(D_EXTRA, dev, "aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n",
|
||||||
"(splitflag=%d, seq=%d)\n",
|
in->sequence, soft->split_flag,
|
||||||
in->sequence, soft->split_flag,
|
soft->sequence);
|
||||||
soft->sequence);
|
|
||||||
dev->stats.rx_errors++;
|
dev->stats.rx_errors++;
|
||||||
dev->stats.rx_missed_errors++;
|
dev->stats.rx_missed_errors++;
|
||||||
dev_kfree_skb_irq(in->skb);
|
dev_kfree_skb_irq(in->skb);
|
||||||
}
|
}
|
||||||
in->sequence = soft->sequence;
|
in->sequence = soft->sequence;
|
||||||
in->numpackets = ((unsigned) soft->split_flag >> 1) + 2;
|
in->numpackets = ((unsigned)soft->split_flag >> 1) + 2;
|
||||||
in->lastpacket = 1;
|
in->lastpacket = 1;
|
||||||
|
|
||||||
if (in->numpackets > 16) {
|
if (in->numpackets > 16) {
|
||||||
BUGMSG(D_EXTRA, "incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
|
arc_printk(D_EXTRA, dev, "incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
|
||||||
soft->split_flag);
|
soft->split_flag);
|
||||||
lp->rfc1201.aborted_seq = soft->sequence;
|
lp->rfc1201.aborted_seq = soft->sequence;
|
||||||
dev->stats.rx_errors++;
|
dev->stats.rx_errors++;
|
||||||
dev->stats.rx_length_errors++;
|
dev->stats.rx_length_errors++;
|
||||||
@ -287,14 +290,14 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
}
|
}
|
||||||
in->skb = skb = alloc_skb(508 * in->numpackets + ARC_HDR_SIZE,
|
in->skb = skb = alloc_skb(508 * in->numpackets + ARC_HDR_SIZE,
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
if (skb == NULL) {
|
if (!skb) {
|
||||||
BUGMSG(D_NORMAL, "(split) memory squeeze, dropping packet.\n");
|
arc_printk(D_NORMAL, dev, "(split) memory squeeze, dropping packet.\n");
|
||||||
lp->rfc1201.aborted_seq = soft->sequence;
|
lp->rfc1201.aborted_seq = soft->sequence;
|
||||||
dev->stats.rx_dropped++;
|
dev->stats.rx_dropped++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
skb->dev = dev;
|
skb->dev = dev;
|
||||||
pkt = (struct archdr *) skb->data;
|
pkt = (struct archdr *)skb->data;
|
||||||
soft = &pkt->soft.rfc1201;
|
soft = &pkt->soft.rfc1201;
|
||||||
|
|
||||||
memcpy(pkt, pkthdr, ARC_HDR_SIZE + RFC1201_HDR_SIZE);
|
memcpy(pkt, pkthdr, ARC_HDR_SIZE + RFC1201_HDR_SIZE);
|
||||||
@ -302,37 +305,37 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
|
|
||||||
soft->split_flag = 0; /* end result won't be split */
|
soft->split_flag = 0; /* end result won't be split */
|
||||||
} else { /* not first packet */
|
} else { /* not first packet */
|
||||||
int packetnum = ((unsigned) soft->split_flag >> 1) + 1;
|
int packetnum = ((unsigned)soft->split_flag >> 1) + 1;
|
||||||
|
|
||||||
/*
|
/* if we're not assembling, there's no point trying to
|
||||||
* if we're not assembling, there's no point trying to
|
|
||||||
* continue.
|
* continue.
|
||||||
*/
|
*/
|
||||||
if (!in->skb) {
|
if (!in->skb) {
|
||||||
if (lp->rfc1201.aborted_seq != soft->sequence) {
|
if (lp->rfc1201.aborted_seq != soft->sequence) {
|
||||||
BUGMSG(D_EXTRA, "can't continue split without starting "
|
arc_printk(D_EXTRA, dev, "can't continue split without starting first! (splitflag=%d, seq=%d, aborted=%d)\n",
|
||||||
"first! (splitflag=%d, seq=%d, aborted=%d)\n",
|
soft->split_flag,
|
||||||
soft->split_flag, soft->sequence,
|
soft->sequence,
|
||||||
lp->rfc1201.aborted_seq);
|
lp->rfc1201.aborted_seq);
|
||||||
dev->stats.rx_errors++;
|
dev->stats.rx_errors++;
|
||||||
dev->stats.rx_missed_errors++;
|
dev->stats.rx_missed_errors++;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
in->lastpacket++;
|
in->lastpacket++;
|
||||||
if (packetnum != in->lastpacket) { /* not the right flag! */
|
/* if not the right flag */
|
||||||
|
if (packetnum != in->lastpacket) {
|
||||||
/* harmless duplicate? ignore. */
|
/* harmless duplicate? ignore. */
|
||||||
if (packetnum <= in->lastpacket - 1) {
|
if (packetnum <= in->lastpacket - 1) {
|
||||||
BUGMSG(D_EXTRA, "duplicate splitpacket ignored! (splitflag=%d)\n",
|
arc_printk(D_EXTRA, dev, "duplicate splitpacket ignored! (splitflag=%d)\n",
|
||||||
soft->split_flag);
|
soft->split_flag);
|
||||||
dev->stats.rx_errors++;
|
dev->stats.rx_errors++;
|
||||||
dev->stats.rx_frame_errors++;
|
dev->stats.rx_frame_errors++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* "bad" duplicate, kill reassembly */
|
/* "bad" duplicate, kill reassembly */
|
||||||
BUGMSG(D_EXTRA, "out-of-order splitpacket, reassembly "
|
arc_printk(D_EXTRA, dev, "out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n",
|
||||||
"(seq=%d) aborted (splitflag=%d, seq=%d)\n",
|
in->sequence, soft->split_flag,
|
||||||
in->sequence, soft->split_flag, soft->sequence);
|
soft->sequence);
|
||||||
lp->rfc1201.aborted_seq = soft->sequence;
|
lp->rfc1201.aborted_seq = soft->sequence;
|
||||||
dev_kfree_skb_irq(in->skb);
|
dev_kfree_skb_irq(in->skb);
|
||||||
in->skb = NULL;
|
in->skb = NULL;
|
||||||
@ -341,7 +344,7 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
in->lastpacket = in->numpackets = 0;
|
in->lastpacket = in->numpackets = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pkt = (struct archdr *) in->skb->data;
|
pkt = (struct archdr *)in->skb->data;
|
||||||
soft = &pkt->soft.rfc1201;
|
soft = &pkt->soft.rfc1201;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,11 +360,12 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
in->skb = NULL;
|
in->skb = NULL;
|
||||||
in->lastpacket = in->numpackets = 0;
|
in->lastpacket = in->numpackets = 0;
|
||||||
|
|
||||||
BUGMSG(D_SKB_SIZE, "skb: received %d bytes from %02X (unsplit)\n",
|
arc_printk(D_SKB_SIZE, dev, "skb: received %d bytes from %02X (unsplit)\n",
|
||||||
skb->len, pkt->hard.source);
|
skb->len, pkt->hard.source);
|
||||||
BUGMSG(D_SKB_SIZE, "skb: received %d bytes from %02X (split)\n",
|
arc_printk(D_SKB_SIZE, dev, "skb: received %d bytes from %02X (split)\n",
|
||||||
skb->len, pkt->hard.source);
|
skb->len, pkt->hard.source);
|
||||||
BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
|
if (BUGLVL(D_SKB))
|
||||||
|
arcnet_dump_skb(dev, skb, "rx");
|
||||||
|
|
||||||
skb->protocol = type_trans(skb, dev);
|
skb->protocol = type_trans(skb, dev);
|
||||||
netif_rx(skb);
|
netif_rx(skb);
|
||||||
@ -369,14 +373,13 @@ static void rx(struct net_device *dev, int bufnum,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Create the ARCnet hard/soft headers for RFC1201. */
|
/* Create the ARCnet hard/soft headers for RFC1201. */
|
||||||
static int build_header(struct sk_buff *skb, struct net_device *dev,
|
static int build_header(struct sk_buff *skb, struct net_device *dev,
|
||||||
unsigned short type, uint8_t daddr)
|
unsigned short type, uint8_t daddr)
|
||||||
{
|
{
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
|
int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
|
||||||
struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
|
struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size);
|
||||||
struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
|
struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
|
||||||
|
|
||||||
/* set the protocol ID according to RFC1201 */
|
/* set the protocol ID according to RFC1201 */
|
||||||
@ -402,19 +405,18 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
|
|||||||
soft->proto = ARC_P_ATALK;
|
soft->proto = ARC_P_ATALK;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUGMSG(D_NORMAL, "RFC1201: I don't understand protocol %d (%Xh)\n",
|
arc_printk(D_NORMAL, dev, "RFC1201: I don't understand protocol %d (%Xh)\n",
|
||||||
type, type);
|
type, type);
|
||||||
dev->stats.tx_errors++;
|
dev->stats.tx_errors++;
|
||||||
dev->stats.tx_aborted_errors++;
|
dev->stats.tx_aborted_errors++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Set the source hardware address.
|
||||||
* Set the source hardware address.
|
|
||||||
*
|
*
|
||||||
* This is pretty pointless for most purposes, but it can help in
|
* This is pretty pointless for most purposes, but it can help in
|
||||||
* debugging. ARCnet does not allow us to change the source address in
|
* debugging. ARCnet does not allow us to change the source address
|
||||||
* the actual packet sent)
|
* in the actual packet sent.
|
||||||
*/
|
*/
|
||||||
pkt->hard.source = *dev->dev_addr;
|
pkt->hard.source = *dev->dev_addr;
|
||||||
|
|
||||||
@ -424,10 +426,10 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
|
|||||||
/* see linux/net/ethernet/eth.c to see where I got the following */
|
/* see linux/net/ethernet/eth.c to see where I got the following */
|
||||||
|
|
||||||
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
|
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
|
||||||
/*
|
/* FIXME: fill in the last byte of the dest ipaddr here
|
||||||
* FIXME: fill in the last byte of the dest ipaddr here to better
|
* to better comply with RFC1051 in "noarp" mode.
|
||||||
* comply with RFC1051 in "noarp" mode. For now, always broadcasting
|
* For now, always broadcasting will probably at least get
|
||||||
* will probably at least get packets sent out :)
|
* packets sent out :)
|
||||||
*/
|
*/
|
||||||
pkt->hard.dest = 0;
|
pkt->hard.dest = 0;
|
||||||
return hdr_size;
|
return hdr_size;
|
||||||
@ -437,7 +439,6 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
|
|||||||
return hdr_size;
|
return hdr_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
|
static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
|
||||||
struct arc_rfc1201 *soft, int softlen, int bufnum)
|
struct arc_rfc1201 *soft, int softlen, int bufnum)
|
||||||
{
|
{
|
||||||
@ -461,8 +462,9 @@ static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
|
|||||||
hard->offset[1] = ofs - RFC1201_HDR_SIZE;
|
hard->offset[1] = ofs - RFC1201_HDR_SIZE;
|
||||||
lp->hw.copy_to_card(dev, bufnum, ofs - RFC1201_HDR_SIZE,
|
lp->hw.copy_to_card(dev, bufnum, ofs - RFC1201_HDR_SIZE,
|
||||||
&excsoft, RFC1201_HDR_SIZE);
|
&excsoft, RFC1201_HDR_SIZE);
|
||||||
} else
|
} else {
|
||||||
hard->offset[0] = ofs = 256 - softlen;
|
hard->offset[0] = ofs = 256 - softlen;
|
||||||
|
}
|
||||||
|
|
||||||
lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
|
lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
|
||||||
lp->hw.copy_to_card(dev, bufnum, ofs, soft, softlen);
|
lp->hw.copy_to_card(dev, bufnum, ofs, soft, softlen);
|
||||||
@ -470,7 +472,6 @@ static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
|
|||||||
lp->lastload_dest = hard->dest;
|
lp->lastload_dest = hard->dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
||||||
int bufnum)
|
int bufnum)
|
||||||
{
|
{
|
||||||
@ -478,11 +479,11 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|||||||
const int maxsegsize = XMTU - RFC1201_HDR_SIZE;
|
const int maxsegsize = XMTU - RFC1201_HDR_SIZE;
|
||||||
struct Outgoing *out;
|
struct Outgoing *out;
|
||||||
|
|
||||||
|
arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n",
|
||||||
|
lp->next_tx, lp->cur_tx, bufnum);
|
||||||
|
|
||||||
BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
|
/* hard header is not included in packet length */
|
||||||
lp->next_tx, lp->cur_tx, bufnum);
|
length -= ARC_HDR_SIZE;
|
||||||
|
|
||||||
length -= ARC_HDR_SIZE; /* hard header is not included in packet length */
|
|
||||||
pkt->soft.rfc1201.split_flag = 0;
|
pkt->soft.rfc1201.split_flag = 0;
|
||||||
|
|
||||||
/* need to do a split packet? */
|
/* need to do a split packet? */
|
||||||
@ -494,9 +495,9 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|||||||
out->numsegs = (out->dataleft + maxsegsize - 1) / maxsegsize;
|
out->numsegs = (out->dataleft + maxsegsize - 1) / maxsegsize;
|
||||||
out->segnum = 0;
|
out->segnum = 0;
|
||||||
|
|
||||||
BUGMSG(D_DURING, "rfc1201 prep_tx: ready for %d-segment split "
|
arc_printk(D_DURING, dev, "rfc1201 prep_tx: ready for %d-segment split (%d bytes, seq=%d)\n",
|
||||||
"(%d bytes, seq=%d)\n", out->numsegs, out->length,
|
out->numsegs, out->length,
|
||||||
pkt->soft.rfc1201.sequence);
|
pkt->soft.rfc1201.sequence);
|
||||||
|
|
||||||
return 0; /* not done */
|
return 0; /* not done */
|
||||||
}
|
}
|
||||||
@ -506,7 +507,6 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
|
|||||||
return 1; /* done */
|
return 1; /* done */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int continue_tx(struct net_device *dev, int bufnum)
|
static int continue_tx(struct net_device *dev, int bufnum)
|
||||||
{
|
{
|
||||||
struct arcnet_local *lp = netdev_priv(dev);
|
struct arcnet_local *lp = netdev_priv(dev);
|
||||||
@ -516,9 +516,9 @@ static int continue_tx(struct net_device *dev, int bufnum)
|
|||||||
int maxsegsize = XMTU - RFC1201_HDR_SIZE;
|
int maxsegsize = XMTU - RFC1201_HDR_SIZE;
|
||||||
int seglen;
|
int seglen;
|
||||||
|
|
||||||
BUGMSG(D_DURING,
|
arc_printk(D_DURING, dev,
|
||||||
"rfc1201 continue_tx: loading segment %d(+1) of %d (seq=%d)\n",
|
"rfc1201 continue_tx: loading segment %d(+1) of %d (seq=%d)\n",
|
||||||
out->segnum, out->numsegs, soft->sequence);
|
out->segnum, out->numsegs, soft->sequence);
|
||||||
|
|
||||||
/* the "new" soft header comes right before the data chunk */
|
/* the "new" soft header comes right before the data chunk */
|
||||||
newsoft = (struct arc_rfc1201 *)
|
newsoft = (struct arc_rfc1201 *)
|
||||||
|
@ -127,6 +127,7 @@ static void ad_marker_info_received(struct bond_marker *marker_info,
|
|||||||
struct port *port);
|
struct port *port);
|
||||||
static void ad_marker_response_received(struct bond_marker *marker,
|
static void ad_marker_response_received(struct bond_marker *marker,
|
||||||
struct port *port);
|
struct port *port);
|
||||||
|
static void ad_update_actor_keys(struct port *port, bool reset);
|
||||||
|
|
||||||
|
|
||||||
/* ================= api to bonding and kernel code ================== */
|
/* ================= api to bonding and kernel code ================== */
|
||||||
@ -327,14 +328,12 @@ static u16 __get_link_speed(struct port *port)
|
|||||||
static u8 __get_duplex(struct port *port)
|
static u8 __get_duplex(struct port *port)
|
||||||
{
|
{
|
||||||
struct slave *slave = port->slave;
|
struct slave *slave = port->slave;
|
||||||
u8 retval;
|
u8 retval = 0x0;
|
||||||
|
|
||||||
/* handling a special case: when the configuration starts with
|
/* handling a special case: when the configuration starts with
|
||||||
* link down, it sets the duplex to 0.
|
* link down, it sets the duplex to 0.
|
||||||
*/
|
*/
|
||||||
if (slave->link != BOND_LINK_UP) {
|
if (slave->link == BOND_LINK_UP) {
|
||||||
retval = 0x0;
|
|
||||||
} else {
|
|
||||||
switch (slave->duplex) {
|
switch (slave->duplex) {
|
||||||
case DUPLEX_FULL:
|
case DUPLEX_FULL:
|
||||||
retval = 0x1;
|
retval = 0x1;
|
||||||
@ -1953,14 +1952,7 @@ void bond_3ad_bind_slave(struct slave *slave)
|
|||||||
* user key
|
* user key
|
||||||
*/
|
*/
|
||||||
port->actor_admin_port_key = bond->params.ad_user_port_key << 6;
|
port->actor_admin_port_key = bond->params.ad_user_port_key << 6;
|
||||||
port->actor_admin_port_key |= __get_duplex(port);
|
ad_update_actor_keys(port, false);
|
||||||
port->actor_admin_port_key |= (__get_link_speed(port) << 1);
|
|
||||||
port->actor_oper_port_key = port->actor_admin_port_key;
|
|
||||||
/* if the port is not full duplex, then the port should be not
|
|
||||||
* lacp Enabled
|
|
||||||
*/
|
|
||||||
if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS))
|
|
||||||
port->sm_vars &= ~AD_PORT_LACP_ENABLED;
|
|
||||||
/* actor system is the bond's system */
|
/* actor system is the bond's system */
|
||||||
port->actor_system = BOND_AD_INFO(bond).system.sys_mac_addr;
|
port->actor_system = BOND_AD_INFO(bond).system.sys_mac_addr;
|
||||||
port->actor_system_priority =
|
port->actor_system_priority =
|
||||||
@ -2310,45 +2302,60 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bond_3ad_adapter_speed_changed - handle a slave's speed change indication
|
* ad_update_actor_keys - Update the oper / admin keys for a port based on
|
||||||
* @slave: slave struct to work on
|
* its current speed and duplex settings.
|
||||||
*
|
*
|
||||||
* Handle reselection of aggregator (if needed) for this port.
|
* @port: the port we'are looking at
|
||||||
|
* @reset: Boolean to just reset the speed and the duplex part of the key
|
||||||
|
*
|
||||||
|
* The logic to change the oper / admin keys is:
|
||||||
|
* (a) A full duplex port can participate in LACP with partner.
|
||||||
|
* (b) When the speed is changed, LACP need to be reinitiated.
|
||||||
*/
|
*/
|
||||||
void bond_3ad_adapter_speed_changed(struct slave *slave)
|
static void ad_update_actor_keys(struct port *port, bool reset)
|
||||||
{
|
{
|
||||||
struct port *port;
|
u8 duplex = 0;
|
||||||
|
u16 ospeed = 0, speed = 0;
|
||||||
|
u16 old_oper_key = port->actor_oper_port_key;
|
||||||
|
|
||||||
port = &(SLAVE_AD_INFO(slave)->port);
|
port->actor_admin_port_key &= ~(AD_SPEED_KEY_MASKS|AD_DUPLEX_KEY_MASKS);
|
||||||
|
if (!reset) {
|
||||||
/* if slave is null, the whole port is not initialized */
|
speed = __get_link_speed(port);
|
||||||
if (!port->slave) {
|
ospeed = (old_oper_key & AD_SPEED_KEY_MASKS) >> 1;
|
||||||
netdev_warn(slave->bond->dev, "speed changed for uninitialized port on %s\n",
|
duplex = __get_duplex(port);
|
||||||
slave->dev->name);
|
port->actor_admin_port_key |= (speed << 1) | duplex;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_bh(&slave->bond->mode_lock);
|
|
||||||
|
|
||||||
port->actor_admin_port_key &= ~AD_SPEED_KEY_MASKS;
|
|
||||||
port->actor_admin_port_key |= __get_link_speed(port) << 1;
|
|
||||||
port->actor_oper_port_key = port->actor_admin_port_key;
|
port->actor_oper_port_key = port->actor_admin_port_key;
|
||||||
netdev_dbg(slave->bond->dev, "Port %d changed speed\n", port->actor_port_number);
|
|
||||||
/* there is no need to reselect a new aggregator, just signal the
|
|
||||||
* state machines to reinitialize
|
|
||||||
*/
|
|
||||||
port->sm_vars |= AD_PORT_BEGIN;
|
|
||||||
|
|
||||||
spin_unlock_bh(&slave->bond->mode_lock);
|
if (old_oper_key != port->actor_oper_port_key) {
|
||||||
|
/* Only 'duplex' port participates in LACP */
|
||||||
|
if (duplex)
|
||||||
|
port->sm_vars |= AD_PORT_LACP_ENABLED;
|
||||||
|
else
|
||||||
|
port->sm_vars &= ~AD_PORT_LACP_ENABLED;
|
||||||
|
|
||||||
|
if (!reset) {
|
||||||
|
if (!speed) {
|
||||||
|
netdev_err(port->slave->dev,
|
||||||
|
"speed changed to 0 for port %s",
|
||||||
|
port->slave->dev->name);
|
||||||
|
} else if (duplex && ospeed != speed) {
|
||||||
|
/* Speed change restarts LACP state-machine */
|
||||||
|
port->sm_vars |= AD_PORT_BEGIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bond_3ad_adapter_duplex_changed - handle a slave's duplex change indication
|
* bond_3ad_adapter_speed_duplex_changed - handle a slave's speed / duplex
|
||||||
|
* change indication
|
||||||
|
*
|
||||||
* @slave: slave struct to work on
|
* @slave: slave struct to work on
|
||||||
*
|
*
|
||||||
* Handle reselection of aggregator (if needed) for this port.
|
* Handle reselection of aggregator (if needed) for this port.
|
||||||
*/
|
*/
|
||||||
void bond_3ad_adapter_duplex_changed(struct slave *slave)
|
void bond_3ad_adapter_speed_duplex_changed(struct slave *slave)
|
||||||
{
|
{
|
||||||
struct port *port;
|
struct port *port;
|
||||||
|
|
||||||
@ -2356,25 +2363,16 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
|
|||||||
|
|
||||||
/* if slave is null, the whole port is not initialized */
|
/* if slave is null, the whole port is not initialized */
|
||||||
if (!port->slave) {
|
if (!port->slave) {
|
||||||
netdev_warn(slave->bond->dev, "duplex changed for uninitialized port on %s\n",
|
netdev_warn(slave->bond->dev,
|
||||||
|
"speed/duplex changed for uninitialized port %s\n",
|
||||||
slave->dev->name);
|
slave->dev->name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_bh(&slave->bond->mode_lock);
|
spin_lock_bh(&slave->bond->mode_lock);
|
||||||
|
ad_update_actor_keys(port, false);
|
||||||
port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS;
|
netdev_dbg(slave->bond->dev, "Port %d slave %s changed speed/duplex\n",
|
||||||
port->actor_admin_port_key |= __get_duplex(port);
|
|
||||||
port->actor_oper_port_key = port->actor_admin_port_key;
|
|
||||||
netdev_dbg(slave->bond->dev, "Port %d slave %s changed duplex\n",
|
|
||||||
port->actor_port_number, slave->dev->name);
|
port->actor_port_number, slave->dev->name);
|
||||||
if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
|
|
||||||
port->sm_vars |= AD_PORT_LACP_ENABLED;
|
|
||||||
/* there is no need to reselect a new aggregator, just signal the
|
|
||||||
* state machines to reinitialize
|
|
||||||
*/
|
|
||||||
port->sm_vars |= AD_PORT_BEGIN;
|
|
||||||
|
|
||||||
spin_unlock_bh(&slave->bond->mode_lock);
|
spin_unlock_bh(&slave->bond->mode_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2406,26 +2404,17 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
|
|||||||
* on link up we are forcing recheck on the duplex and speed since
|
* on link up we are forcing recheck on the duplex and speed since
|
||||||
* some of he adaptors(ce1000.lan) report.
|
* some of he adaptors(ce1000.lan) report.
|
||||||
*/
|
*/
|
||||||
port->actor_admin_port_key &= ~(AD_DUPLEX_KEY_MASKS|AD_SPEED_KEY_MASKS);
|
|
||||||
if (link == BOND_LINK_UP) {
|
if (link == BOND_LINK_UP) {
|
||||||
port->is_enabled = true;
|
port->is_enabled = true;
|
||||||
port->actor_admin_port_key |=
|
ad_update_actor_keys(port, false);
|
||||||
(__get_link_speed(port) << 1) | __get_duplex(port);
|
|
||||||
if (port->actor_admin_port_key & AD_DUPLEX_KEY_MASKS)
|
|
||||||
port->sm_vars |= AD_PORT_LACP_ENABLED;
|
|
||||||
} else {
|
} else {
|
||||||
/* link has failed */
|
/* link has failed */
|
||||||
port->is_enabled = false;
|
port->is_enabled = false;
|
||||||
port->sm_vars &= ~AD_PORT_LACP_ENABLED;
|
ad_update_actor_keys(port, true);
|
||||||
}
|
}
|
||||||
port->actor_oper_port_key = port->actor_admin_port_key;
|
|
||||||
netdev_dbg(slave->bond->dev, "Port %d changed link status to %s\n",
|
netdev_dbg(slave->bond->dev, "Port %d changed link status to %s\n",
|
||||||
port->actor_port_number,
|
port->actor_port_number,
|
||||||
link == BOND_LINK_UP ? "UP" : "DOWN");
|
link == BOND_LINK_UP ? "UP" : "DOWN");
|
||||||
/* there is no need to reselect a new aggregator, just signal the
|
|
||||||
* state machines to reinitialize
|
|
||||||
*/
|
|
||||||
port->sm_vars |= AD_PORT_BEGIN;
|
|
||||||
|
|
||||||
spin_unlock_bh(&slave->bond->mode_lock);
|
spin_unlock_bh(&slave->bond->mode_lock);
|
||||||
|
|
||||||
|
@ -1071,7 +1071,7 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
|
|||||||
NETIF_F_HIGHDMA | NETIF_F_LRO)
|
NETIF_F_HIGHDMA | NETIF_F_LRO)
|
||||||
|
|
||||||
#define BOND_ENC_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_RXCSUM |\
|
#define BOND_ENC_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_RXCSUM |\
|
||||||
NETIF_F_TSO)
|
NETIF_F_ALL_TSO)
|
||||||
|
|
||||||
static void bond_compute_features(struct bonding *bond)
|
static void bond_compute_features(struct bonding *bond)
|
||||||
{
|
{
|
||||||
@ -2943,8 +2943,6 @@ static int bond_slave_netdev_event(unsigned long event,
|
|||||||
struct slave *slave = bond_slave_get_rtnl(slave_dev), *primary;
|
struct slave *slave = bond_slave_get_rtnl(slave_dev), *primary;
|
||||||
struct bonding *bond;
|
struct bonding *bond;
|
||||||
struct net_device *bond_dev;
|
struct net_device *bond_dev;
|
||||||
u32 old_speed;
|
|
||||||
u8 old_duplex;
|
|
||||||
|
|
||||||
/* A netdev event can be generated while enslaving a device
|
/* A netdev event can be generated while enslaving a device
|
||||||
* before netdev_rx_handler_register is called in which case
|
* before netdev_rx_handler_register is called in which case
|
||||||
@ -2965,17 +2963,9 @@ static int bond_slave_netdev_event(unsigned long event,
|
|||||||
break;
|
break;
|
||||||
case NETDEV_UP:
|
case NETDEV_UP:
|
||||||
case NETDEV_CHANGE:
|
case NETDEV_CHANGE:
|
||||||
old_speed = slave->speed;
|
|
||||||
old_duplex = slave->duplex;
|
|
||||||
|
|
||||||
bond_update_speed_duplex(slave);
|
bond_update_speed_duplex(slave);
|
||||||
|
if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
bond_3ad_adapter_speed_duplex_changed(slave);
|
||||||
if (old_speed != slave->speed)
|
|
||||||
bond_3ad_adapter_speed_changed(slave);
|
|
||||||
if (old_duplex != slave->duplex)
|
|
||||||
bond_3ad_adapter_duplex_changed(slave);
|
|
||||||
}
|
|
||||||
/* Fallthrough */
|
/* Fallthrough */
|
||||||
case NETDEV_DOWN:
|
case NETDEV_DOWN:
|
||||||
/* Refresh slave-array if applicable!
|
/* Refresh slave-array if applicable!
|
||||||
@ -3136,6 +3126,10 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
|
|||||||
struct flow_keys flow;
|
struct flow_keys flow;
|
||||||
u32 hash;
|
u32 hash;
|
||||||
|
|
||||||
|
if (bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP34 &&
|
||||||
|
skb->l4_hash)
|
||||||
|
return skb->hash;
|
||||||
|
|
||||||
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
|
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
|
||||||
!bond_flow_dissect(bond, skb, &flow))
|
!bond_flow_dissect(bond, skb, &flow))
|
||||||
return bond_eth_hash(skb);
|
return bond_eth_hash(skb);
|
||||||
|
@ -129,6 +129,16 @@ config CAN_RCAR
|
|||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called rcar_can.
|
be called rcar_can.
|
||||||
|
|
||||||
|
config CAN_SUN4I
|
||||||
|
tristate "Allwinner A10 CAN controller"
|
||||||
|
depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST
|
||||||
|
---help---
|
||||||
|
Say Y here if you want to use CAN controller found on Allwinner
|
||||||
|
A10/A20 SoCs.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
be called sun4i_can.
|
||||||
|
|
||||||
config CAN_XILINXCAN
|
config CAN_XILINXCAN
|
||||||
tristate "Xilinx CAN"
|
tristate "Xilinx CAN"
|
||||||
depends on ARCH_ZYNQ || ARM64 || MICROBLAZE || COMPILE_TEST
|
depends on ARCH_ZYNQ || ARM64 || MICROBLAZE || COMPILE_TEST
|
||||||
|
@ -27,6 +27,7 @@ obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
|
|||||||
obj-$(CONFIG_PCH_CAN) += pch_can.o
|
obj-$(CONFIG_PCH_CAN) += pch_can.o
|
||||||
obj-$(CONFIG_CAN_GRCAN) += grcan.o
|
obj-$(CONFIG_CAN_GRCAN) += grcan.o
|
||||||
obj-$(CONFIG_CAN_RCAR) += rcar_can.o
|
obj-$(CONFIG_CAN_RCAR) += rcar_can.o
|
||||||
|
obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
|
||||||
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
|
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
|
||||||
|
|
||||||
subdir-ccflags-y += -D__CHECK_ENDIAN__
|
subdir-ccflags-y += -D__CHECK_ENDIAN__
|
||||||
|
@ -8,15 +8,6 @@
|
|||||||
* Public License ("GPL") version 2 as distributed in the 'COPYING'
|
* Public License ("GPL") version 2 as distributed in the 'COPYING'
|
||||||
* file from the main directory of the linux kernel source.
|
* file from the main directory of the linux kernel source.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* Your platform definition file should specify something like:
|
|
||||||
*
|
|
||||||
* static struct at91_can_data ek_can_data = {
|
|
||||||
* transceiver_switch = sam9263ek_transceiver_switch,
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* at91_add_device_can(&ek_can_data);
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@ -33,7 +24,6 @@
|
|||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/platform_data/atmel.h>
|
|
||||||
|
|
||||||
#include <linux/can/dev.h>
|
#include <linux/can/dev.h>
|
||||||
#include <linux/can/error.h>
|
#include <linux/can/error.h>
|
||||||
@ -324,15 +314,6 @@ static inline u32 at91_can_id_to_reg_mid(canid_t can_id)
|
|||||||
return reg_mid;
|
return reg_mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Swtich transceiver on or off
|
|
||||||
*/
|
|
||||||
static void at91_transceiver_switch(const struct at91_priv *priv, int on)
|
|
||||||
{
|
|
||||||
if (priv->pdata && priv->pdata->transceiver_switch)
|
|
||||||
priv->pdata->transceiver_switch(on);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void at91_setup_mailboxes(struct net_device *dev)
|
static void at91_setup_mailboxes(struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct at91_priv *priv = netdev_priv(dev);
|
struct at91_priv *priv = netdev_priv(dev);
|
||||||
@ -416,7 +397,6 @@ static void at91_chip_start(struct net_device *dev)
|
|||||||
|
|
||||||
at91_set_bittiming(dev);
|
at91_set_bittiming(dev);
|
||||||
at91_setup_mailboxes(dev);
|
at91_setup_mailboxes(dev);
|
||||||
at91_transceiver_switch(priv, 1);
|
|
||||||
|
|
||||||
/* enable chip */
|
/* enable chip */
|
||||||
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
||||||
@ -444,7 +424,6 @@ static void at91_chip_stop(struct net_device *dev, enum can_state state)
|
|||||||
reg_mr = at91_read(priv, AT91_MR);
|
reg_mr = at91_read(priv, AT91_MR);
|
||||||
at91_write(priv, AT91_MR, reg_mr & ~AT91_MR_CANEN);
|
at91_write(priv, AT91_MR, reg_mr & ~AT91_MR_CANEN);
|
||||||
|
|
||||||
at91_transceiver_switch(priv, 0);
|
|
||||||
priv->can.state = state;
|
priv->can.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,7 +915,7 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
|||||||
nla_put(skb, IFLA_CAN_BITTIMING_CONST,
|
nla_put(skb, IFLA_CAN_BITTIMING_CONST,
|
||||||
sizeof(*priv->bittiming_const), priv->bittiming_const)) ||
|
sizeof(*priv->bittiming_const), priv->bittiming_const)) ||
|
||||||
|
|
||||||
nla_put(skb, IFLA_CAN_CLOCK, sizeof(cm), &priv->clock) ||
|
nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) ||
|
||||||
nla_put_u32(skb, IFLA_CAN_STATE, state) ||
|
nla_put_u32(skb, IFLA_CAN_STATE, state) ||
|
||||||
nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
|
nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
|
||||||
nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
|
nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
|
||||||
|
@ -26,12 +26,8 @@
|
|||||||
#include <linux/can/led.h>
|
#include <linux/can/led.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/if_arp.h>
|
|
||||||
#include <linux/if_ether.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
@ -63,10 +59,10 @@
|
|||||||
#define FLEXCAN_MCR_LPRIO_EN BIT(13)
|
#define FLEXCAN_MCR_LPRIO_EN BIT(13)
|
||||||
#define FLEXCAN_MCR_AEN BIT(12)
|
#define FLEXCAN_MCR_AEN BIT(12)
|
||||||
#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f)
|
#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f)
|
||||||
#define FLEXCAN_MCR_IDAM_A (0 << 8)
|
#define FLEXCAN_MCR_IDAM_A (0x0 << 8)
|
||||||
#define FLEXCAN_MCR_IDAM_B (1 << 8)
|
#define FLEXCAN_MCR_IDAM_B (0x1 << 8)
|
||||||
#define FLEXCAN_MCR_IDAM_C (2 << 8)
|
#define FLEXCAN_MCR_IDAM_C (0x2 << 8)
|
||||||
#define FLEXCAN_MCR_IDAM_D (3 << 8)
|
#define FLEXCAN_MCR_IDAM_D (0x3 << 8)
|
||||||
|
|
||||||
/* FLEXCAN control register (CANCTRL) bits */
|
/* FLEXCAN control register (CANCTRL) bits */
|
||||||
#define FLEXCAN_CTRL_PRESDIV(x) (((x) & 0xff) << 24)
|
#define FLEXCAN_CTRL_PRESDIV(x) (((x) & 0xff) << 24)
|
||||||
@ -161,7 +157,7 @@
|
|||||||
#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24)
|
#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24)
|
||||||
#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24)
|
#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24)
|
||||||
#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24)
|
#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24)
|
||||||
#define FLEXCAN_MB_CODE_RX_OVERRRUN (0x6 << 24)
|
#define FLEXCAN_MB_CODE_RX_OVERRUN (0x6 << 24)
|
||||||
#define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24)
|
#define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24)
|
||||||
|
|
||||||
#define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24)
|
#define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24)
|
||||||
@ -175,12 +171,9 @@
|
|||||||
#define FLEXCAN_MB_CNT_LENGTH(x) (((x) & 0xf) << 16)
|
#define FLEXCAN_MB_CNT_LENGTH(x) (((x) & 0xf) << 16)
|
||||||
#define FLEXCAN_MB_CNT_TIMESTAMP(x) ((x) & 0xffff)
|
#define FLEXCAN_MB_CNT_TIMESTAMP(x) ((x) & 0xffff)
|
||||||
|
|
||||||
#define FLEXCAN_MB_CODE_MASK (0xf0ffffff)
|
#define FLEXCAN_TIMEOUT_US (50)
|
||||||
|
|
||||||
#define FLEXCAN_TIMEOUT_US (50)
|
/* FLEXCAN hardware feature flags
|
||||||
|
|
||||||
/*
|
|
||||||
* FLEXCAN hardware feature flags
|
|
||||||
*
|
*
|
||||||
* Below is some version info we got:
|
* Below is some version info we got:
|
||||||
* SOC Version IP-Version Glitch- [TR]WRN_INT Memory err RTR re-
|
* SOC Version IP-Version Glitch- [TR]WRN_INT Memory err RTR re-
|
||||||
@ -194,9 +187,9 @@
|
|||||||
*
|
*
|
||||||
* Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected.
|
* Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected.
|
||||||
*/
|
*/
|
||||||
#define FLEXCAN_HAS_V10_FEATURES BIT(1) /* For core version >= 10 */
|
#define FLEXCAN_QUIRK_BROKEN_ERR_STATE BIT(1) /* [TR]WRN_INT not connected */
|
||||||
#define FLEXCAN_HAS_BROKEN_ERR_STATE BIT(2) /* [TR]WRN_INT not connected */
|
#define FLEXCAN_QUIRK_DISABLE_RXFG BIT(2) /* Disable RX FIFO Global mask */
|
||||||
#define FLEXCAN_HAS_MECR_FEATURES BIT(3) /* Memory error detection */
|
#define FLEXCAN_QUIRK_DISABLE_MECR BIT(3) /* Disble Memory error detection */
|
||||||
|
|
||||||
/* Structure of the message buffer */
|
/* Structure of the message buffer */
|
||||||
struct flexcan_mb {
|
struct flexcan_mb {
|
||||||
@ -228,7 +221,7 @@ struct flexcan_regs {
|
|||||||
u32 rxfgmask; /* 0x48 */
|
u32 rxfgmask; /* 0x48 */
|
||||||
u32 rxfir; /* 0x4c */
|
u32 rxfir; /* 0x4c */
|
||||||
u32 _reserved3[12]; /* 0x50 */
|
u32 _reserved3[12]; /* 0x50 */
|
||||||
struct flexcan_mb cantxfg[64]; /* 0x80 */
|
struct flexcan_mb mb[64]; /* 0x80 */
|
||||||
/* FIFO-mode:
|
/* FIFO-mode:
|
||||||
* MB
|
* MB
|
||||||
* 0x080...0x08f 0 RX message buffer
|
* 0x080...0x08f 0 RX message buffer
|
||||||
@ -236,7 +229,7 @@ struct flexcan_regs {
|
|||||||
* 0x0e0...0x0ff 6-7 8 entry ID table
|
* 0x0e0...0x0ff 6-7 8 entry ID table
|
||||||
* (mx25, mx28, mx35, mx53)
|
* (mx25, mx28, mx35, mx53)
|
||||||
* 0x0e0...0x2df 6-7..37 8..128 entry ID table
|
* 0x0e0...0x2df 6-7..37 8..128 entry ID table
|
||||||
* size conf'ed via ctrl2::RFFN
|
* size conf'ed via ctrl2::RFFN
|
||||||
* (mx6, vf610)
|
* (mx6, vf610)
|
||||||
*/
|
*/
|
||||||
u32 _reserved4[408];
|
u32 _reserved4[408];
|
||||||
@ -251,14 +244,14 @@ struct flexcan_regs {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct flexcan_devtype_data {
|
struct flexcan_devtype_data {
|
||||||
u32 features; /* hardware controller features */
|
u32 quirks; /* quirks needed for different IP cores */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct flexcan_priv {
|
struct flexcan_priv {
|
||||||
struct can_priv can;
|
struct can_priv can;
|
||||||
struct napi_struct napi;
|
struct napi_struct napi;
|
||||||
|
|
||||||
void __iomem *base;
|
struct flexcan_regs __iomem *regs;
|
||||||
u32 reg_esr;
|
u32 reg_esr;
|
||||||
u32 reg_ctrl_default;
|
u32 reg_ctrl_default;
|
||||||
|
|
||||||
@ -270,14 +263,17 @@ struct flexcan_priv {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct flexcan_devtype_data fsl_p1010_devtype_data = {
|
static struct flexcan_devtype_data fsl_p1010_devtype_data = {
|
||||||
.features = FLEXCAN_HAS_BROKEN_ERR_STATE,
|
.quirks = FLEXCAN_QUIRK_BROKEN_ERR_STATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct flexcan_devtype_data fsl_imx28_devtype_data;
|
static struct flexcan_devtype_data fsl_imx28_devtype_data;
|
||||||
|
|
||||||
static struct flexcan_devtype_data fsl_imx6q_devtype_data = {
|
static struct flexcan_devtype_data fsl_imx6q_devtype_data = {
|
||||||
.features = FLEXCAN_HAS_V10_FEATURES,
|
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct flexcan_devtype_data fsl_vf610_devtype_data = {
|
static struct flexcan_devtype_data fsl_vf610_devtype_data = {
|
||||||
.features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES,
|
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_DISABLE_MECR,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct can_bittiming_const flexcan_bittiming_const = {
|
static const struct can_bittiming_const flexcan_bittiming_const = {
|
||||||
@ -292,11 +288,10 @@ static const struct can_bittiming_const flexcan_bittiming_const = {
|
|||||||
.brp_inc = 1,
|
.brp_inc = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/* Abstract off the read/write for arm versus ppc. This
|
||||||
* Abstract off the read/write for arm versus ppc. This
|
|
||||||
* assumes that PPC uses big-endian registers and everything
|
* assumes that PPC uses big-endian registers and everything
|
||||||
* else uses little-endian registers, independent of CPU
|
* else uses little-endian registers, independent of CPU
|
||||||
* endianess.
|
* endianness.
|
||||||
*/
|
*/
|
||||||
#if defined(CONFIG_PPC)
|
#if defined(CONFIG_PPC)
|
||||||
static inline u32 flexcan_read(void __iomem *addr)
|
static inline u32 flexcan_read(void __iomem *addr)
|
||||||
@ -345,7 +340,7 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
|
|||||||
|
|
||||||
static int flexcan_chip_enable(struct flexcan_priv *priv)
|
static int flexcan_chip_enable(struct flexcan_priv *priv)
|
||||||
{
|
{
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
@ -364,7 +359,7 @@ static int flexcan_chip_enable(struct flexcan_priv *priv)
|
|||||||
|
|
||||||
static int flexcan_chip_disable(struct flexcan_priv *priv)
|
static int flexcan_chip_disable(struct flexcan_priv *priv)
|
||||||
{
|
{
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
@ -383,7 +378,7 @@ static int flexcan_chip_disable(struct flexcan_priv *priv)
|
|||||||
|
|
||||||
static int flexcan_chip_freeze(struct flexcan_priv *priv)
|
static int flexcan_chip_freeze(struct flexcan_priv *priv)
|
||||||
{
|
{
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate;
|
unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
@ -402,7 +397,7 @@ static int flexcan_chip_freeze(struct flexcan_priv *priv)
|
|||||||
|
|
||||||
static int flexcan_chip_unfreeze(struct flexcan_priv *priv)
|
static int flexcan_chip_unfreeze(struct flexcan_priv *priv)
|
||||||
{
|
{
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
@ -421,7 +416,7 @@ static int flexcan_chip_unfreeze(struct flexcan_priv *priv)
|
|||||||
|
|
||||||
static int flexcan_chip_softreset(struct flexcan_priv *priv)
|
static int flexcan_chip_softreset(struct flexcan_priv *priv)
|
||||||
{
|
{
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
||||||
|
|
||||||
flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr);
|
flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr);
|
||||||
@ -434,12 +429,11 @@ static int flexcan_chip_softreset(struct flexcan_priv *priv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int __flexcan_get_berr_counter(const struct net_device *dev,
|
static int __flexcan_get_berr_counter(const struct net_device *dev,
|
||||||
struct can_berr_counter *bec)
|
struct can_berr_counter *bec)
|
||||||
{
|
{
|
||||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
u32 reg = flexcan_read(®s->ecr);
|
u32 reg = flexcan_read(®s->ecr);
|
||||||
|
|
||||||
bec->txerr = (reg >> 0) & 0xff;
|
bec->txerr = (reg >> 0) & 0xff;
|
||||||
@ -474,9 +468,10 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
|
|||||||
static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||||
u32 can_id;
|
u32 can_id;
|
||||||
|
u32 data;
|
||||||
u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (cf->can_dlc << 16);
|
u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (cf->can_dlc << 16);
|
||||||
|
|
||||||
if (can_dropped_invalid_skb(dev, skb))
|
if (can_dropped_invalid_skb(dev, skb))
|
||||||
@ -495,26 +490,26 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
ctrl |= FLEXCAN_MB_CNT_RTR;
|
ctrl |= FLEXCAN_MB_CNT_RTR;
|
||||||
|
|
||||||
if (cf->can_dlc > 0) {
|
if (cf->can_dlc > 0) {
|
||||||
u32 data = be32_to_cpup((__be32 *)&cf->data[0]);
|
data = be32_to_cpup((__be32 *)&cf->data[0]);
|
||||||
flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[0]);
|
flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[0]);
|
||||||
}
|
}
|
||||||
if (cf->can_dlc > 3) {
|
if (cf->can_dlc > 3) {
|
||||||
u32 data = be32_to_cpup((__be32 *)&cf->data[4]);
|
data = be32_to_cpup((__be32 *)&cf->data[4]);
|
||||||
flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[1]);
|
flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
can_put_echo_skb(skb, dev, 0);
|
can_put_echo_skb(skb, dev, 0);
|
||||||
|
|
||||||
flexcan_write(can_id, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_id);
|
flexcan_write(can_id, ®s->mb[FLEXCAN_TX_BUF_ID].can_id);
|
||||||
flexcan_write(ctrl, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
|
flexcan_write(ctrl, ®s->mb[FLEXCAN_TX_BUF_ID].can_ctrl);
|
||||||
|
|
||||||
/* Errata ERR005829 step8:
|
/* Errata ERR005829 step8:
|
||||||
* Write twice INACTIVE(0x8) code to first MB.
|
* Write twice INACTIVE(0x8) code to first MB.
|
||||||
*/
|
*/
|
||||||
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
||||||
®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
|
®s->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
|
||||||
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
||||||
®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
|
®s->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
|
||||||
|
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
}
|
}
|
||||||
@ -597,14 +592,14 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr)
|
|||||||
flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK;
|
flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK;
|
||||||
if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) {
|
if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) {
|
||||||
tx_state = unlikely(reg_esr & FLEXCAN_ESR_TX_WRN) ?
|
tx_state = unlikely(reg_esr & FLEXCAN_ESR_TX_WRN) ?
|
||||||
CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
|
CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
|
||||||
rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ?
|
rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ?
|
||||||
CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
|
CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
|
||||||
new_state = max(tx_state, rx_state);
|
new_state = max(tx_state, rx_state);
|
||||||
} else {
|
} else {
|
||||||
__flexcan_get_berr_counter(dev, &bec);
|
__flexcan_get_berr_counter(dev, &bec);
|
||||||
new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ?
|
new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ?
|
||||||
CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF;
|
CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF;
|
||||||
rx_state = bec.rxerr >= bec.txerr ? new_state : 0;
|
rx_state = bec.rxerr >= bec.txerr ? new_state : 0;
|
||||||
tx_state = bec.rxerr <= bec.txerr ? new_state : 0;
|
tx_state = bec.rxerr <= bec.txerr ? new_state : 0;
|
||||||
}
|
}
|
||||||
@ -633,8 +628,8 @@ static void flexcan_read_fifo(const struct net_device *dev,
|
|||||||
struct can_frame *cf)
|
struct can_frame *cf)
|
||||||
{
|
{
|
||||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
struct flexcan_mb __iomem *mb = ®s->cantxfg[0];
|
struct flexcan_mb __iomem *mb = ®s->mb[0];
|
||||||
u32 reg_ctrl, reg_id;
|
u32 reg_ctrl, reg_id;
|
||||||
|
|
||||||
reg_ctrl = flexcan_read(&mb->can_ctrl);
|
reg_ctrl = flexcan_read(&mb->can_ctrl);
|
||||||
@ -683,12 +678,11 @@ static int flexcan_poll(struct napi_struct *napi, int quota)
|
|||||||
{
|
{
|
||||||
struct net_device *dev = napi->dev;
|
struct net_device *dev = napi->dev;
|
||||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
u32 reg_iflag1, reg_esr;
|
u32 reg_iflag1, reg_esr;
|
||||||
int work_done = 0;
|
int work_done = 0;
|
||||||
|
|
||||||
/*
|
/* The error bits are cleared on read,
|
||||||
* The error bits are cleared on read,
|
|
||||||
* use saved value from irq handler.
|
* use saved value from irq handler.
|
||||||
*/
|
*/
|
||||||
reg_esr = flexcan_read(®s->esr) | priv->reg_esr;
|
reg_esr = flexcan_read(®s->esr) | priv->reg_esr;
|
||||||
@ -723,17 +717,17 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
|
|||||||
struct net_device *dev = dev_id;
|
struct net_device *dev = dev_id;
|
||||||
struct net_device_stats *stats = &dev->stats;
|
struct net_device_stats *stats = &dev->stats;
|
||||||
struct flexcan_priv *priv = netdev_priv(dev);
|
struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
u32 reg_iflag1, reg_esr;
|
u32 reg_iflag1, reg_esr;
|
||||||
|
|
||||||
reg_iflag1 = flexcan_read(®s->iflag1);
|
reg_iflag1 = flexcan_read(®s->iflag1);
|
||||||
reg_esr = flexcan_read(®s->esr);
|
reg_esr = flexcan_read(®s->esr);
|
||||||
|
|
||||||
/* ACK all bus error and state change IRQ sources */
|
/* ACK all bus error and state change IRQ sources */
|
||||||
if (reg_esr & FLEXCAN_ESR_ALL_INT)
|
if (reg_esr & FLEXCAN_ESR_ALL_INT)
|
||||||
flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr);
|
flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr);
|
||||||
|
|
||||||
/*
|
/* schedule NAPI in case of:
|
||||||
* schedule NAPI in case of:
|
|
||||||
* - rx IRQ
|
* - rx IRQ
|
||||||
* - state change IRQ
|
* - state change IRQ
|
||||||
* - bus error IRQ and bus error reporting is activated
|
* - bus error IRQ and bus error reporting is activated
|
||||||
@ -741,15 +735,14 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
|
|||||||
if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) ||
|
if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) ||
|
||||||
(reg_esr & FLEXCAN_ESR_ERR_STATE) ||
|
(reg_esr & FLEXCAN_ESR_ERR_STATE) ||
|
||||||
flexcan_has_and_handle_berr(priv, reg_esr)) {
|
flexcan_has_and_handle_berr(priv, reg_esr)) {
|
||||||
/*
|
/* The error bits are cleared on read,
|
||||||
* The error bits are cleared on read,
|
|
||||||
* save them for later use.
|
* save them for later use.
|
||||||
*/
|
*/
|
||||||
priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
|
priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
|
||||||
flexcan_write(FLEXCAN_IFLAG_DEFAULT &
|
flexcan_write(FLEXCAN_IFLAG_DEFAULT &
|
||||||
~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1);
|
~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1);
|
||||||
flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
|
flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
|
||||||
®s->ctrl);
|
®s->ctrl);
|
||||||
napi_schedule(&priv->napi);
|
napi_schedule(&priv->napi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -765,9 +758,10 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
|
|||||||
stats->tx_bytes += can_get_echo_skb(dev, 0);
|
stats->tx_bytes += can_get_echo_skb(dev, 0);
|
||||||
stats->tx_packets++;
|
stats->tx_packets++;
|
||||||
can_led_event(dev, CAN_LED_EVENT_TX);
|
can_led_event(dev, CAN_LED_EVENT_TX);
|
||||||
/* after sending a RTR frame mailbox is in RX mode */
|
|
||||||
|
/* after sending a RTR frame MB is in RX mode */
|
||||||
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
||||||
®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
|
®s->mb[FLEXCAN_TX_BUF_ID].can_ctrl);
|
||||||
flexcan_write((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1);
|
flexcan_write((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1);
|
||||||
netif_wake_queue(dev);
|
netif_wake_queue(dev);
|
||||||
}
|
}
|
||||||
@ -779,7 +773,7 @@ static void flexcan_set_bittiming(struct net_device *dev)
|
|||||||
{
|
{
|
||||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
const struct can_bittiming *bt = &priv->can.bittiming;
|
const struct can_bittiming *bt = &priv->can.bittiming;
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
reg = flexcan_read(®s->ctrl);
|
reg = flexcan_read(®s->ctrl);
|
||||||
@ -813,8 +807,7 @@ static void flexcan_set_bittiming(struct net_device *dev)
|
|||||||
flexcan_read(®s->mcr), flexcan_read(®s->ctrl));
|
flexcan_read(®s->mcr), flexcan_read(®s->ctrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* flexcan_chip_start
|
||||||
* flexcan_chip_start
|
|
||||||
*
|
*
|
||||||
* this functions is entered with clocks enabled
|
* this functions is entered with clocks enabled
|
||||||
*
|
*
|
||||||
@ -822,7 +815,7 @@ static void flexcan_set_bittiming(struct net_device *dev)
|
|||||||
static int flexcan_chip_start(struct net_device *dev)
|
static int flexcan_chip_start(struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct flexcan_priv *priv = netdev_priv(dev);
|
struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr;
|
u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr;
|
||||||
int err, i;
|
int err, i;
|
||||||
|
|
||||||
@ -838,29 +831,26 @@ static int flexcan_chip_start(struct net_device *dev)
|
|||||||
|
|
||||||
flexcan_set_bittiming(dev);
|
flexcan_set_bittiming(dev);
|
||||||
|
|
||||||
/*
|
/* MCR
|
||||||
* MCR
|
|
||||||
*
|
*
|
||||||
* enable freeze
|
* enable freeze
|
||||||
* enable fifo
|
* enable fifo
|
||||||
* halt now
|
* halt now
|
||||||
* only supervisor access
|
* only supervisor access
|
||||||
* enable warning int
|
* enable warning int
|
||||||
* choose format C
|
|
||||||
* disable local echo
|
* disable local echo
|
||||||
*
|
* choose format C
|
||||||
|
* set max mailbox number
|
||||||
*/
|
*/
|
||||||
reg_mcr = flexcan_read(®s->mcr);
|
reg_mcr = flexcan_read(®s->mcr);
|
||||||
reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
|
reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
|
||||||
reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
|
reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
|
||||||
FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
|
FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS |
|
||||||
FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS |
|
FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
|
||||||
FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
|
|
||||||
netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
|
netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
|
||||||
flexcan_write(reg_mcr, ®s->mcr);
|
flexcan_write(reg_mcr, ®s->mcr);
|
||||||
|
|
||||||
/*
|
/* CTRL
|
||||||
* CTRL
|
|
||||||
*
|
*
|
||||||
* disable timer sync feature
|
* disable timer sync feature
|
||||||
*
|
*
|
||||||
@ -875,12 +865,12 @@ static int flexcan_chip_start(struct net_device *dev)
|
|||||||
reg_ctrl &= ~FLEXCAN_CTRL_TSYN;
|
reg_ctrl &= ~FLEXCAN_CTRL_TSYN;
|
||||||
reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF |
|
reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF |
|
||||||
FLEXCAN_CTRL_ERR_STATE;
|
FLEXCAN_CTRL_ERR_STATE;
|
||||||
/*
|
|
||||||
* enable the "error interrupt" (FLEXCAN_CTRL_ERR_MSK),
|
/* enable the "error interrupt" (FLEXCAN_CTRL_ERR_MSK),
|
||||||
* on most Flexcan cores, too. Otherwise we don't get
|
* on most Flexcan cores, too. Otherwise we don't get
|
||||||
* any error warning or passive interrupts.
|
* any error warning or passive interrupts.
|
||||||
*/
|
*/
|
||||||
if (priv->devtype_data->features & FLEXCAN_HAS_BROKEN_ERR_STATE ||
|
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_BROKEN_ERR_STATE ||
|
||||||
priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
|
priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
|
||||||
reg_ctrl |= FLEXCAN_CTRL_ERR_MSK;
|
reg_ctrl |= FLEXCAN_CTRL_ERR_MSK;
|
||||||
else
|
else
|
||||||
@ -888,41 +878,41 @@ static int flexcan_chip_start(struct net_device *dev)
|
|||||||
|
|
||||||
/* save for later use */
|
/* save for later use */
|
||||||
priv->reg_ctrl_default = reg_ctrl;
|
priv->reg_ctrl_default = reg_ctrl;
|
||||||
|
/* leave interrupts disabled for now */
|
||||||
|
reg_ctrl &= ~FLEXCAN_CTRL_ERR_ALL;
|
||||||
netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
|
netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
|
||||||
flexcan_write(reg_ctrl, ®s->ctrl);
|
flexcan_write(reg_ctrl, ®s->ctrl);
|
||||||
|
|
||||||
/* clear and invalidate all mailboxes first */
|
/* clear and invalidate all mailboxes first */
|
||||||
for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->cantxfg); i++) {
|
for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->mb); i++) {
|
||||||
flexcan_write(FLEXCAN_MB_CODE_RX_INACTIVE,
|
flexcan_write(FLEXCAN_MB_CODE_RX_INACTIVE,
|
||||||
®s->cantxfg[i].can_ctrl);
|
®s->mb[i].can_ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Errata ERR005829: mark first TX mailbox as INACTIVE */
|
/* Errata ERR005829: mark first TX mailbox as INACTIVE */
|
||||||
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
||||||
®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
|
®s->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
|
||||||
|
|
||||||
/* mark TX mailbox as INACTIVE */
|
/* mark TX mailbox as INACTIVE */
|
||||||
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
|
||||||
®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
|
®s->mb[FLEXCAN_TX_BUF_ID].can_ctrl);
|
||||||
|
|
||||||
/* acceptance mask/acceptance code (accept everything) */
|
/* acceptance mask/acceptance code (accept everything) */
|
||||||
flexcan_write(0x0, ®s->rxgmask);
|
flexcan_write(0x0, ®s->rxgmask);
|
||||||
flexcan_write(0x0, ®s->rx14mask);
|
flexcan_write(0x0, ®s->rx14mask);
|
||||||
flexcan_write(0x0, ®s->rx15mask);
|
flexcan_write(0x0, ®s->rx15mask);
|
||||||
|
|
||||||
if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)
|
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG)
|
||||||
flexcan_write(0x0, ®s->rxfgmask);
|
flexcan_write(0x0, ®s->rxfgmask);
|
||||||
|
|
||||||
/*
|
/* On Vybrid, disable memory error detection interrupts
|
||||||
* On Vybrid, disable memory error detection interrupts
|
|
||||||
* and freeze mode.
|
* and freeze mode.
|
||||||
* This also works around errata e5295 which generates
|
* This also works around errata e5295 which generates
|
||||||
* false positive memory errors and put the device in
|
* false positive memory errors and put the device in
|
||||||
* freeze mode.
|
* freeze mode.
|
||||||
*/
|
*/
|
||||||
if (priv->devtype_data->features & FLEXCAN_HAS_MECR_FEATURES) {
|
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_MECR) {
|
||||||
/*
|
/* Follow the protocol as described in "Detection
|
||||||
* Follow the protocol as described in "Detection
|
|
||||||
* and Correction of Memory Errors" to write to
|
* and Correction of Memory Errors" to write to
|
||||||
* MECR register
|
* MECR register
|
||||||
*/
|
*/
|
||||||
@ -934,7 +924,7 @@ static int flexcan_chip_start(struct net_device *dev)
|
|||||||
reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS;
|
reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS;
|
||||||
flexcan_write(reg_mecr, ®s->mecr);
|
flexcan_write(reg_mecr, ®s->mecr);
|
||||||
reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK |
|
reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK |
|
||||||
FLEXCAN_MECR_FANCEI_MSK);
|
FLEXCAN_MECR_FANCEI_MSK);
|
||||||
flexcan_write(reg_mecr, ®s->mecr);
|
flexcan_write(reg_mecr, ®s->mecr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -949,8 +939,11 @@ static int flexcan_chip_start(struct net_device *dev)
|
|||||||
|
|
||||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||||
|
|
||||||
/* enable FIFO interrupts */
|
/* enable interrupts atomically */
|
||||||
|
disable_irq(dev->irq);
|
||||||
|
flexcan_write(priv->reg_ctrl_default, ®s->ctrl);
|
||||||
flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1);
|
flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1);
|
||||||
|
enable_irq(dev->irq);
|
||||||
|
|
||||||
/* print chip status */
|
/* print chip status */
|
||||||
netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__,
|
netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__,
|
||||||
@ -965,16 +958,14 @@ static int flexcan_chip_start(struct net_device *dev)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* flexcan_chip_stop
|
||||||
* flexcan_chip_stop
|
|
||||||
*
|
*
|
||||||
* this functions is entered with clocks enabled
|
* this functions is entered with clocks enabled
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
static void flexcan_chip_stop(struct net_device *dev)
|
static void flexcan_chip_stop(struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct flexcan_priv *priv = netdev_priv(dev);
|
struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
|
|
||||||
/* freeze + disable module */
|
/* freeze + disable module */
|
||||||
flexcan_chip_freeze(priv);
|
flexcan_chip_freeze(priv);
|
||||||
@ -987,8 +978,6 @@ static void flexcan_chip_stop(struct net_device *dev)
|
|||||||
|
|
||||||
flexcan_transceiver_disable(priv);
|
flexcan_transceiver_disable(priv);
|
||||||
priv->can.state = CAN_STATE_STOPPED;
|
priv->can.state = CAN_STATE_STOPPED;
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flexcan_open(struct net_device *dev)
|
static int flexcan_open(struct net_device *dev)
|
||||||
@ -1085,7 +1074,7 @@ static const struct net_device_ops flexcan_netdev_ops = {
|
|||||||
static int register_flexcandev(struct net_device *dev)
|
static int register_flexcandev(struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct flexcan_priv *priv = netdev_priv(dev);
|
struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
struct flexcan_regs __iomem *regs = priv->base;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
u32 reg, err;
|
u32 reg, err;
|
||||||
|
|
||||||
err = clk_prepare_enable(priv->clk_ipg);
|
err = clk_prepare_enable(priv->clk_ipg);
|
||||||
@ -1114,8 +1103,7 @@ static int register_flexcandev(struct net_device *dev)
|
|||||||
FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV;
|
FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV;
|
||||||
flexcan_write(reg, ®s->mcr);
|
flexcan_write(reg, ®s->mcr);
|
||||||
|
|
||||||
/*
|
/* Currently we only support newer versions of this core
|
||||||
* Currently we only support newer versions of this core
|
|
||||||
* featuring a RX FIFO. Older cores found on some Coldfire
|
* featuring a RX FIFO. Older cores found on some Coldfire
|
||||||
* derivates are not yet supported.
|
* derivates are not yet supported.
|
||||||
*/
|
*/
|
||||||
@ -1168,7 +1156,7 @@ static int flexcan_probe(struct platform_device *pdev)
|
|||||||
struct regulator *reg_xceiver;
|
struct regulator *reg_xceiver;
|
||||||
struct resource *mem;
|
struct resource *mem;
|
||||||
struct clk *clk_ipg = NULL, *clk_per = NULL;
|
struct clk *clk_ipg = NULL, *clk_per = NULL;
|
||||||
void __iomem *base;
|
struct flexcan_regs __iomem *regs;
|
||||||
int err, irq;
|
int err, irq;
|
||||||
u32 clock_freq = 0;
|
u32 clock_freq = 0;
|
||||||
|
|
||||||
@ -1180,7 +1168,7 @@ static int flexcan_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
if (pdev->dev.of_node)
|
if (pdev->dev.of_node)
|
||||||
of_property_read_u32(pdev->dev.of_node,
|
of_property_read_u32(pdev->dev.of_node,
|
||||||
"clock-frequency", &clock_freq);
|
"clock-frequency", &clock_freq);
|
||||||
|
|
||||||
if (!clock_freq) {
|
if (!clock_freq) {
|
||||||
clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
||||||
@ -1202,9 +1190,9 @@ static int flexcan_probe(struct platform_device *pdev)
|
|||||||
if (irq <= 0)
|
if (irq <= 0)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
base = devm_ioremap_resource(&pdev->dev, mem);
|
regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||||
if (IS_ERR(base))
|
if (IS_ERR(regs))
|
||||||
return PTR_ERR(base);
|
return PTR_ERR(regs);
|
||||||
|
|
||||||
of_id = of_match_device(flexcan_of_match, &pdev->dev);
|
of_id = of_match_device(flexcan_of_match, &pdev->dev);
|
||||||
if (of_id) {
|
if (of_id) {
|
||||||
@ -1232,12 +1220,11 @@ static int flexcan_probe(struct platform_device *pdev)
|
|||||||
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
|
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
|
||||||
CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_3_SAMPLES |
|
CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_3_SAMPLES |
|
||||||
CAN_CTRLMODE_BERR_REPORTING;
|
CAN_CTRLMODE_BERR_REPORTING;
|
||||||
priv->base = base;
|
priv->regs = regs;
|
||||||
priv->clk_ipg = clk_ipg;
|
priv->clk_ipg = clk_ipg;
|
||||||
priv->clk_per = clk_per;
|
priv->clk_per = clk_per;
|
||||||
priv->pdata = dev_get_platdata(&pdev->dev);
|
priv->pdata = dev_get_platdata(&pdev->dev);
|
||||||
priv->devtype_data = devtype_data;
|
priv->devtype_data = devtype_data;
|
||||||
|
|
||||||
priv->reg_xceiver = reg_xceiver;
|
priv->reg_xceiver = reg_xceiver;
|
||||||
|
|
||||||
netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);
|
netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);
|
||||||
@ -1254,7 +1241,7 @@ static int flexcan_probe(struct platform_device *pdev)
|
|||||||
devm_can_led_init(dev);
|
devm_can_led_init(dev);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n",
|
dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n",
|
||||||
priv->base, dev->irq);
|
priv->regs, dev->irq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
857
drivers/net/can/sun4i_can.c
Normal file
857
drivers/net/can/sun4i_can.c
Normal file
@ -0,0 +1,857 @@
|
|||||||
|
/*
|
||||||
|
* sun4i_can.c - CAN bus controller driver for Allwinner SUN4I&SUN7I based SoCs
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Peter Chen
|
||||||
|
* Copyright (C) 2015 Gerhard Bertelsmann
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Parts of this software are based on (derived from) the SJA1000 code by:
|
||||||
|
* Copyright (C) 2014 Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
|
||||||
|
* Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
|
||||||
|
* Copyright (C) 2002-2007 Volkswagen Group Electronic Research
|
||||||
|
* Copyright (C) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
|
||||||
|
* 38106 Braunschweig, GERMANY
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of Volkswagen nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* Alternatively, provided that this notice is retained in full, this
|
||||||
|
* software may be distributed under the terms of the GNU General
|
||||||
|
* Public License ("GPL") version 2, in which case the provisions of the
|
||||||
|
* GPL apply INSTEAD OF those given above.
|
||||||
|
*
|
||||||
|
* The provided data structures and external interfaces from this code
|
||||||
|
* are not restricted to be used by modules with a GPL compatible license.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
* DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/can.h>
|
||||||
|
#include <linux/can/dev.h>
|
||||||
|
#include <linux/can/error.h>
|
||||||
|
#include <linux/can/led.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
#define DRV_NAME "sun4i_can"
|
||||||
|
|
||||||
|
/* Registers address (physical base address 0x01C2BC00) */
|
||||||
|
#define SUN4I_REG_MSEL_ADDR 0x0000 /* CAN Mode Select */
|
||||||
|
#define SUN4I_REG_CMD_ADDR 0x0004 /* CAN Command */
|
||||||
|
#define SUN4I_REG_STA_ADDR 0x0008 /* CAN Status */
|
||||||
|
#define SUN4I_REG_INT_ADDR 0x000c /* CAN Interrupt Flag */
|
||||||
|
#define SUN4I_REG_INTEN_ADDR 0x0010 /* CAN Interrupt Enable */
|
||||||
|
#define SUN4I_REG_BTIME_ADDR 0x0014 /* CAN Bus Timing 0 */
|
||||||
|
#define SUN4I_REG_TEWL_ADDR 0x0018 /* CAN Tx Error Warning Limit */
|
||||||
|
#define SUN4I_REG_ERRC_ADDR 0x001c /* CAN Error Counter */
|
||||||
|
#define SUN4I_REG_RMCNT_ADDR 0x0020 /* CAN Receive Message Counter */
|
||||||
|
#define SUN4I_REG_RBUFSA_ADDR 0x0024 /* CAN Receive Buffer Start Address */
|
||||||
|
#define SUN4I_REG_BUF0_ADDR 0x0040 /* CAN Tx/Rx Buffer 0 */
|
||||||
|
#define SUN4I_REG_BUF1_ADDR 0x0044 /* CAN Tx/Rx Buffer 1 */
|
||||||
|
#define SUN4I_REG_BUF2_ADDR 0x0048 /* CAN Tx/Rx Buffer 2 */
|
||||||
|
#define SUN4I_REG_BUF3_ADDR 0x004c /* CAN Tx/Rx Buffer 3 */
|
||||||
|
#define SUN4I_REG_BUF4_ADDR 0x0050 /* CAN Tx/Rx Buffer 4 */
|
||||||
|
#define SUN4I_REG_BUF5_ADDR 0x0054 /* CAN Tx/Rx Buffer 5 */
|
||||||
|
#define SUN4I_REG_BUF6_ADDR 0x0058 /* CAN Tx/Rx Buffer 6 */
|
||||||
|
#define SUN4I_REG_BUF7_ADDR 0x005c /* CAN Tx/Rx Buffer 7 */
|
||||||
|
#define SUN4I_REG_BUF8_ADDR 0x0060 /* CAN Tx/Rx Buffer 8 */
|
||||||
|
#define SUN4I_REG_BUF9_ADDR 0x0064 /* CAN Tx/Rx Buffer 9 */
|
||||||
|
#define SUN4I_REG_BUF10_ADDR 0x0068 /* CAN Tx/Rx Buffer 10 */
|
||||||
|
#define SUN4I_REG_BUF11_ADDR 0x006c /* CAN Tx/Rx Buffer 11 */
|
||||||
|
#define SUN4I_REG_BUF12_ADDR 0x0070 /* CAN Tx/Rx Buffer 12 */
|
||||||
|
#define SUN4I_REG_ACPC_ADDR 0x0040 /* CAN Acceptance Code 0 */
|
||||||
|
#define SUN4I_REG_ACPM_ADDR 0x0044 /* CAN Acceptance Mask 0 */
|
||||||
|
#define SUN4I_REG_RBUF_RBACK_START_ADDR 0x0180 /* CAN transmit buffer start */
|
||||||
|
#define SUN4I_REG_RBUF_RBACK_END_ADDR 0x01b0 /* CAN transmit buffer end */
|
||||||
|
|
||||||
|
/* Controller Register Description */
|
||||||
|
|
||||||
|
/* mode select register (r/w)
|
||||||
|
* offset:0x0000 default:0x0000_0001
|
||||||
|
*/
|
||||||
|
#define SUN4I_MSEL_SLEEP_MODE (0x01 << 4) /* write in reset mode */
|
||||||
|
#define SUN4I_MSEL_WAKE_UP (0x00 << 4)
|
||||||
|
#define SUN4I_MSEL_SINGLE_FILTER (0x01 << 3) /* write in reset mode */
|
||||||
|
#define SUN4I_MSEL_DUAL_FILTERS (0x00 << 3)
|
||||||
|
#define SUN4I_MSEL_LOOPBACK_MODE BIT(2)
|
||||||
|
#define SUN4I_MSEL_LISTEN_ONLY_MODE BIT(1)
|
||||||
|
#define SUN4I_MSEL_RESET_MODE BIT(0)
|
||||||
|
|
||||||
|
/* command register (w)
|
||||||
|
* offset:0x0004 default:0x0000_0000
|
||||||
|
*/
|
||||||
|
#define SUN4I_CMD_BUS_OFF_REQ BIT(5)
|
||||||
|
#define SUN4I_CMD_SELF_RCV_REQ BIT(4)
|
||||||
|
#define SUN4I_CMD_CLEAR_OR_FLAG BIT(3)
|
||||||
|
#define SUN4I_CMD_RELEASE_RBUF BIT(2)
|
||||||
|
#define SUN4I_CMD_ABORT_REQ BIT(1)
|
||||||
|
#define SUN4I_CMD_TRANS_REQ BIT(0)
|
||||||
|
|
||||||
|
/* status register (r)
|
||||||
|
* offset:0x0008 default:0x0000_003c
|
||||||
|
*/
|
||||||
|
#define SUN4I_STA_BIT_ERR (0x00 << 22)
|
||||||
|
#define SUN4I_STA_FORM_ERR (0x01 << 22)
|
||||||
|
#define SUN4I_STA_STUFF_ERR (0x02 << 22)
|
||||||
|
#define SUN4I_STA_OTHER_ERR (0x03 << 22)
|
||||||
|
#define SUN4I_STA_MASK_ERR (0x03 << 22)
|
||||||
|
#define SUN4I_STA_ERR_DIR BIT(21)
|
||||||
|
#define SUN4I_STA_ERR_SEG_CODE (0x1f << 16)
|
||||||
|
#define SUN4I_STA_START (0x03 << 16)
|
||||||
|
#define SUN4I_STA_ID28_21 (0x02 << 16)
|
||||||
|
#define SUN4I_STA_ID20_18 (0x06 << 16)
|
||||||
|
#define SUN4I_STA_SRTR (0x04 << 16)
|
||||||
|
#define SUN4I_STA_IDE (0x05 << 16)
|
||||||
|
#define SUN4I_STA_ID17_13 (0x07 << 16)
|
||||||
|
#define SUN4I_STA_ID12_5 (0x0f << 16)
|
||||||
|
#define SUN4I_STA_ID4_0 (0x0e << 16)
|
||||||
|
#define SUN4I_STA_RTR (0x0c << 16)
|
||||||
|
#define SUN4I_STA_RB1 (0x0d << 16)
|
||||||
|
#define SUN4I_STA_RB0 (0x09 << 16)
|
||||||
|
#define SUN4I_STA_DLEN (0x0b << 16)
|
||||||
|
#define SUN4I_STA_DATA_FIELD (0x0a << 16)
|
||||||
|
#define SUN4I_STA_CRC_SEQUENCE (0x08 << 16)
|
||||||
|
#define SUN4I_STA_CRC_DELIMITER (0x18 << 16)
|
||||||
|
#define SUN4I_STA_ACK (0x19 << 16)
|
||||||
|
#define SUN4I_STA_ACK_DELIMITER (0x1b << 16)
|
||||||
|
#define SUN4I_STA_END (0x1a << 16)
|
||||||
|
#define SUN4I_STA_INTERMISSION (0x12 << 16)
|
||||||
|
#define SUN4I_STA_ACTIVE_ERROR (0x11 << 16)
|
||||||
|
#define SUN4I_STA_PASSIVE_ERROR (0x16 << 16)
|
||||||
|
#define SUN4I_STA_TOLERATE_DOMINANT_BITS (0x13 << 16)
|
||||||
|
#define SUN4I_STA_ERROR_DELIMITER (0x17 << 16)
|
||||||
|
#define SUN4I_STA_OVERLOAD (0x1c << 16)
|
||||||
|
#define SUN4I_STA_BUS_OFF BIT(7)
|
||||||
|
#define SUN4I_STA_ERR_STA BIT(6)
|
||||||
|
#define SUN4I_STA_TRANS_BUSY BIT(5)
|
||||||
|
#define SUN4I_STA_RCV_BUSY BIT(4)
|
||||||
|
#define SUN4I_STA_TRANS_OVER BIT(3)
|
||||||
|
#define SUN4I_STA_TBUF_RDY BIT(2)
|
||||||
|
#define SUN4I_STA_DATA_ORUN BIT(1)
|
||||||
|
#define SUN4I_STA_RBUF_RDY BIT(0)
|
||||||
|
|
||||||
|
/* interrupt register (r)
|
||||||
|
* offset:0x000c default:0x0000_0000
|
||||||
|
*/
|
||||||
|
#define SUN4I_INT_BUS_ERR BIT(7)
|
||||||
|
#define SUN4I_INT_ARB_LOST BIT(6)
|
||||||
|
#define SUN4I_INT_ERR_PASSIVE BIT(5)
|
||||||
|
#define SUN4I_INT_WAKEUP BIT(4)
|
||||||
|
#define SUN4I_INT_DATA_OR BIT(3)
|
||||||
|
#define SUN4I_INT_ERR_WRN BIT(2)
|
||||||
|
#define SUN4I_INT_TBUF_VLD BIT(1)
|
||||||
|
#define SUN4I_INT_RBUF_VLD BIT(0)
|
||||||
|
|
||||||
|
/* interrupt enable register (r/w)
|
||||||
|
* offset:0x0010 default:0x0000_0000
|
||||||
|
*/
|
||||||
|
#define SUN4I_INTEN_BERR BIT(7)
|
||||||
|
#define SUN4I_INTEN_ARB_LOST BIT(6)
|
||||||
|
#define SUN4I_INTEN_ERR_PASSIVE BIT(5)
|
||||||
|
#define SUN4I_INTEN_WAKEUP BIT(4)
|
||||||
|
#define SUN4I_INTEN_OR BIT(3)
|
||||||
|
#define SUN4I_INTEN_ERR_WRN BIT(2)
|
||||||
|
#define SUN4I_INTEN_TX BIT(1)
|
||||||
|
#define SUN4I_INTEN_RX BIT(0)
|
||||||
|
|
||||||
|
/* error code */
|
||||||
|
#define SUN4I_ERR_INRCV (0x1 << 5)
|
||||||
|
#define SUN4I_ERR_INTRANS (0x0 << 5)
|
||||||
|
|
||||||
|
/* filter mode */
|
||||||
|
#define SUN4I_FILTER_CLOSE 0
|
||||||
|
#define SUN4I_SINGLE_FLTER_MODE 1
|
||||||
|
#define SUN4I_DUAL_FILTER_MODE 2
|
||||||
|
|
||||||
|
/* message buffer flags */
|
||||||
|
#define SUN4I_MSG_EFF_FLAG BIT(7)
|
||||||
|
#define SUN4I_MSG_RTR_FLAG BIT(6)
|
||||||
|
|
||||||
|
/* max. number of interrupts handled in ISR */
|
||||||
|
#define SUN4I_CAN_MAX_IRQ 20
|
||||||
|
#define SUN4I_MODE_MAX_RETRIES 100
|
||||||
|
|
||||||
|
struct sun4ican_priv {
|
||||||
|
struct can_priv can;
|
||||||
|
void __iomem *base;
|
||||||
|
struct clk *clk;
|
||||||
|
spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct can_bittiming_const sun4ican_bittiming_const = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
.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 void sun4i_can_write_cmdreg(struct sun4ican_priv *priv, u8 val)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&priv->cmdreg_lock, flags);
|
||||||
|
writel(val, priv->base + SUN4I_REG_CMD_ADDR);
|
||||||
|
spin_unlock_irqrestore(&priv->cmdreg_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_normal_mode(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
int retry = SUN4I_MODE_MAX_RETRIES;
|
||||||
|
u32 mod_reg_val = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR);
|
||||||
|
mod_reg_val &= ~SUN4I_MSEL_RESET_MODE;
|
||||||
|
writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR);
|
||||||
|
} while (retry-- && (mod_reg_val & SUN4I_MSEL_RESET_MODE));
|
||||||
|
|
||||||
|
if (readl(priv->base + SUN4I_REG_MSEL_ADDR) & SUN4I_MSEL_RESET_MODE) {
|
||||||
|
netdev_err(dev,
|
||||||
|
"setting controller into normal mode failed!\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_reset_mode(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
int retry = SUN4I_MODE_MAX_RETRIES;
|
||||||
|
u32 mod_reg_val = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR);
|
||||||
|
mod_reg_val |= SUN4I_MSEL_RESET_MODE;
|
||||||
|
writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR);
|
||||||
|
} while (retry-- && !(mod_reg_val & SUN4I_MSEL_RESET_MODE));
|
||||||
|
|
||||||
|
if (!(readl(priv->base + SUN4I_REG_MSEL_ADDR) &
|
||||||
|
SUN4I_MSEL_RESET_MODE)) {
|
||||||
|
netdev_err(dev, "setting controller into reset mode failed!\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bittiming is called in reset_mode only */
|
||||||
|
static int sun4ican_set_bittiming(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
struct can_bittiming *bt = &priv->can.bittiming;
|
||||||
|
u32 cfg;
|
||||||
|
|
||||||
|
cfg = ((bt->brp - 1) & 0x3FF) |
|
||||||
|
(((bt->sjw - 1) & 0x3) << 14) |
|
||||||
|
(((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) << 16) |
|
||||||
|
(((bt->phase_seg2 - 1) & 0x7) << 20);
|
||||||
|
if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
|
||||||
|
cfg |= 0x800000;
|
||||||
|
|
||||||
|
netdev_dbg(dev, "setting BITTIMING=0x%08x\n", cfg);
|
||||||
|
writel(cfg, priv->base + SUN4I_REG_BTIME_ADDR);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4ican_get_berr_counter(const struct net_device *dev,
|
||||||
|
struct can_berr_counter *bec)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
u32 errors;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = clk_prepare_enable(priv->clk);
|
||||||
|
if (err) {
|
||||||
|
netdev_err(dev, "could not enable clock\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
errors = readl(priv->base + SUN4I_REG_ERRC_ADDR);
|
||||||
|
|
||||||
|
bec->txerr = errors & 0xFF;
|
||||||
|
bec->rxerr = (errors >> 16) & 0xFF;
|
||||||
|
|
||||||
|
clk_disable_unprepare(priv->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_can_start(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
int err;
|
||||||
|
u32 mod_reg_val;
|
||||||
|
|
||||||
|
/* we need to enter the reset mode */
|
||||||
|
err = set_reset_mode(dev);
|
||||||
|
if (err) {
|
||||||
|
netdev_err(dev, "could not enter reset mode\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set filters - we accept all */
|
||||||
|
writel(0x00000000, priv->base + SUN4I_REG_ACPC_ADDR);
|
||||||
|
writel(0xFFFFFFFF, priv->base + SUN4I_REG_ACPM_ADDR);
|
||||||
|
|
||||||
|
/* clear error counters and error code capture */
|
||||||
|
writel(0, priv->base + SUN4I_REG_ERRC_ADDR);
|
||||||
|
|
||||||
|
/* enable interrupts */
|
||||||
|
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
|
||||||
|
writel(0xFF, priv->base + SUN4I_REG_INTEN_ADDR);
|
||||||
|
else
|
||||||
|
writel(0xFF & ~SUN4I_INTEN_BERR,
|
||||||
|
priv->base + SUN4I_REG_INTEN_ADDR);
|
||||||
|
|
||||||
|
/* enter the selected mode */
|
||||||
|
mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR);
|
||||||
|
if (priv->can.ctrlmode & CAN_CTRLMODE_PRESUME_ACK)
|
||||||
|
mod_reg_val |= SUN4I_MSEL_LOOPBACK_MODE;
|
||||||
|
else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
||||||
|
mod_reg_val |= SUN4I_MSEL_LISTEN_ONLY_MODE;
|
||||||
|
writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR);
|
||||||
|
|
||||||
|
err = sun4ican_set_bittiming(dev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* we are ready to enter the normal mode */
|
||||||
|
err = set_normal_mode(dev);
|
||||||
|
if (err) {
|
||||||
|
netdev_err(dev, "could not enter normal mode\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_can_stop(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
priv->can.state = CAN_STATE_STOPPED;
|
||||||
|
/* we need to enter reset mode */
|
||||||
|
err = set_reset_mode(dev);
|
||||||
|
if (err) {
|
||||||
|
netdev_err(dev, "could not enter reset mode\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* disable all interrupts */
|
||||||
|
writel(0, priv->base + SUN4I_REG_INTEN_ADDR);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4ican_set_mode(struct net_device *dev, enum can_mode mode)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case CAN_MODE_START:
|
||||||
|
err = sun4i_can_start(dev);
|
||||||
|
if (err) {
|
||||||
|
netdev_err(dev, "starting CAN controller failed!\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (netif_queue_stopped(dev))
|
||||||
|
netif_wake_queue(dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* transmit a CAN message
|
||||||
|
* message layout in the sk_buff should be like this:
|
||||||
|
* xx xx xx xx ff ll 00 11 22 33 44 55 66 77
|
||||||
|
* [ can_id ] [flags] [len] [can data (up to 8 bytes]
|
||||||
|
*/
|
||||||
|
static int sun4ican_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||||
|
u8 dlc;
|
||||||
|
u32 dreg, msg_flag_n;
|
||||||
|
canid_t id;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (can_dropped_invalid_skb(dev, skb))
|
||||||
|
return NETDEV_TX_OK;
|
||||||
|
|
||||||
|
netif_stop_queue(dev);
|
||||||
|
|
||||||
|
id = cf->can_id;
|
||||||
|
dlc = cf->can_dlc;
|
||||||
|
msg_flag_n = dlc;
|
||||||
|
|
||||||
|
if (id & CAN_RTR_FLAG)
|
||||||
|
msg_flag_n |= SUN4I_MSG_RTR_FLAG;
|
||||||
|
|
||||||
|
if (id & CAN_EFF_FLAG) {
|
||||||
|
msg_flag_n |= SUN4I_MSG_EFF_FLAG;
|
||||||
|
dreg = SUN4I_REG_BUF5_ADDR;
|
||||||
|
writel((id >> 21) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR);
|
||||||
|
writel((id >> 13) & 0xFF, priv->base + SUN4I_REG_BUF2_ADDR);
|
||||||
|
writel((id >> 5) & 0xFF, priv->base + SUN4I_REG_BUF3_ADDR);
|
||||||
|
writel((id << 3) & 0xF8, priv->base + SUN4I_REG_BUF4_ADDR);
|
||||||
|
} else {
|
||||||
|
dreg = SUN4I_REG_BUF3_ADDR;
|
||||||
|
writel((id >> 3) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR);
|
||||||
|
writel((id << 5) & 0xE0, priv->base + SUN4I_REG_BUF2_ADDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < dlc; i++)
|
||||||
|
writel(cf->data[i], priv->base + (dreg + i * 4));
|
||||||
|
|
||||||
|
writel(msg_flag_n, priv->base + SUN4I_REG_BUF0_ADDR);
|
||||||
|
|
||||||
|
can_put_echo_skb(skb, dev, 0);
|
||||||
|
|
||||||
|
if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
|
||||||
|
sun4i_can_write_cmdreg(priv, SUN4I_CMD_SELF_RCV_REQ);
|
||||||
|
else
|
||||||
|
sun4i_can_write_cmdreg(priv, SUN4I_CMD_TRANS_REQ);
|
||||||
|
|
||||||
|
return NETDEV_TX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sun4i_can_rx(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
struct net_device_stats *stats = &dev->stats;
|
||||||
|
struct can_frame *cf;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
u8 fi;
|
||||||
|
u32 dreg;
|
||||||
|
canid_t id;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* create zero'ed CAN frame buffer */
|
||||||
|
skb = alloc_can_skb(dev, &cf);
|
||||||
|
if (!skb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fi = readl(priv->base + SUN4I_REG_BUF0_ADDR);
|
||||||
|
cf->can_dlc = get_can_dlc(fi & 0x0F);
|
||||||
|
if (fi & SUN4I_MSG_EFF_FLAG) {
|
||||||
|
dreg = SUN4I_REG_BUF5_ADDR;
|
||||||
|
id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 21) |
|
||||||
|
(readl(priv->base + SUN4I_REG_BUF2_ADDR) << 13) |
|
||||||
|
(readl(priv->base + SUN4I_REG_BUF3_ADDR) << 5) |
|
||||||
|
((readl(priv->base + SUN4I_REG_BUF4_ADDR) >> 3) & 0x1f);
|
||||||
|
id |= CAN_EFF_FLAG;
|
||||||
|
} else {
|
||||||
|
dreg = SUN4I_REG_BUF3_ADDR;
|
||||||
|
id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 3) |
|
||||||
|
((readl(priv->base + SUN4I_REG_BUF2_ADDR) >> 5) & 0x7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remote frame ? */
|
||||||
|
if (fi & SUN4I_MSG_RTR_FLAG)
|
||||||
|
id |= CAN_RTR_FLAG;
|
||||||
|
else
|
||||||
|
for (i = 0; i < cf->can_dlc; i++)
|
||||||
|
cf->data[i] = readl(priv->base + dreg + i * 4);
|
||||||
|
|
||||||
|
cf->can_id = id;
|
||||||
|
|
||||||
|
sun4i_can_write_cmdreg(priv, SUN4I_CMD_RELEASE_RBUF);
|
||||||
|
|
||||||
|
stats->rx_packets++;
|
||||||
|
stats->rx_bytes += cf->can_dlc;
|
||||||
|
netif_rx(skb);
|
||||||
|
|
||||||
|
can_led_event(dev, CAN_LED_EVENT_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
struct net_device_stats *stats = &dev->stats;
|
||||||
|
struct can_frame *cf;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
enum can_state state = priv->can.state;
|
||||||
|
enum can_state rx_state, tx_state;
|
||||||
|
unsigned int rxerr, txerr, errc;
|
||||||
|
u32 ecc, alc;
|
||||||
|
|
||||||
|
/* we don't skip if alloc fails because we want the stats anyhow */
|
||||||
|
skb = alloc_can_err_skb(dev, &cf);
|
||||||
|
|
||||||
|
errc = readl(priv->base + SUN4I_REG_ERRC_ADDR);
|
||||||
|
rxerr = (errc >> 16) & 0xFF;
|
||||||
|
txerr = errc & 0xFF;
|
||||||
|
|
||||||
|
if (skb) {
|
||||||
|
cf->data[6] = txerr;
|
||||||
|
cf->data[7] = rxerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isrc & SUN4I_INT_DATA_OR) {
|
||||||
|
/* data overrun interrupt */
|
||||||
|
netdev_dbg(dev, "data overrun interrupt\n");
|
||||||
|
if (likely(skb)) {
|
||||||
|
cf->can_id |= CAN_ERR_CRTL;
|
||||||
|
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
||||||
|
}
|
||||||
|
stats->rx_over_errors++;
|
||||||
|
stats->rx_errors++;
|
||||||
|
/* clear bit */
|
||||||
|
sun4i_can_write_cmdreg(priv, SUN4I_CMD_CLEAR_OR_FLAG);
|
||||||
|
}
|
||||||
|
if (isrc & SUN4I_INT_ERR_WRN) {
|
||||||
|
/* error warning interrupt */
|
||||||
|
netdev_dbg(dev, "error warning interrupt\n");
|
||||||
|
|
||||||
|
if (status & SUN4I_STA_BUS_OFF)
|
||||||
|
state = CAN_STATE_BUS_OFF;
|
||||||
|
else if (status & SUN4I_STA_ERR_STA)
|
||||||
|
state = CAN_STATE_ERROR_WARNING;
|
||||||
|
else
|
||||||
|
state = CAN_STATE_ERROR_ACTIVE;
|
||||||
|
}
|
||||||
|
if (isrc & SUN4I_INT_BUS_ERR) {
|
||||||
|
/* bus error interrupt */
|
||||||
|
netdev_dbg(dev, "bus error interrupt\n");
|
||||||
|
priv->can.can_stats.bus_error++;
|
||||||
|
stats->rx_errors++;
|
||||||
|
|
||||||
|
if (likely(skb)) {
|
||||||
|
ecc = readl(priv->base + SUN4I_REG_STA_ADDR);
|
||||||
|
|
||||||
|
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
|
||||||
|
|
||||||
|
switch (ecc & SUN4I_STA_MASK_ERR) {
|
||||||
|
case SUN4I_STA_BIT_ERR:
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_BIT;
|
||||||
|
break;
|
||||||
|
case SUN4I_STA_FORM_ERR:
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_FORM;
|
||||||
|
break;
|
||||||
|
case SUN4I_STA_STUFF_ERR:
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_STUFF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_UNSPEC;
|
||||||
|
cf->data[3] = (ecc & SUN4I_STA_ERR_SEG_CODE)
|
||||||
|
>> 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* error occurred during transmission? */
|
||||||
|
if ((ecc & SUN4I_STA_ERR_DIR) == 0)
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_TX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isrc & SUN4I_INT_ERR_PASSIVE) {
|
||||||
|
/* error passive interrupt */
|
||||||
|
netdev_dbg(dev, "error passive interrupt\n");
|
||||||
|
if (state == CAN_STATE_ERROR_PASSIVE)
|
||||||
|
state = CAN_STATE_ERROR_WARNING;
|
||||||
|
else
|
||||||
|
state = CAN_STATE_ERROR_PASSIVE;
|
||||||
|
}
|
||||||
|
if (isrc & SUN4I_INT_ARB_LOST) {
|
||||||
|
/* arbitration lost interrupt */
|
||||||
|
netdev_dbg(dev, "arbitration lost interrupt\n");
|
||||||
|
alc = readl(priv->base + SUN4I_REG_STA_ADDR);
|
||||||
|
priv->can.can_stats.arbitration_lost++;
|
||||||
|
stats->tx_errors++;
|
||||||
|
if (likely(skb)) {
|
||||||
|
cf->can_id |= CAN_ERR_LOSTARB;
|
||||||
|
cf->data[0] = (alc >> 8) & 0x1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != priv->can.state) {
|
||||||
|
tx_state = txerr >= rxerr ? state : 0;
|
||||||
|
rx_state = txerr <= rxerr ? state : 0;
|
||||||
|
|
||||||
|
if (likely(skb))
|
||||||
|
can_change_state(dev, cf, tx_state, rx_state);
|
||||||
|
else
|
||||||
|
priv->can.state = state;
|
||||||
|
if (state == CAN_STATE_BUS_OFF)
|
||||||
|
can_bus_off(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(skb)) {
|
||||||
|
stats->rx_packets++;
|
||||||
|
stats->rx_bytes += cf->can_dlc;
|
||||||
|
netif_rx(skb);
|
||||||
|
} else {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t sun4i_can_interrupt(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct net_device *dev = (struct net_device *)dev_id;
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
struct net_device_stats *stats = &dev->stats;
|
||||||
|
u8 isrc, status;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
while ((isrc = readl(priv->base + SUN4I_REG_INT_ADDR)) &&
|
||||||
|
(n < SUN4I_CAN_MAX_IRQ)) {
|
||||||
|
n++;
|
||||||
|
status = readl(priv->base + SUN4I_REG_STA_ADDR);
|
||||||
|
|
||||||
|
if (isrc & SUN4I_INT_WAKEUP)
|
||||||
|
netdev_warn(dev, "wakeup interrupt\n");
|
||||||
|
|
||||||
|
if (isrc & SUN4I_INT_TBUF_VLD) {
|
||||||
|
/* transmission complete interrupt */
|
||||||
|
stats->tx_bytes +=
|
||||||
|
readl(priv->base +
|
||||||
|
SUN4I_REG_RBUF_RBACK_START_ADDR) & 0xf;
|
||||||
|
stats->tx_packets++;
|
||||||
|
can_get_echo_skb(dev, 0);
|
||||||
|
netif_wake_queue(dev);
|
||||||
|
can_led_event(dev, CAN_LED_EVENT_TX);
|
||||||
|
}
|
||||||
|
if (isrc & SUN4I_INT_RBUF_VLD) {
|
||||||
|
/* receive interrupt */
|
||||||
|
while (status & SUN4I_STA_RBUF_RDY) {
|
||||||
|
/* RX buffer is not empty */
|
||||||
|
sun4i_can_rx(dev);
|
||||||
|
status = readl(priv->base + SUN4I_REG_STA_ADDR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isrc &
|
||||||
|
(SUN4I_INT_DATA_OR | SUN4I_INT_ERR_WRN | SUN4I_INT_BUS_ERR |
|
||||||
|
SUN4I_INT_ERR_PASSIVE | SUN4I_INT_ARB_LOST)) {
|
||||||
|
/* error interrupt */
|
||||||
|
if (sun4i_can_err(dev, isrc, status))
|
||||||
|
netdev_err(dev, "can't allocate buffer - clearing pending interrupts\n");
|
||||||
|
}
|
||||||
|
/* clear interrupts */
|
||||||
|
writel(isrc, priv->base + SUN4I_REG_INT_ADDR);
|
||||||
|
readl(priv->base + SUN4I_REG_INT_ADDR);
|
||||||
|
}
|
||||||
|
if (n >= SUN4I_CAN_MAX_IRQ)
|
||||||
|
netdev_dbg(dev, "%d messages handled in ISR", n);
|
||||||
|
|
||||||
|
return (n) ? IRQ_HANDLED : IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4ican_open(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* common open */
|
||||||
|
err = open_candev(dev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* register interrupt handler */
|
||||||
|
err = request_irq(dev->irq, sun4i_can_interrupt, 0, dev->name, dev);
|
||||||
|
if (err) {
|
||||||
|
netdev_err(dev, "request_irq err: %d\n", err);
|
||||||
|
goto exit_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* turn on clocking for CAN peripheral block */
|
||||||
|
err = clk_prepare_enable(priv->clk);
|
||||||
|
if (err) {
|
||||||
|
netdev_err(dev, "could not enable CAN peripheral clock\n");
|
||||||
|
goto exit_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sun4i_can_start(dev);
|
||||||
|
if (err) {
|
||||||
|
netdev_err(dev, "could not start CAN peripheral\n");
|
||||||
|
goto exit_can_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
can_led_event(dev, CAN_LED_EVENT_OPEN);
|
||||||
|
netif_start_queue(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit_can_start:
|
||||||
|
clk_disable_unprepare(priv->clk);
|
||||||
|
exit_clock:
|
||||||
|
free_irq(dev->irq, dev);
|
||||||
|
exit_irq:
|
||||||
|
close_candev(dev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4ican_close(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||||
|
|
||||||
|
netif_stop_queue(dev);
|
||||||
|
sun4i_can_stop(dev);
|
||||||
|
clk_disable_unprepare(priv->clk);
|
||||||
|
|
||||||
|
free_irq(dev->irq, dev);
|
||||||
|
close_candev(dev);
|
||||||
|
can_led_event(dev, CAN_LED_EVENT_STOP);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct net_device_ops sun4ican_netdev_ops = {
|
||||||
|
.ndo_open = sun4ican_open,
|
||||||
|
.ndo_stop = sun4ican_close,
|
||||||
|
.ndo_start_xmit = sun4ican_start_xmit,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id sun4ican_of_match[] = {
|
||||||
|
{.compatible = "allwinner,sun4i-a10-can"},
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(of, sun4ican_of_match);
|
||||||
|
|
||||||
|
static int sun4ican_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct net_device *dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
unregister_netdev(dev);
|
||||||
|
free_candev(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4ican_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct resource *mem;
|
||||||
|
struct clk *clk;
|
||||||
|
void __iomem *addr;
|
||||||
|
int err, irq;
|
||||||
|
struct net_device *dev;
|
||||||
|
struct sun4ican_priv *priv;
|
||||||
|
|
||||||
|
clk = of_clk_get(np, 0);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
|
dev_err(&pdev->dev, "unable to request clock\n");
|
||||||
|
err = -ENODEV;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "could not get a valid irq\n");
|
||||||
|
err = -ENODEV;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
addr = devm_ioremap_resource(&pdev->dev, mem);
|
||||||
|
if (IS_ERR(addr)) {
|
||||||
|
err = -EBUSY;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = alloc_candev(sizeof(struct sun4ican_priv), 1);
|
||||||
|
if (!dev) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"could not allocate memory for CAN device\n");
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->netdev_ops = &sun4ican_netdev_ops;
|
||||||
|
dev->irq = irq;
|
||||||
|
dev->flags |= IFF_ECHO;
|
||||||
|
|
||||||
|
priv = netdev_priv(dev);
|
||||||
|
priv->can.clock.freq = clk_get_rate(clk);
|
||||||
|
priv->can.bittiming_const = &sun4ican_bittiming_const;
|
||||||
|
priv->can.do_set_mode = sun4ican_set_mode;
|
||||||
|
priv->can.do_get_berr_counter = sun4ican_get_berr_counter;
|
||||||
|
priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING |
|
||||||
|
CAN_CTRLMODE_LISTENONLY |
|
||||||
|
CAN_CTRLMODE_LOOPBACK |
|
||||||
|
CAN_CTRLMODE_PRESUME_ACK |
|
||||||
|
CAN_CTRLMODE_3_SAMPLES;
|
||||||
|
priv->base = addr;
|
||||||
|
priv->clk = clk;
|
||||||
|
spin_lock_init(&priv->cmdreg_lock);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, dev);
|
||||||
|
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||||
|
|
||||||
|
err = register_candev(dev);
|
||||||
|
if (err) {
|
||||||
|
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
|
||||||
|
DRV_NAME, err);
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
devm_can_led_init(dev);
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "device registered (base=%p, irq=%d)\n",
|
||||||
|
priv->base, dev->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit_free:
|
||||||
|
free_candev(dev);
|
||||||
|
exit:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver sun4i_can_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
.of_match_table = sun4ican_of_match,
|
||||||
|
},
|
||||||
|
.probe = sun4ican_probe,
|
||||||
|
.remove = sun4ican_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(sun4i_can_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Peter Chen <xingkongcp@gmail.com>");
|
||||||
|
MODULE_AUTHOR("Gerhard Bertelsmann <info@gerhard-bertelsmann.de>");
|
||||||
|
MODULE_LICENSE("Dual BSD/GPL");
|
||||||
|
MODULE_DESCRIPTION("CAN driver for Allwinner SoCs (A10/A20)");
|
@ -21,10 +21,13 @@
|
|||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_net.h>
|
||||||
#include <net/dsa.h>
|
#include <net/dsa.h>
|
||||||
#include <linux/ethtool.h>
|
#include <linux/ethtool.h>
|
||||||
#include <linux/if_bridge.h>
|
#include <linux/if_bridge.h>
|
||||||
#include <linux/brcmphy.h>
|
#include <linux/brcmphy.h>
|
||||||
|
#include <linux/etherdevice.h>
|
||||||
|
#include <net/switchdev.h>
|
||||||
|
|
||||||
#include "bcm_sf2.h"
|
#include "bcm_sf2.h"
|
||||||
#include "bcm_sf2_regs.h"
|
#include "bcm_sf2_regs.h"
|
||||||
@ -264,6 +267,50 @@ static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void bcm_sf2_port_intr_enable(struct bcm_sf2_priv *priv,
|
||||||
|
int port)
|
||||||
|
{
|
||||||
|
unsigned int off;
|
||||||
|
|
||||||
|
switch (port) {
|
||||||
|
case 7:
|
||||||
|
off = P7_IRQ_OFF;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
/* Port 0 interrupts are located on the first bank */
|
||||||
|
intrl2_0_mask_clear(priv, P_IRQ_MASK(P0_IRQ_OFF));
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
off = P_IRQ_OFF(port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
intrl2_1_mask_clear(priv, P_IRQ_MASK(off));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bcm_sf2_port_intr_disable(struct bcm_sf2_priv *priv,
|
||||||
|
int port)
|
||||||
|
{
|
||||||
|
unsigned int off;
|
||||||
|
|
||||||
|
switch (port) {
|
||||||
|
case 7:
|
||||||
|
off = P7_IRQ_OFF;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
/* Port 0 interrupts are located on the first bank */
|
||||||
|
intrl2_0_mask_set(priv, P_IRQ_MASK(P0_IRQ_OFF));
|
||||||
|
intrl2_0_writel(priv, P_IRQ_MASK(P0_IRQ_OFF), INTRL2_CPU_CLEAR);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
off = P_IRQ_OFF(port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
intrl2_1_mask_set(priv, P_IRQ_MASK(off));
|
||||||
|
intrl2_1_writel(priv, P_IRQ_MASK(off), INTRL2_CPU_CLEAR);
|
||||||
|
}
|
||||||
|
|
||||||
static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
|
static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
|
||||||
struct phy_device *phy)
|
struct phy_device *phy)
|
||||||
{
|
{
|
||||||
@ -280,7 +327,7 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
|
|||||||
core_writel(priv, 0, CORE_G_PCTL_PORT(port));
|
core_writel(priv, 0, CORE_G_PCTL_PORT(port));
|
||||||
|
|
||||||
/* Re-enable the GPHY and re-apply workarounds */
|
/* Re-enable the GPHY and re-apply workarounds */
|
||||||
if (port == 0 && priv->hw_params.num_gphy == 1) {
|
if (priv->int_phy_mask & 1 << port && priv->hw_params.num_gphy == 1) {
|
||||||
bcm_sf2_gphy_enable_set(ds, true);
|
bcm_sf2_gphy_enable_set(ds, true);
|
||||||
if (phy) {
|
if (phy) {
|
||||||
/* if phy_stop() has been called before, phy
|
/* if phy_stop() has been called before, phy
|
||||||
@ -297,9 +344,9 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable port 7 interrupts to get notified */
|
/* Enable MoCA port interrupts to get notified */
|
||||||
if (port == 7)
|
if (port == priv->moca_port)
|
||||||
intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF));
|
bcm_sf2_port_intr_enable(priv, port);
|
||||||
|
|
||||||
/* Set this port, and only this one to be in the default VLAN,
|
/* Set this port, and only this one to be in the default VLAN,
|
||||||
* if member of a bridge, restore its membership prior to
|
* if member of a bridge, restore its membership prior to
|
||||||
@ -329,12 +376,10 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port,
|
|||||||
if (priv->wol_ports_mask & (1 << port))
|
if (priv->wol_ports_mask & (1 << port))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (port == 7) {
|
if (port == priv->moca_port)
|
||||||
intrl2_1_mask_set(priv, P_IRQ_MASK(P7_IRQ_OFF));
|
bcm_sf2_port_intr_disable(priv, port);
|
||||||
intrl2_1_writel(priv, P_IRQ_MASK(P7_IRQ_OFF), INTRL2_CPU_CLEAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (port == 0 && priv->hw_params.num_gphy == 1)
|
if (priv->int_phy_mask & 1 << port && priv->hw_params.num_gphy == 1)
|
||||||
bcm_sf2_gphy_enable_set(ds, false);
|
bcm_sf2_gphy_enable_set(ds, false);
|
||||||
|
|
||||||
if (dsa_is_cpu_port(ds, port))
|
if (dsa_is_cpu_port(ds, port))
|
||||||
@ -555,6 +600,236 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Address Resolution Logic routines */
|
||||||
|
static int bcm_sf2_arl_op_wait(struct bcm_sf2_priv *priv)
|
||||||
|
{
|
||||||
|
unsigned int timeout = 10;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
do {
|
||||||
|
reg = core_readl(priv, CORE_ARLA_RWCTL);
|
||||||
|
if (!(reg & ARL_STRTDN))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
} while (timeout--);
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_arl_rw_op(struct bcm_sf2_priv *priv, unsigned int op)
|
||||||
|
{
|
||||||
|
u32 cmd;
|
||||||
|
|
||||||
|
if (op > ARL_RW)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
cmd = core_readl(priv, CORE_ARLA_RWCTL);
|
||||||
|
cmd &= ~IVL_SVL_SELECT;
|
||||||
|
cmd |= ARL_STRTDN;
|
||||||
|
if (op)
|
||||||
|
cmd |= ARL_RW;
|
||||||
|
else
|
||||||
|
cmd &= ~ARL_RW;
|
||||||
|
core_writel(priv, cmd, CORE_ARLA_RWCTL);
|
||||||
|
|
||||||
|
return bcm_sf2_arl_op_wait(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_arl_read(struct bcm_sf2_priv *priv, u64 mac,
|
||||||
|
u16 vid, struct bcm_sf2_arl_entry *ent, u8 *idx,
|
||||||
|
bool is_valid)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bcm_sf2_arl_op_wait(priv);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Read the 4 bins */
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
u64 mac_vid;
|
||||||
|
u32 fwd_entry;
|
||||||
|
|
||||||
|
mac_vid = core_readq(priv, CORE_ARLA_MACVID_ENTRY(i));
|
||||||
|
fwd_entry = core_readl(priv, CORE_ARLA_FWD_ENTRY(i));
|
||||||
|
bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
|
||||||
|
|
||||||
|
if (ent->is_valid && is_valid) {
|
||||||
|
*idx = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is the MAC we just deleted */
|
||||||
|
if (!is_valid && (mac_vid & mac))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_arl_op(struct bcm_sf2_priv *priv, int op, int port,
|
||||||
|
const unsigned char *addr, u16 vid, bool is_valid)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_arl_entry ent;
|
||||||
|
u32 fwd_entry;
|
||||||
|
u64 mac, mac_vid = 0;
|
||||||
|
u8 idx = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Convert the array into a 64-bit MAC */
|
||||||
|
mac = bcm_sf2_mac_to_u64(addr);
|
||||||
|
|
||||||
|
/* Perform a read for the given MAC and VID */
|
||||||
|
core_writeq(priv, mac, CORE_ARLA_MAC);
|
||||||
|
core_writel(priv, vid, CORE_ARLA_VID);
|
||||||
|
|
||||||
|
/* Issue a read operation for this MAC */
|
||||||
|
ret = bcm_sf2_arl_rw_op(priv, 1);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
|
||||||
|
/* If this is a read, just finish now */
|
||||||
|
if (op)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* We could not find a matching MAC, so reset to a new entry */
|
||||||
|
if (ret) {
|
||||||
|
fwd_entry = 0;
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ent, 0, sizeof(ent));
|
||||||
|
ent.port = port;
|
||||||
|
ent.is_valid = is_valid;
|
||||||
|
ent.vid = vid;
|
||||||
|
ent.is_static = true;
|
||||||
|
memcpy(ent.mac, addr, ETH_ALEN);
|
||||||
|
bcm_sf2_arl_from_entry(&mac_vid, &fwd_entry, &ent);
|
||||||
|
|
||||||
|
core_writeq(priv, mac_vid, CORE_ARLA_MACVID_ENTRY(idx));
|
||||||
|
core_writel(priv, fwd_entry, CORE_ARLA_FWD_ENTRY(idx));
|
||||||
|
|
||||||
|
ret = bcm_sf2_arl_rw_op(priv, 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Re-read the entry to check */
|
||||||
|
return bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_fdb_prepare(struct dsa_switch *ds, int port,
|
||||||
|
const struct switchdev_obj_port_fdb *fdb,
|
||||||
|
struct switchdev_trans *trans)
|
||||||
|
{
|
||||||
|
/* We do not need to do anything specific here yet */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_fdb_add(struct dsa_switch *ds, int port,
|
||||||
|
const struct switchdev_obj_port_fdb *fdb,
|
||||||
|
struct switchdev_trans *trans)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
|
||||||
|
return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_fdb_del(struct dsa_switch *ds, int port,
|
||||||
|
const struct switchdev_obj_port_fdb *fdb)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
|
||||||
|
return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_arl_search_wait(struct bcm_sf2_priv *priv)
|
||||||
|
{
|
||||||
|
unsigned timeout = 1000;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
do {
|
||||||
|
reg = core_readl(priv, CORE_ARLA_SRCH_CTL);
|
||||||
|
if (!(reg & ARLA_SRCH_STDN))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (reg & ARLA_SRCH_VLID)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
} while (timeout--);
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_sf2_arl_search_rd(struct bcm_sf2_priv *priv, u8 idx,
|
||||||
|
struct bcm_sf2_arl_entry *ent)
|
||||||
|
{
|
||||||
|
u64 mac_vid;
|
||||||
|
u32 fwd_entry;
|
||||||
|
|
||||||
|
mac_vid = core_readq(priv, CORE_ARLA_SRCH_RSLT_MACVID(idx));
|
||||||
|
fwd_entry = core_readl(priv, CORE_ARLA_SRCH_RSLT(idx));
|
||||||
|
bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_fdb_copy(struct net_device *dev, int port,
|
||||||
|
const struct bcm_sf2_arl_entry *ent,
|
||||||
|
struct switchdev_obj_port_fdb *fdb,
|
||||||
|
int (*cb)(struct switchdev_obj *obj))
|
||||||
|
{
|
||||||
|
if (!ent->is_valid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (port != ent->port)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ether_addr_copy(fdb->addr, ent->mac);
|
||||||
|
fdb->vid = ent->vid;
|
||||||
|
fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE;
|
||||||
|
|
||||||
|
return cb(&fdb->obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_fdb_dump(struct dsa_switch *ds, int port,
|
||||||
|
struct switchdev_obj_port_fdb *fdb,
|
||||||
|
int (*cb)(struct switchdev_obj *obj))
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
struct net_device *dev = ds->ports[port];
|
||||||
|
struct bcm_sf2_arl_entry results[2];
|
||||||
|
unsigned int count = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Start search operation */
|
||||||
|
core_writel(priv, ARLA_SRCH_STDN, CORE_ARLA_SRCH_CTL);
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = bcm_sf2_arl_search_wait(priv);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Read both entries, then return their values back */
|
||||||
|
bcm_sf2_arl_search_rd(priv, 0, &results[0]);
|
||||||
|
ret = bcm_sf2_sw_fdb_copy(dev, port, &results[0], fdb, cb);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
bcm_sf2_arl_search_rd(priv, 1, &results[1]);
|
||||||
|
ret = bcm_sf2_sw_fdb_copy(dev, port, &results[1], fdb, cb);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!results[0].is_valid && !results[1].is_valid)
|
||||||
|
break;
|
||||||
|
|
||||||
|
} while (count++ < CORE_ARLA_NUM_ENTRIES);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
|
static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct bcm_sf2_priv *priv = dev_id;
|
struct bcm_sf2_priv *priv = dev_id;
|
||||||
@ -615,6 +890,42 @@ static void bcm_sf2_intr_disable(struct bcm_sf2_priv *priv)
|
|||||||
intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
|
intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bcm_sf2_identify_ports(struct bcm_sf2_priv *priv,
|
||||||
|
struct device_node *dn)
|
||||||
|
{
|
||||||
|
struct device_node *port;
|
||||||
|
const char *phy_mode_str;
|
||||||
|
int mode;
|
||||||
|
unsigned int port_num;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
priv->moca_port = -1;
|
||||||
|
|
||||||
|
for_each_available_child_of_node(dn, port) {
|
||||||
|
if (of_property_read_u32(port, "reg", &port_num))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Internal PHYs get assigned a specific 'phy-mode' property
|
||||||
|
* value: "internal" to help flag them before MDIO probing
|
||||||
|
* has completed, since they might be turned off at that
|
||||||
|
* time
|
||||||
|
*/
|
||||||
|
mode = of_get_phy_mode(port);
|
||||||
|
if (mode < 0) {
|
||||||
|
ret = of_property_read_string(port, "phy-mode",
|
||||||
|
&phy_mode_str);
|
||||||
|
if (ret < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!strcasecmp(phy_mode_str, "internal"))
|
||||||
|
priv->int_phy_mask |= 1 << port_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == PHY_INTERFACE_MODE_MOCA)
|
||||||
|
priv->moca_port = port_num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int bcm_sf2_sw_setup(struct dsa_switch *ds)
|
static int bcm_sf2_sw_setup(struct dsa_switch *ds)
|
||||||
{
|
{
|
||||||
const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
|
const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
|
||||||
@ -633,6 +944,7 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)
|
|||||||
* level
|
* level
|
||||||
*/
|
*/
|
||||||
dn = ds->pd->of_node->parent;
|
dn = ds->pd->of_node->parent;
|
||||||
|
bcm_sf2_identify_ports(priv, ds->pd->of_node);
|
||||||
|
|
||||||
priv->irq0 = irq_of_parse_and_map(dn, 0);
|
priv->irq0 = irq_of_parse_and_map(dn, 0);
|
||||||
priv->irq1 = irq_of_parse_and_map(dn, 1);
|
priv->irq1 = irq_of_parse_and_map(dn, 1);
|
||||||
@ -913,7 +1225,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
|
|||||||
|
|
||||||
status->link = 0;
|
status->link = 0;
|
||||||
|
|
||||||
/* Port 7 is special as we do not get link status from CORE_LNKSTS,
|
/* MoCA port is special as we do not get link status from CORE_LNKSTS,
|
||||||
* which means that we need to force the link at the port override
|
* which means that we need to force the link at the port override
|
||||||
* level to get the data to flow. We do use what the interrupt handler
|
* level to get the data to flow. We do use what the interrupt handler
|
||||||
* did determine before.
|
* did determine before.
|
||||||
@ -921,7 +1233,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
|
|||||||
* For the other ports, we just force the link status, since this is
|
* For the other ports, we just force the link status, since this is
|
||||||
* a fixed PHY device.
|
* a fixed PHY device.
|
||||||
*/
|
*/
|
||||||
if (port == 7) {
|
if (port == priv->moca_port) {
|
||||||
status->link = priv->port_sts[port].link;
|
status->link = priv->port_sts[port].link;
|
||||||
/* For MoCA interfaces, also force a link down notification
|
/* For MoCA interfaces, also force a link down notification
|
||||||
* since some version of the user-space daemon (mocad) use
|
* since some version of the user-space daemon (mocad) use
|
||||||
@ -1076,6 +1388,10 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = {
|
|||||||
.port_join_bridge = bcm_sf2_sw_br_join,
|
.port_join_bridge = bcm_sf2_sw_br_join,
|
||||||
.port_leave_bridge = bcm_sf2_sw_br_leave,
|
.port_leave_bridge = bcm_sf2_sw_br_leave,
|
||||||
.port_stp_update = bcm_sf2_sw_br_set_stp_state,
|
.port_stp_update = bcm_sf2_sw_br_set_stp_state,
|
||||||
|
.port_fdb_prepare = bcm_sf2_sw_fdb_prepare,
|
||||||
|
.port_fdb_add = bcm_sf2_sw_fdb_add,
|
||||||
|
.port_fdb_del = bcm_sf2_sw_fdb_del,
|
||||||
|
.port_fdb_dump = bcm_sf2_sw_fdb_dump,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init bcm_sf2_init(void)
|
static int __init bcm_sf2_init(void)
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/mii.h>
|
#include <linux/mii.h>
|
||||||
#include <linux/ethtool.h>
|
#include <linux/ethtool.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
|
||||||
#include <net/dsa.h>
|
#include <net/dsa.h>
|
||||||
|
|
||||||
@ -50,6 +52,60 @@ struct bcm_sf2_port_status {
|
|||||||
u32 vlan_ctl_mask;
|
u32 vlan_ctl_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct bcm_sf2_arl_entry {
|
||||||
|
u8 port;
|
||||||
|
u8 mac[ETH_ALEN];
|
||||||
|
u16 vid;
|
||||||
|
u8 is_valid:1;
|
||||||
|
u8 is_age:1;
|
||||||
|
u8 is_static:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void bcm_sf2_mac_from_u64(u64 src, u8 *dst)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ETH_ALEN; i++)
|
||||||
|
dst[ETH_ALEN - 1 - i] = (src >> (8 * i)) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 bcm_sf2_mac_to_u64(const u8 *src)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
u64 dst = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ETH_ALEN; i++)
|
||||||
|
dst |= (u64)src[ETH_ALEN - 1 - i] << (8 * i);
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bcm_sf2_arl_to_entry(struct bcm_sf2_arl_entry *ent,
|
||||||
|
u64 mac_vid, u32 fwd_entry)
|
||||||
|
{
|
||||||
|
memset(ent, 0, sizeof(*ent));
|
||||||
|
ent->port = fwd_entry & PORTID_MASK;
|
||||||
|
ent->is_valid = !!(fwd_entry & ARL_VALID);
|
||||||
|
ent->is_age = !!(fwd_entry & ARL_AGE);
|
||||||
|
ent->is_static = !!(fwd_entry & ARL_STATIC);
|
||||||
|
bcm_sf2_mac_from_u64(mac_vid, ent->mac);
|
||||||
|
ent->vid = mac_vid >> VID_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bcm_sf2_arl_from_entry(u64 *mac_vid, u32 *fwd_entry,
|
||||||
|
const struct bcm_sf2_arl_entry *ent)
|
||||||
|
{
|
||||||
|
*mac_vid = bcm_sf2_mac_to_u64(ent->mac);
|
||||||
|
*mac_vid |= (u64)(ent->vid & VID_MASK) << VID_SHIFT;
|
||||||
|
*fwd_entry = ent->port & PORTID_MASK;
|
||||||
|
if (ent->is_valid)
|
||||||
|
*fwd_entry |= ARL_VALID;
|
||||||
|
if (ent->is_static)
|
||||||
|
*fwd_entry |= ARL_STATIC;
|
||||||
|
if (ent->is_age)
|
||||||
|
*fwd_entry |= ARL_AGE;
|
||||||
|
}
|
||||||
|
|
||||||
struct bcm_sf2_priv {
|
struct bcm_sf2_priv {
|
||||||
/* Base registers, keep those in order with BCM_SF2_REGS_NAME */
|
/* Base registers, keep those in order with BCM_SF2_REGS_NAME */
|
||||||
void __iomem *core;
|
void __iomem *core;
|
||||||
@ -78,6 +134,12 @@ struct bcm_sf2_priv {
|
|||||||
|
|
||||||
/* Mask of ports enabled for Wake-on-LAN */
|
/* Mask of ports enabled for Wake-on-LAN */
|
||||||
u32 wol_ports_mask;
|
u32 wol_ports_mask;
|
||||||
|
|
||||||
|
/* MoCA port location */
|
||||||
|
int moca_port;
|
||||||
|
|
||||||
|
/* Bitmask of ports having an integrated PHY */
|
||||||
|
unsigned int int_phy_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bcm_sf2_hw_stats {
|
struct bcm_sf2_hw_stats {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user