Linux Documentation
 help / color / mirror / Atom feed
* [PATCH 5/7] net: arcnet: remove ISA and PCMCIA support; modernize documentation
From: Ethan Nelson-Moore @ 2026-05-18  1:07 UTC (permalink / raw)
  To: linux-doc, netdev, linux-mips
  Cc: Ethan Nelson-Moore, Jonathan Corbet, Shuah Khan, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Thomas Bogendoerfer, Michael Grzeschik, Andrew Lunn,
	Borislav Petkov (AMD), Andrew Morton, Eric Biggers, Li RongQing,
	Paul E. McKenney, Greg Kroah-Hartman, Mingyu Wang,
	Theodore Ts'o
In-Reply-To: <20260518010739.80979-1-enelsonmoore@gmail.com>

While ARCnet is still used in industrial environments, and cards are
still manufactured, it is unlikely anyone is still using it with ISA
and PCMCIA cards. Reduce future maintenance burden by removing all ISA
and PCMCIA ARCnet drivers and documentation related to them. Update
instructions for loading modules and passing parameters to work on
modern kernels and with the com20020_pci driver. Also take the
opportunity to document the rest of the module parameters, correct a
file path in Documentation/networking/arcnet.rst, and change a
reference to /etc/rc.inet1, which no longer exists, to refer to
ifconfig.

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 .../admin-guide/kernel-parameters.txt         |   14 -
 Documentation/networking/arcnet-hardware.rst  | 2943 +----------------
 Documentation/networking/arcnet.rst           |  166 +-
 arch/mips/configs/mtx1_defconfig              |    4 -
 drivers/net/arcnet/Kconfig                    |   52 +-
 drivers/net/arcnet/Makefile                   |    5 -
 drivers/net/arcnet/arc-rimi.c                 |  386 ---
 drivers/net/arcnet/com20020-isa.c             |  230 --
 drivers/net/arcnet/com20020.c                 |    4 +-
 drivers/net/arcnet/com20020_cs.c              |  330 --
 drivers/net/arcnet/com90io.c                  |  427 ---
 drivers/net/arcnet/com90xx.c                  |  716 ----
 12 files changed, 37 insertions(+), 5240 deletions(-)
 delete mode 100644 drivers/net/arcnet/arc-rimi.c
 delete mode 100644 drivers/net/arcnet/com20020-isa.c
 delete mode 100644 drivers/net/arcnet/com20020_cs.c
 delete mode 100644 drivers/net/arcnet/com90io.c
 delete mode 100644 drivers/net/arcnet/com90xx.c

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 7834ee927310..063c11ca33e5 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -558,9 +558,6 @@ Kernel parameters
 			1 -- enable.
 			Default value is set via kernel config option.
 
-	arcrimi=	[HW,NET] ARCnet - "RIM I" (entirely mem-mapped) cards
-			Format: <io>,<irq>,<nodeID>
-
 	arm64.no32bit_el0 [ARM64] Unconditionally disable the execution of
 			32 bit applications.
 
@@ -911,17 +908,6 @@ Kernel parameters
 			Sets the size of memory pool for coherent, atomic dma
 			allocations, by default set to 256K.
 
-	com20020=	[HW,NET] ARCnet - COM20020 chipset
-			Format:
-			<io>[,<irq>[,<nodeID>[,<backplane>[,<ckp>[,<timeout>]]]]]
-
-	com90io=	[HW,NET] ARCnet - COM90xx chipset (IO-mapped buffers)
-			Format: <io>[,<irq>]
-
-	com90xx=	[HW,NET]
-			ARCnet - COM90xx chipset (memory-mapped buffers)
-			Format: <io>[,<irq>[,<memstart>]]
-
 	condev=		[HW,S390] console device
 	conmode=
 
diff --git a/Documentation/networking/arcnet-hardware.rst b/Documentation/networking/arcnet-hardware.rst
index 20e5075d0d0e..17450e8e6ca7 100644
--- a/Documentation/networking/arcnet-hardware.rst
+++ b/Documentation/networking/arcnet-hardware.rst
@@ -14,10 +14,9 @@ ARCnet Hardware
       of the kernel sources.  Ideas?
 
 Because so many people (myself included) seem to have obtained ARCnet cards
-without manuals, this file contains a quick introduction to ARCnet hardware,
-some cabling tips, and a listing of all jumper settings I can find. If you
-have any settings for your particular card, and/or any other information you
-have, do not hesitate to :ref:`email to netdev <arcnet-netdev>`.
+without manuals, this file contains a quick introduction to ARCnet hardware
+and some cabling tips. If you have any other information, do not hesitate to
+:ref:`send an email to netdev <arcnet-netdev>`.
 
 
 Introduction to ARCnet
@@ -264,95 +263,13 @@ of a TP cable between two cards/hubs is 650 meters.
 Setting the Jumpers
 ===================
 
-All ARCnet cards should have a total of four or five different settings:
-
-  - the I/O address:  this is the "port" your ARCnet card is on.  Probed
-    values in the Linux ARCnet driver are only from 0x200 through 0x3F0. (If
-    your card has additional ones, which is possible, please tell me.) This
-    should not be the same as any other device on your system.  According to
-    a doc I got from Novell, MS Windows prefers values of 0x300 or more,
-    eating net connections on my system (at least) otherwise.  My guess is
-    this may be because, if your card is at 0x2E0, probing for a serial port
-    at 0x2E8 will reset the card and probably mess things up royally.
-
-	- Avery's favourite: 0x300.
-
-  - the IRQ: on  8-bit cards, it might be 2 (9), 3, 4, 5, or 7.
-	     on 16-bit cards, it might be 2 (9), 3, 4, 5, 7, or 10-15.
-
-    Make sure this is different from any other card on your system.  Note
-    that IRQ2 is the same as IRQ9, as far as Linux is concerned.  You can
-    "cat /proc/interrupts" for a somewhat complete list of which ones are in
-    use at any given time.  Here is a list of common usages from Vojtech
-    Pavlik <vojtech@suse.cz>:
-
-	("Not on bus" means there is no way for a card to generate this
-	interrupt)
-
-	======   =========================================================
-	IRQ  0   Timer 0 (Not on bus)
-	IRQ  1   Keyboard (Not on bus)
-	IRQ  2   IRQ Controller 2 (Not on bus, nor does interrupt the CPU)
-	IRQ  3   COM2
-	IRQ  4   COM1
-	IRQ  5   FREE (LPT2 if you have it; sometimes COM3; maybe PLIP)
-	IRQ  6   Floppy disk controller
-	IRQ  7   FREE (LPT1 if you don't use the polling driver; PLIP)
-	IRQ  8   Realtime Clock Interrupt (Not on bus)
-	IRQ  9   FREE (VGA vertical sync interrupt if enabled)
-	IRQ 10   FREE
-	IRQ 11   FREE
-	IRQ 12   FREE
-	IRQ 13   Numeric Coprocessor (Not on bus)
-	IRQ 14   Fixed Disk Controller
-	IRQ 15   FREE (Fixed Disk Controller 2 if you have it)
-	======   =========================================================
-
-
-	.. note::
-
-	   IRQ 9 is used on some video cards for the "vertical retrace"
-	   interrupt.  This interrupt would have been handy for things like
-	   video games, as it occurs exactly once per screen refresh, but
-	   unfortunately IBM cancelled this feature starting with the original
-	   VGA and thus many VGA/SVGA cards do not support it.  For this
-	   reason, no modern software uses this interrupt and it can almost
-	   always be safely disabled, if your video card supports it at all.
-
-	If your card for some reason CANNOT disable this IRQ (usually there
-	is a jumper), one solution would be to clip the printed circuit
-	contact on the board: it's the fourth contact from the left on the
-	back side.  I take no responsibility if you try this.
-
-	- Avery's favourite: IRQ2 (actually IRQ9).  Watch that VGA, though.
-
-  - the memory address:  Unlike most cards, ARCnets use "shared memory" for
-    copying buffers around.  Make SURE it doesn't conflict with any other
-    used memory in your system!
-
-    ::
-
-	A0000		- VGA graphics memory (ok if you don't have VGA)
-	B0000		- Monochrome text mode
-	C0000		\  One of these is your VGA BIOS - usually C0000.
-	E0000		/
-	F0000		- System BIOS
-
-    Anything less than 0xA0000 is, well, a BAD idea since it isn't above
-    640k.
-
-	- Avery's favourite: 0xD0000
-
-  - the station address:  Every ARCnet card has its own "unique" network
-    address from 0 to 255.  Unlike Ethernet, you can set this address
-    yourself with a jumper or switch (or on some cards, with special
-    software).  Since it's only 8 bits, you can only have 254 ARCnet cards
-    on a network.  DON'T use 0 or 255, since these are reserved (although
-    neat stuff will probably happen if you DO use them).  By the way, if you
-    haven't already guessed, don't set this the same as any other ARCnet on
-    your network!
-
-	- Avery's favourite:  3 and 4.  Not that it matters.
+  - Every ARCnet card has its own "unique" network address from 0 to 255.
+    Unlike Ethernet, you can set this address yourself with a jumper or switch
+    (or on some cards, with special software).  Since it's only 8 bits, you can
+    only have 254 ARCnet cards on a network.  DON'T use 0 or 255, since these
+    are reserved (although neat stuff will probably happen if you DO use them).
+    By the way, if you haven't already guessed, don't set this the same as any
+    other ARCnet device on your network!
 
   - There may be ETS1 and ETS2 settings.  These may or may not make a
     difference on your card (many manuals call them "reserved"), but are
@@ -390,2843 +307,3 @@ Vojtech Pavlik <vojtech@suse.cz> tells me this is what they mean:
 	ON              Long flashes    Data transfer
 	ON              OFF             Never happens (maybe when wrong ID)
 	=============== =============== =====================================
-
-
-The following is all the specific information people have sent me about
-their own particular ARCnet cards.  It is officially a mess, and contains
-huge amounts of duplicated information.  I have no time to fix it.  If you
-want to, PLEASE DO!  Just send me a 'diff -u' of all your changes.
-
-The model # is listed right above specifics for that card, so you should be
-able to use your text viewer's "search" function to find the entry you want.
-If you don't KNOW what kind of card you have, try looking through the
-various diagrams to see if you can tell.
-
-If your model isn't listed and/or has different settings, PLEASE PLEASE
-tell me.  I had to figure mine out without the manual, and it WASN'T FUN!
-
-Even if your ARCnet model isn't listed, but has the same jumpers as another
-model that is, please e-mail me to say so.
-
-Cards Listed in this file (in this order, mostly):
-
-	=============== ======================= ====
-	Manufacturer	Model #			Bits
-	=============== ======================= ====
-	SMC		PC100			8
-	SMC		PC110			8
-	SMC		PC120			8
-	SMC		PC130			8
-	SMC		PC270E			8
-	SMC		PC500			16
-	SMC		PC500Longboard		16
-	SMC		PC550Longboard		16
-	SMC		PC600			16
-	SMC		PC710			8
-	SMC?		LCS-8830(-T)		8/16
-	Puredata	PDI507			8
-	CNet Tech	CN120-Series		8
-	CNet Tech	CN160-Series		16
-	Lantech?	UM9065L chipset		8
-	Acer		5210-003		8
-	Datapoint?	LAN-ARC-8		8
-	Topware		TA-ARC/10		8
-	Thomas-Conrad	500-6242-0097 REV A	8
-	Waterloo?	(C)1985 Waterloo Micro. 8
-	No Name		--			8/16
-	No Name		Taiwan R.O.C?		8
-	No Name		Model 9058		8
-	Tiara		Tiara Lancard?		8
-	=============== ======================= ====
-
-
-* SMC = Standard Microsystems Corp.
-* CNet Tech = CNet Technology, Inc.
-
-Unclassified Stuff
-==================
-
-  - Please send any other information you can find.
-
-  - And some other stuff (more info is welcome!)::
-
-     From: root@ultraworld.xs4all.nl (Timo Hilbrink)
-     To: apenwarr@foxnet.net (Avery Pennarun)
-     Date: Wed, 26 Oct 1994 02:10:32 +0000 (GMT)
-     Reply-To: timoh@xs4all.nl
-
-     [...parts deleted...]
-
-     About the jumpers: On my PC130 there is one more jumper, located near the
-     cable-connector and it's for changing to star or bus topology;
-     closed: star - open: bus
-     On the PC500 are some more jumper-pins, one block labeled with RX,PDN,TXI
-     and another with ALE,LA17,LA18,LA19 these are undocumented..
-
-     [...more parts deleted...]
-
-     --- CUT ---
-
-Standard Microsystems Corp (SMC)
-================================
-
-PC100, PC110, PC120, PC130 (8-bit cards) and PC500, PC600 (16-bit cards)
-------------------------------------------------------------------------
-
-  - mainly from Avery Pennarun <apenwarr@worldvisions.ca>.  Values depicted
-    are from Avery's setup.
-  - special thanks to Timo Hilbrink <timoh@xs4all.nl> for noting that PC120,
-    130, 500, and 600 all have the same switches as Avery's PC100.
-    PC500/600 have several extra, undocumented pins though. (?)
-  - PC110 settings were verified by Stephen A. Wood <saw@cebaf.gov>
-  - Also, the JP- and S-numbers probably don't match your card exactly.  Try
-    to find jumpers/switches with the same number of settings - it's
-    probably more reliable.
-
-::
-
-	     JP5		       [|]    :    :    :    :
-	(IRQ Setting)		      IRQ2  IRQ3 IRQ4 IRQ5 IRQ7
-			Put exactly one jumper on exactly one set of pins.
-
-
-				  1  2   3  4  5  6   7  8  9 10
-	     S1                /----------------------------------\
-	(I/O and Memory        |  1  1 * 0  0  0  0 * 1  1  0  1  |
-	 addresses)            \----------------------------------/
-				  |--|   |--------|   |--------|
-				  (a)       (b)           (m)
-
-			WARNING.  It's very important when setting these which way
-			you're holding the card, and which way you think is '1'!
-
-			If you suspect that your settings are not being made
-			correctly, try reversing the direction or inverting the
-			switch positions.
-
-			a: The first digit of the I/O address.
-				Setting		Value
-				-------		-----
-				00		0
-				01		1
-				10		2
-				11		3
-
-			b: The second digit of the I/O address.
-				Setting		Value
-				-------		-----
-				0000		0
-				0001		1
-				0010		2
-				...		...
-				1110		E
-				1111		F
-
-			The I/O address is in the form ab0.  For example, if
-			a is 0x2 and b is 0xE, the address will be 0x2E0.
-
-			DO NOT SET THIS LESS THAN 0x200!!!!!
-
-
-			m: The first digit of the memory address.
-				Setting		Value
-				-------		-----
-				0000		0
-				0001		1
-				0010		2
-				...		...
-				1110		E
-				1111		F
-
-			The memory address is in the form m0000.  For example, if
-			m is D, the address will be 0xD0000.
-
-			DO NOT SET THIS TO C0000, F0000, OR LESS THAN A0000!
-
-				  1  2  3  4  5  6  7  8
-	     S2                /--------------------------\
-	(Station Address)      |  1  1  0  0  0  0  0  0  |
-			       \--------------------------/
-
-				Setting		Value
-				-------		-----
-				00000000	00
-				10000000	01
-				01000000	02
-				...
-				01111111	FE
-				11111111	FF
-
-			Note that this is binary with the digits reversed!
-
-			DO NOT SET THIS TO 0 OR 255 (0xFF)!
-
-
-PC130E/PC270E (8-bit cards)
----------------------------
-
-  - from Juergen Seifert <seifert@htwm.de>
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the following Original SMC Manual
-
-	     "Configuration Guide for ARCNET(R)-PC130E/PC270 Network
-	     Controller Boards Pub. # 900.044A June, 1989"
-
-ARCNET is a registered trademark of the Datapoint Corporation
-SMC is a registered trademark of the Standard Microsystems Corporation
-
-The PC130E is an enhanced version of the PC130 board, is equipped with a
-standard BNC female connector for connection to RG-62/U coax cable.
-Since this board is designed both for point-to-point connection in star
-networks and for connection to bus networks, it is downwardly compatible
-with all the other standard boards designed for coax networks (that is,
-the PC120, PC110 and PC100 star topology boards and the PC220, PC210 and
-PC200 bus topology boards).
-
-The PC270E is an enhanced version of the PC260 board, is equipped with two
-modular RJ11-type jacks for connection to twisted pair wiring.
-It can be used in a star or a daisy-chained network.
-
-::
-
-	 8 7 6 5 4 3 2 1
-    ________________________________________________________________
-   |   |       S1        |                                          |
-   |   |_________________|                                          |
-   |    Offs|Base |I/O Addr                                         |
-   |     RAM Addr |                                              ___|
-   |         ___  ___                                       CR3 |___|
-   |        |   \/   |                                      CR4 |___|
-   |        |  PROM  |                                           ___|
-   |        |        |                                        N |   | 8
-   |        | SOCKET |                                        o |   | 7
-   |        |________|                                        d |   | 6
-   |                   ___________________                    e |   | 5
-   |                  |                   |                   A | S | 4
-   |       |oo| EXT2  |                   |                   d | 2 | 3
-   |       |oo| EXT1  |       SMC         |                   d |   | 2
-   |       |oo| ROM   |      90C63        |                   r |___| 1
-   |       |oo| IRQ7  |                   |               |o|  _____|
-   |       |oo| IRQ5  |                   |               |o| | J1  |
-   |       |oo| IRQ4  |                   |              STAR |_____|
-   |       |oo| IRQ3  |                   |                   | J2  |
-   |       |oo| IRQ2  |___________________|                   |_____|
-   |___                                               ______________|
-       |                                             |
-       |_____________________________________________|
-
-Legend::
-
-  SMC 90C63	ARCNET Controller / Transceiver /Logic
-  S1	1-3:	I/O Base Address Select
-	4-6:	Memory Base Address Select
-	7-8:	RAM Offset Select
-  S2	1-8:	Node ID Select
-  EXT		Extended Timeout Select
-  ROM		ROM Enable Select
-  STAR		Selected - Star Topology	(PC130E only)
-		Deselected - Bus Topology	(PC130E only)
-  CR3/CR4	Diagnostic LEDs
-  J1		BNC RG62/U Connector		(PC130E only)
-  J1		6-position Telephone Jack	(PC270E only)
-  J2		6-position Telephone Jack	(PC270E only)
-
-Setting one of the switches to Off/Open means "1", On/Closed means "0".
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in group S2 are used to set the node ID.
-These switches work in a way similar to the PC100-series cards; see that
-entry for more information.
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The first three switches in switch group S1 are used to select one
-of eight possible I/O Base addresses using the following table::
-
-
-   Switch | Hex I/O
-   1 2 3  | Address
-   -------|--------
-   0 0 0  |  260
-   0 0 1  |  290
-   0 1 0  |  2E0  (Manufacturer's default)
-   0 1 1  |  2F0
-   1 0 0  |  300
-   1 0 1  |  350
-   1 1 0  |  380
-   1 1 1  |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The memory buffer requires 2K of a 16K block of RAM. The base of this
-16K block can be located in any of eight positions.
-Switches 4-6 of switch group S1 select the Base of the 16K block.
-Within that 16K address space, the buffer may be assigned any one of four
-positions, determined by the offset, switches 7 and 8 of group S1.
-
-::
-
-   Switch     | Hex RAM | Hex ROM
-   4 5 6  7 8 | Address | Address *)
-   -----------|---------|-----------
-   0 0 0  0 0 |  C0000  |  C2000
-   0 0 0  0 1 |  C0800  |  C2000
-   0 0 0  1 0 |  C1000  |  C2000
-   0 0 0  1 1 |  C1800  |  C2000
-	      |         |
-   0 0 1  0 0 |  C4000  |  C6000
-   0 0 1  0 1 |  C4800  |  C6000
-   0 0 1  1 0 |  C5000  |  C6000
-   0 0 1  1 1 |  C5800  |  C6000
-	      |         |
-   0 1 0  0 0 |  CC000  |  CE000
-   0 1 0  0 1 |  CC800  |  CE000
-   0 1 0  1 0 |  CD000  |  CE000
-   0 1 0  1 1 |  CD800  |  CE000
-	      |         |
-   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
-   0 1 1  0 1 |  D0800  |  D2000
-   0 1 1  1 0 |  D1000  |  D2000
-   0 1 1  1 1 |  D1800  |  D2000
-	      |         |
-   1 0 0  0 0 |  D4000  |  D6000
-   1 0 0  0 1 |  D4800  |  D6000
-   1 0 0  1 0 |  D5000  |  D6000
-   1 0 0  1 1 |  D5800  |  D6000
-	      |         |
-   1 0 1  0 0 |  D8000  |  DA000
-   1 0 1  0 1 |  D8800  |  DA000
-   1 0 1  1 0 |  D9000  |  DA000
-   1 0 1  1 1 |  D9800  |  DA000
-	      |         |
-   1 1 0  0 0 |  DC000  |  DE000
-   1 1 0  0 1 |  DC800  |  DE000
-   1 1 0  1 0 |  DD000  |  DE000
-   1 1 0  1 1 |  DD800  |  DE000
-	      |         |
-   1 1 1  0 0 |  E0000  |  E2000
-   1 1 1  0 1 |  E0800  |  E2000
-   1 1 1  1 0 |  E1000  |  E2000
-   1 1 1  1 1 |  E1800  |  E2000
-
-  *) To enable the 8K Boot PROM install the jumper ROM.
-     The default is jumper ROM not installed.
-
-
-Setting the Timeouts and Interrupt
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The jumpers labeled EXT1 and EXT2 are used to determine the timeout
-parameters. These two jumpers are normally left open.
-
-To select a hardware interrupt level set one (only one!) of the jumpers
-IRQ2, IRQ3, IRQ4, IRQ5, IRQ7. The Manufacturer's default is IRQ2.
-
-
-Configuring the PC130E for Star or Bus Topology
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The single jumper labeled STAR is used to configure the PC130E board for
-star or bus topology.
-When the jumper is installed, the board may be used in a star network, when
-it is removed, the board can be used in a bus topology.
-
-
-Diagnostic LEDs
-^^^^^^^^^^^^^^^
-
-Two diagnostic LEDs are visible on the rear bracket of the board.
-The green LED monitors the network activity: the red one shows the
-board activity::
-
- Green  | Status               Red      | Status
- -------|-------------------   ---------|-------------------
-  on    | normal activity      flash/on | data transfer
-  blink | reconfiguration      off      | no data transfer;
-  off   | defective board or            | incorrect memory or
-	| node ID is zero               | I/O address
-
-
-PC500/PC550 Longboard (16-bit cards)
-------------------------------------
-
-  - from Juergen Seifert <seifert@htwm.de>
-
-
-  .. note::
-
-      There is another Version of the PC500 called Short Version, which
-      is different in hard- and software! The most important differences
-      are:
-
-      - The long board has no Shared memory.
-      - On the long board the selection of the interrupt is done by binary
-	coded switch, on the short board directly by jumper.
-
-[Avery's note: pay special attention to that: the long board HAS NO SHARED
-MEMORY.  This means the current Linux-ARCnet driver can't use these cards.
-I have obtained a PC500Longboard and will be doing some experiments on it in
-the future, but don't hold your breath.  Thanks again to Juergen Seifert for
-his advice about this!]
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the following Original SMC Manual
-
-	 "Configuration Guide for SMC ARCNET-PC500/PC550
-	 Series Network Controller Boards Pub. # 900.033 Rev. A
-	 November, 1989"
-
-ARCNET is a registered trademark of the Datapoint Corporation
-SMC is a registered trademark of the Standard Microsystems Corporation
-
-The PC500 is equipped with a standard BNC female connector for connection
-to RG-62/U coax cable.
-The board is designed both for point-to-point connection in star networks
-and for connection to bus networks.
-
-The PC550 is equipped with two modular RJ11-type jacks for connection
-to twisted pair wiring.
-It can be used in a star or a daisy-chained (BUS) network.
-
-::
-
-       1
-       0 9 8 7 6 5 4 3 2 1     6 5 4 3 2 1
-    ____________________________________________________________________
-   < |         SW1         | |     SW2     |                            |
-   > |_____________________| |_____________|                            |
-   <   IRQ    |I/O Addr                                                 |
-   >                                                                 ___|
-   <                                                            CR4 |___|
-   >                                                            CR3 |___|
-   <                                                                 ___|
-   >                                                              N |   | 8
-   <                                                              o |   | 7
-   >                                                              d | S | 6
-   <                                                              e | W | 5
-   >                                                              A | 3 | 4
-   <                                                              d |   | 3
-   >                                                              d |   | 2
-   <                                                              r |___| 1
-   >                                                        |o|    _____|
-   <                                                        |o|   | J1  |
-   >  3 1                                                   JP6   |_____|
-   < |o|o| JP2                                                    | J2  |
-   > |o|o|                                                        |_____|
-   <  4 2__                                               ______________|
-   >    |  |                                             |
-   <____|  |_____________________________________________|
-
-Legend::
-
-  SW1	1-6:	I/O Base Address Select
-	7-10:	Interrupt Select
-  SW2	1-6:	Reserved for Future Use
-  SW3	1-8:	Node ID Select
-  JP2	1-4:	Extended Timeout Select
-  JP6		Selected - Star Topology	(PC500 only)
-		Deselected - Bus Topology	(PC500 only)
-  CR3	Green	Monitors Network Activity
-  CR4	Red	Monitors Board Activity
-  J1		BNC RG62/U Connector		(PC500 only)
-  J1		6-position Telephone Jack	(PC550 only)
-  J2		6-position Telephone Jack	(PC550 only)
-
-Setting one of the switches to Off/Open means "1", On/Closed means "0".
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in group SW3 are used to set the node ID. Each node
-attached to the network must have an unique node ID which must be
-different from 0.
-Switch 1 serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"
-These values are::
-
-    Switch | Value
-    -------|-------
-      1    |   1
-      2    |   2
-      3    |   4
-      4    |   8
-      5    |  16
-      6    |  32
-      7    |  64
-      8    | 128
-
-Some Examples::
-
-    Switch         | Hex     | Decimal
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The first six switches in switch group SW1 are used to select one
-of 32 possible I/O Base addresses using the following table::
-
-   Switch       | Hex I/O
-   6 5  4 3 2 1 | Address
-   -------------|--------
-   0 1  0 0 0 0 |  200
-   0 1  0 0 0 1 |  210
-   0 1  0 0 1 0 |  220
-   0 1  0 0 1 1 |  230
-   0 1  0 1 0 0 |  240
-   0 1  0 1 0 1 |  250
-   0 1  0 1 1 0 |  260
-   0 1  0 1 1 1 |  270
-   0 1  1 0 0 0 |  280
-   0 1  1 0 0 1 |  290
-   0 1  1 0 1 0 |  2A0
-   0 1  1 0 1 1 |  2B0
-   0 1  1 1 0 0 |  2C0
-   0 1  1 1 0 1 |  2D0
-   0 1  1 1 1 0 |  2E0 (Manufacturer's default)
-   0 1  1 1 1 1 |  2F0
-   1 1  0 0 0 0 |  300
-   1 1  0 0 0 1 |  310
-   1 1  0 0 1 0 |  320
-   1 1  0 0 1 1 |  330
-   1 1  0 1 0 0 |  340
-   1 1  0 1 0 1 |  350
-   1 1  0 1 1 0 |  360
-   1 1  0 1 1 1 |  370
-   1 1  1 0 0 0 |  380
-   1 1  1 0 0 1 |  390
-   1 1  1 0 1 0 |  3A0
-   1 1  1 0 1 1 |  3B0
-   1 1  1 1 0 0 |  3C0
-   1 1  1 1 0 1 |  3D0
-   1 1  1 1 1 0 |  3E0
-   1 1  1 1 1 1 |  3F0
-
-
-Setting the Interrupt
-^^^^^^^^^^^^^^^^^^^^^
-
-Switches seven through ten of switch group SW1 are used to select the
-interrupt level. The interrupt level is binary coded, so selections
-from 0 to 15 would be possible, but only the following eight values will
-be supported: 3, 4, 5, 7, 9, 10, 11, 12.
-
-::
-
-   Switch   | IRQ
-   10 9 8 7 |
-   ---------|--------
-    0 0 1 1 |  3
-    0 1 0 0 |  4
-    0 1 0 1 |  5
-    0 1 1 1 |  7
-    1 0 0 1 |  9 (=2) (default)
-    1 0 1 0 | 10
-    1 0 1 1 | 11
-    1 1 0 0 | 12
-
-
-Setting the Timeouts
-^^^^^^^^^^^^^^^^^^^^
-
-The two jumpers JP2 (1-4) are used to determine the timeout parameters.
-These two jumpers are normally left open.
-Refer to the COM9026 Data Sheet for alternate configurations.
-
-
-Configuring the PC500 for Star or Bus Topology
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The single jumper labeled JP6 is used to configure the PC500 board for
-star or bus topology.
-When the jumper is installed, the board may be used in a star network, when
-it is removed, the board can be used in a bus topology.
-
-
-Diagnostic LEDs
-^^^^^^^^^^^^^^^
-
-Two diagnostic LEDs are visible on the rear bracket of the board.
-The green LED monitors the network activity: the red one shows the
-board activity::
-
- Green  | Status               Red      | Status
- -------|-------------------   ---------|-------------------
-  on    | normal activity      flash/on | data transfer
-  blink | reconfiguration      off      | no data transfer;
-  off   | defective board or            | incorrect memory or
-	| node ID is zero               | I/O address
-
-
-PC710 (8-bit card)
-------------------
-
-  - from J.S. van Oosten <jvoosten@compiler.tdcnet.nl>
-
-Note: this data is gathered by experimenting and looking at info of other
-cards. However, I'm sure I got 99% of the settings right.
-
-The SMC710 card resembles the PC270 card, but is much more basic (i.e. no
-LEDs, RJ11 jacks, etc.) and 8 bit. Here's a little drawing::
-
-    _______________________________________
-   | +---------+  +---------+              |____
-   | |   S2    |  |   S1    |              |
-   | +---------+  +---------+              |
-   |                                       |
-   |  +===+    __                          |
-   |  | R |   |  | X-tal                 ###___
-   |  | O |   |__|                      ####__'|
-   |  | M |    ||                        ###
-   |  +===+                                |
-   |                                       |
-   |   .. JP1   +----------+               |
-   |   ..       | big chip |               |
-   |   ..       |  90C63   |               |
-   |   ..       |          |               |
-   |   ..       +----------+               |
-    -------                     -----------
-	   |||||||||||||||||||||
-
-The row of jumpers at JP1 actually consists of 8 jumpers, (sometimes
-labelled) the same as on the PC270, from top to bottom: EXT2, EXT1, ROM,
-IRQ7, IRQ5, IRQ4, IRQ3, IRQ2 (gee, wonder what they would do? :-) )
-
-S1 and S2 perform the same function as on the PC270, only their numbers
-are swapped (S1 is the nodeaddress, S2 sets IO- and RAM-address).
-
-I know it works when connected to a PC110 type ARCnet board.
-
-
-*****************************************************************************
-
-Possibly SMC
-============
-
-LCS-8830(-T) (8 and 16-bit cards)
----------------------------------
-
-  - from Mathias Katzer <mkatzer@HRZ.Uni-Bielefeld.DE>
-  - Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl> says the
-    LCS-8830 is slightly different from LCS-8830-T.  These are 8 bit, BUS
-    only (the JP0 jumper is hardwired), and BNC only.
-
-This is a LCS-8830-T made by SMC, I think ('SMC' only appears on one PLCC,
-nowhere else, not even on the few Xeroxed sheets from the manual).
-
-SMC ARCnet Board Type LCS-8830-T::
-
-     ------------------------------------
-    |                                    |
-    |              JP3 88  8 JP2         |
-    |       #####      | \               |
-    |       #####    ET1 ET2          ###|
-    |                              8  ###|
-    |  U3   SW 1                  JP0 ###|  Phone Jacks
-    |  --                             ###|
-    | |  |                               |
-    | |  |   SW2                         |
-    | |  |                               |
-    | |  |  #####                        |
-    |  --   #####                       ####  BNC Connector
-    |                                   ####
-    |   888888 JP1                       |
-    |   234567                           |
-     --                           -------
-       |||||||||||||||||||||||||||
-	--------------------------
-
-
-  SW1: DIP-Switches for Station Address
-  SW2: DIP-Switches for Memory Base and I/O Base addresses
-
-  JP0: If closed, internal termination on (default open)
-  JP1: IRQ Jumpers
-  JP2: Boot-ROM enabled if closed
-  JP3: Jumpers for response timeout
-
-  U3: Boot-ROM Socket
-
-
-  ET1 ET2     Response Time     Idle Time    Reconfiguration Time
-
-		 78                86               840
-   X            285               316              1680
-       X        563               624              1680
-   X   X       1130              1237              1680
-
-  (X means closed jumper)
-
-  (DIP-Switch downwards means "0")
-
-The station address is binary-coded with SW1.
-
-The I/O base address is coded with DIP-Switches 6,7 and 8 of SW2:
-
-========	========
-Switches        Base
-678             Address
-========	========
-000		260-26f
-100		290-29f
-010		2e0-2ef
-110		2f0-2ff
-001		300-30f
-101		350-35f
-011		380-38f
-111 		3e0-3ef
-========	========
-
-
-DIP Switches 1-5 of SW2 encode the RAM and ROM Address Range:
-
-========        ============= ================
-Switches        RAM           ROM
-12345           Address Range  Address Range
-========        ============= ================
-00000		C:0000-C:07ff	C:2000-C:3fff
-10000		C:0800-C:0fff
-01000		C:1000-C:17ff
-11000		C:1800-C:1fff
-00100		C:4000-C:47ff	C:6000-C:7fff
-10100		C:4800-C:4fff
-01100		C:5000-C:57ff
-11100		C:5800-C:5fff
-00010		C:C000-C:C7ff	C:E000-C:ffff
-10010		C:C800-C:Cfff
-01010		C:D000-C:D7ff
-11010		C:D800-C:Dfff
-00110		D:0000-D:07ff	D:2000-D:3fff
-10110		D:0800-D:0fff
-01110		D:1000-D:17ff
-11110		D:1800-D:1fff
-00001		D:4000-D:47ff	D:6000-D:7fff
-10001		D:4800-D:4fff
-01001		D:5000-D:57ff
-11001		D:5800-D:5fff
-00101		D:8000-D:87ff	D:A000-D:bfff
-10101		D:8800-D:8fff
-01101		D:9000-D:97ff
-11101		D:9800-D:9fff
-00011		D:C000-D:c7ff	D:E000-D:ffff
-10011		D:C800-D:cfff
-01011		D:D000-D:d7ff
-11011		D:D800-D:dfff
-00111		E:0000-E:07ff	E:2000-E:3fff
-10111		E:0800-E:0fff
-01111		E:1000-E:17ff
-11111		E:1800-E:1fff
-========        ============= ================
-
-
-PureData Corp
-=============
-
-PDI507 (8-bit card)
---------------------
-
-  - from Mark Rejhon <mdrejhon@magi.com> (slight modifications by Avery)
-  - Avery's note: I think PDI508 cards (but definitely NOT PDI508Plus cards)
-    are mostly the same as this.  PDI508Plus cards appear to be mainly
-    software-configured.
-
-Jumpers:
-
-	There is a jumper array at the bottom of the card, near the edge
-	connector.  This array is labelled J1.  They control the IRQs and
-	something else.  Put only one jumper on the IRQ pins.
-
-	ETS1, ETS2 are for timing on very long distance networks.  See the
-	more general information near the top of this file.
-
-	There is a J2 jumper on two pins.  A jumper should be put on them,
-	since it was already there when I got the card.  I don't know what
-	this jumper is for though.
-
-	There is a two-jumper array for J3.  I don't know what it is for,
-	but there were already two jumpers on it when I got the card.  It's
-	a six pin grid in a two-by-three fashion.  The jumpers were
-	configured as follows::
-
-	   .-------.
-	 o | o   o |
-	   :-------:    ------> Accessible end of card with connectors
-	 o | o   o |             in this direction ------->
-	   `-------'
-
-Carl de Billy <CARL@carainfo.com> explains J3 and J4:
-
-   J3 Diagram::
-
-	   .-------.
-	 o | o   o |
-	   :-------:    TWIST Technology
-	 o | o   o |
-	   `-------'
-	   .-------.
-	   | o   o | o
-	   :-------:    COAX Technology
-	   | o   o | o
-	   `-------'
-
-  - If using coax cable in a bus topology the J4 jumper must be removed;
-    place it on one pin.
-
-  - If using bus topology with twisted pair wiring move the J3
-    jumpers so they connect the middle pin and the pins closest to the RJ11
-    Connectors.  Also the J4 jumper must be removed; place it on one pin of
-    J4 jumper for storage.
-
-  - If using  star topology with twisted pair wiring move the J3
-    jumpers so they connect the middle pin and the pins closest to the RJ11
-    connectors.
-
-
-DIP Switches:
-
-	The DIP switches accessible on the accessible end of the card while
-	it is installed, is used to set the ARCnet address.  There are 8
-	switches.  Use an address from 1 to 254
-
-	==========      =========================
-	Switch No.	ARCnet address
-	12345678
-	==========      =========================
-	00000000	FF  	(Don't use this!)
-	00000001	FE
-	00000010	FD
-	...
-	11111101	2
-	11111110	1
-	11111111	0	(Don't use this!)
-	==========      =========================
-
-	There is another array of eight DIP switches at the top of the
-	card.  There are five labelled MS0-MS4 which seem to control the
-	memory address, and another three labelled IO0-IO2 which seem to
-	control the base I/O address of the card.
-
-	This was difficult to test by trial and error, and the I/O addresses
-	are in a weird order.  This was tested by setting the DIP switches,
-	rebooting the computer, and attempting to load ARCETHER at various
-	addresses (mostly between 0x200 and 0x400).  The address that caused
-	the red transmit LED to blink, is the one that I thought works.
-
-	Also, the address 0x3D0 seem to have a special meaning, since the
-	ARCETHER packet driver loaded fine, but without the red LED
-	blinking.  I don't know what 0x3D0 is for though.  I recommend using
-	an address of 0x300 since Windows may not like addresses below
-	0x300.
-
-	=============   ===========
-	IO Switch No.   I/O address
-	210
-	=============   ===========
-	111             0x260
-	110             0x290
-	101             0x2E0
-	100             0x2F0
-	011             0x300
-	010             0x350
-	001             0x380
-	000             0x3E0
-	=============   ===========
-
-	The memory switches set a reserved address space of 0x1000 bytes
-	(0x100 segment units, or 4k).  For example if I set an address of
-	0xD000, it will use up addresses 0xD000 to 0xD100.
-
-	The memory switches were tested by booting using QEMM386 stealth,
-	and using LOADHI to see what address automatically became excluded
-	from the upper memory regions, and then attempting to load ARCETHER
-	using these addresses.
-
-	I recommend using an ARCnet memory address of 0xD000, and putting
-	the EMS page frame at 0xC000 while using QEMM stealth mode.  That
-	way, you get contiguous high memory from 0xD100 almost all the way
-	the end of the megabyte.
-
-	Memory Switch 0 (MS0) didn't seem to work properly when set to OFF
-	on my card.  It could be malfunctioning on my card.  Experiment with
-	it ON first, and if it doesn't work, set it to OFF.  (It may be a
-	modifier for the 0x200 bit?)
-
-	=============   ============================================
-	MS Switch No.
-	43210           Memory address
-	=============   ============================================
-	00001           0xE100  (guessed - was not detected by QEMM)
-	00011           0xE000  (guessed - was not detected by QEMM)
-	00101           0xDD00
-	00111           0xDC00
-	01001           0xD900
-	01011           0xD800
-	01101           0xD500
-	01111           0xD400
-	10001           0xD100
-	10011           0xD000
-	10101           0xCD00
-	10111           0xCC00
-	11001           0xC900 (guessed - crashes tested system)
-	11011           0xC800 (guessed - crashes tested system)
-	11101           0xC500 (guessed - crashes tested system)
-	11111           0xC400 (guessed - crashes tested system)
-	=============   ============================================
-
-CNet Technology Inc. (8-bit cards)
-==================================
-
-120 Series (8-bit cards)
-------------------------
-  - from Juergen Seifert <seifert@htwm.de>
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the following Original CNet Manual
-
-	      "ARCNET USER'S MANUAL for
-	      CN120A
-	      CN120AB
-	      CN120TP
-	      CN120ST
-	      CN120SBT
-	      P/N:12-01-0007
-	      Revision 3.00"
-
-ARCNET is a registered trademark of the Datapoint Corporation
-
-- P/N 120A   ARCNET 8 bit XT/AT Star
-- P/N 120AB  ARCNET 8 bit XT/AT Bus
-- P/N 120TP  ARCNET 8 bit XT/AT Twisted Pair
-- P/N 120ST  ARCNET 8 bit XT/AT Star, Twisted Pair
-- P/N 120SBT ARCNET 8 bit XT/AT Star, Bus, Twisted Pair
-
-::
-
-    __________________________________________________________________
-   |                                                                  |
-   |                                                               ___|
-   |                                                          LED |___|
-   |                                                               ___|
-   |                                                            N |   | ID7
-   |                                                            o |   | ID6
-   |                                                            d | S | ID5
-   |                                                            e | W | ID4
-   |                     ___________________                    A | 2 | ID3
-   |                    |                   |                   d |   | ID2
-   |                    |                   |  1 2 3 4 5 6 7 8  d |   | ID1
-   |                    |                   | _________________ r |___| ID0
-   |                    |      90C65        ||       SW1       |  ____|
-   |  JP 8 7            |                   ||_________________| |    |
-   |    |o|o|  JP1      |                   |                    | J2 |
-   |    |o|o|  |oo|     |                   |         JP 1 1 1   |    |
-   |   ______________   |                   |            0 1 2   |____|
-   |  |  PROM        |  |___________________|           |o|o|o|  _____|
-   |  >  SOCKET      |  JP 6 5 4 3 2                    |o|o|o| | J1  |
-   |  |______________|    |o|o|o|o|o|                   |o|o|o| |_____|
-   |_____                 |o|o|o|o|o|                   ______________|
-	 |                                             |
-	 |_____________________________________________|
-
-Legend::
-
-  90C65       ARCNET Probe
-  S1  1-5:    Base Memory Address Select
-      6-8:    Base I/O Address Select
-  S2  1-8:    Node ID Select (ID0-ID7)
-  JP1     ROM Enable Select
-  JP2     IRQ2
-  JP3     IRQ3
-  JP4     IRQ4
-  JP5     IRQ5
-  JP6     IRQ7
-  JP7/JP8     ET1, ET2 Timeout Parameters
-  JP10/JP11   Coax / Twisted Pair Select  (CN120ST/SBT only)
-  JP12        Terminator Select       (CN120AB/ST/SBT only)
-  J1      BNC RG62/U Connector        (all except CN120TP)
-  J2      Two 6-position Telephone Jack   (CN120TP/ST/SBT only)
-
-Setting one of the switches to Off means "1", On means "0".
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in SW2 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must be different from 0.
-Switch 1 (ID0) serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"
-These values are:
-
-   =======  ======  =====
-   Switch   Label   Value
-   =======  ======  =====
-     1      ID0       1
-     2      ID1       2
-     3      ID2       4
-     4      ID3       8
-     5      ID4      16
-     6      ID5      32
-     7      ID6      64
-     8      ID7     128
-   =======  ======  =====
-
-Some Examples::
-
-    Switch         | Hex     | Decimal
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The last three switches in switch block SW1 are used to select one
-of eight possible I/O Base addresses using the following table::
-
-
-   Switch      | Hex I/O
-    6   7   8  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   OFF ON  ON  |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   OFF OFF ON  |  2F0
-   ON  ON  OFF |  300
-   OFF ON  OFF |  350
-   ON  OFF OFF |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be
-located in any of eight positions. The address of the Boot Prom is
-memory base + 8K or memory base + 0x2000.
-Switches 1-5 of switch block SW1 select the Memory Base address.
-
-::
-
-   Switch              | Hex RAM | Hex ROM
-    1   2   3   4   5  | Address | Address *)
-   --------------------|---------|-----------
-   ON  ON  ON  ON  ON  |  C0000  |  C2000
-   ON  ON  OFF ON  ON  |  C4000  |  C6000
-   ON  ON  ON  OFF ON  |  CC000  |  CE000
-   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturer's default)
-   ON  ON  ON  ON  OFF |  D4000  |  D6000
-   ON  ON  OFF ON  OFF |  D8000  |  DA000
-   ON  ON  ON  OFF OFF |  DC000  |  DE000
-   ON  ON  OFF OFF OFF |  E0000  |  E2000
-
-  *) To enable the Boot ROM install the jumper JP1
-
-.. note::
-
-      Since the switches 1 and 2 are always set to ON it may be possible
-      that they can be used to add an offset of 2K, 4K or 6K to the base
-      address, but this feature is not documented in the manual and I
-      haven't tested it yet.
-
-
-Setting the Interrupt Line
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-To select a hardware interrupt level install one (only one!) of the jumpers
-JP2, JP3, JP4, JP5, JP6. JP2 is the default::
-
-   Jumper | IRQ
-   -------|-----
-     2    |  2
-     3    |  3
-     4    |  4
-     5    |  5
-     6    |  7
-
-
-Setting the Internal Terminator on CN120AB/TP/SBT
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The jumper JP12 is used to enable the internal terminator::
-
-			 -----
-       0                |  0  |
-     -----   ON         |     |  ON
-    |  0  |             |  0  |
-    |     |  OFF         -----   OFF
-    |  0  |                0
-     -----
-   Terminator          Terminator
-    disabled            enabled
-
-
-Selecting the Connector Type on CN120ST/SBT
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-::
-
-     JP10    JP11        JP10    JP11
-			 -----   -----
-       0       0        |  0  | |  0  |
-     -----   -----      |     | |     |
-    |  0  | |  0  |     |  0  | |  0  |
-    |     | |     |      -----   -----
-    |  0  | |  0  |        0       0
-     -----   -----
-     Coaxial Cable       Twisted Pair Cable
-       (Default)
-
-
-Setting the Timeout Parameters
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The jumpers labeled EXT1 and EXT2 are used to determine the timeout
-parameters. These two jumpers are normally left open.
-
-
-CNet Technology Inc. (16-bit cards)
-===================================
-
-160 Series (16-bit cards)
--------------------------
-  - from Juergen Seifert <seifert@htwm.de>
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the following Original CNet Manual
-
-	      "ARCNET USER'S MANUAL for
-	      CN160A CN160AB CN160TP
-	      P/N:12-01-0006 Revision 3.00"
-
-ARCNET is a registered trademark of the Datapoint Corporation
-
-- P/N 160A   ARCNET 16 bit XT/AT Star
-- P/N 160AB  ARCNET 16 bit XT/AT Bus
-- P/N 160TP  ARCNET 16 bit XT/AT Twisted Pair
-
-::
-
-   ___________________________________________________________________
-  <                             _________________________          ___|
-  >               |oo| JP2     |                         |    LED |___|
-  <               |oo| JP1     |        9026             |    LED |___|
-  >                            |_________________________|         ___|
-  <                                                             N |   | ID7
-  >                                                      1      o |   | ID6
-  <                                    1 2 3 4 5 6 7 8 9 0      d | S | ID5
-  >         _______________           _____________________     e | W | ID4
-  <        |     PROM      |         |         SW1         |    A | 2 | ID3
-  >        >    SOCKET     |         |_____________________|    d |   | ID2
-  <        |_______________|          | IO-Base   | MEM   |     d |   | ID1
-  >                                                             r |___| ID0
-  <                                                               ____|
-  >                                                              |    |
-  <                                                              | J1 |
-  >                                                              |    |
-  <                                                              |____|
-  >                            1 1 1 1                                |
-  <  3 4 5 6 7      JP     8 9 0 1 2 3                                |
-  > |o|o|o|o|o|           |o|o|o|o|o|o|                               |
-  < |o|o|o|o|o| __        |o|o|o|o|o|o|                    ___________|
-  >            |  |                                       |
-  <____________|  |_______________________________________|
-
-Legend::
-
-  9026            ARCNET Probe
-  SW1 1-6:    Base I/O Address Select
-      7-10:   Base Memory Address Select
-  SW2 1-8:    Node ID Select (ID0-ID7)
-  JP1/JP2     ET1, ET2 Timeout Parameters
-  JP3-JP13    Interrupt Select
-  J1      BNC RG62/U Connector        (CN160A/AB only)
-  J1      Two 6-position Telephone Jack   (CN160TP only)
-  LED
-
-Setting one of the switches to Off means "1", On means "0".
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in SW2 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must be different from 0.
-Switch 1 (ID0) serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"
-These values are::
-
-   Switch | Label | Value
-   -------|-------|-------
-     1    | ID0   |   1
-     2    | ID1   |   2
-     3    | ID2   |   4
-     4    | ID3   |   8
-     5    | ID4   |  16
-     6    | ID5   |  32
-     7    | ID6   |  64
-     8    | ID7   | 128
-
-Some Examples::
-
-    Switch         | Hex     | Decimal
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The first six switches in switch block SW1 are used to select the I/O Base
-address using the following table::
-
-	     Switch        | Hex I/O
-    1   2   3   4   5   6  | Address
-   ------------------------|--------
-   OFF ON  ON  OFF OFF ON  |  260
-   OFF ON  OFF ON  ON  OFF |  290
-   OFF ON  OFF OFF OFF ON  |  2E0  (Manufacturer's default)
-   OFF ON  OFF OFF OFF OFF |  2F0
-   OFF OFF ON  ON  ON  ON  |  300
-   OFF OFF ON  OFF ON  OFF |  350
-   OFF OFF OFF ON  ON  ON  |  380
-   OFF OFF OFF OFF OFF ON  |  3E0
-
-Note: Other IO-Base addresses seem to be selectable, but only the above
-      combinations are documented.
-
-
-Setting the Base Memory (RAM) buffer Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The switches 7-10 of switch block SW1 are used to select the Memory
-Base address of the RAM (2K) and the PROM::
-
-   Switch          | Hex RAM | Hex ROM
-    7   8   9  10  | Address | Address
-   ----------------|---------|-----------
-   OFF OFF ON  ON  |  C0000  |  C8000
-   OFF OFF ON  OFF |  D0000  |  D8000 (Default)
-   OFF OFF OFF ON  |  E0000  |  E8000
-
-.. note::
-
-      Other MEM-Base addresses seem to be selectable, but only the above
-      combinations are documented.
-
-
-Setting the Interrupt Line
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-To select a hardware interrupt level install one (only one!) of the jumpers
-JP3 through JP13 using the following table::
-
-   Jumper | IRQ
-   -------|-----------------
-     3    |  14
-     4    |  15
-     5    |  12
-     6    |  11
-     7    |  10
-     8    |   3
-     9    |   4
-    10    |   5
-    11    |   6
-    12    |   7
-    13    |   2 (=9) Default!
-
-.. note::
-
-       - Do not use JP11=IRQ6, it may conflict with your Floppy Disk
-	 Controller
-       - Use JP3=IRQ14 only, if you don't have an IDE-, MFM-, or RLL-
-	 Hard Disk, it may conflict with their controllers
-
-
-Setting the Timeout Parameters
-------------------------------
-
-The jumpers labeled JP1 and JP2 are used to determine the timeout
-parameters. These two jumpers are normally left open.
-
-
-Lantech
-=======
-
-8-bit card, unknown model
--------------------------
-  - from Vlad Lungu <vlungu@ugal.ro> - his e-mail address seemed broken at
-    the time I tried to reach him.  Sorry Vlad, if you didn't get my reply.
-
-::
-
-   ________________________________________________________________
-   |   1         8                                                 |
-   |   ___________                                               __|
-   |   |   SW1    |                                         LED |__|
-   |   |__________|                                                |
-   |                                                            ___|
-   |                _____________________                       |S | 8
-   |                |                   |                       |W |
-   |                |                   |                       |2 |
-   |                |                   |                       |__| 1
-   |                |      UM9065L      |     |o|  JP4         ____|____
-   |                |                   |     |o|              |  CN    |
-   |                |                   |                      |________|
-   |                |                   |                          |
-   |                |___________________|                          |
-   |                                                               |
-   |                                                               |
-   |      _____________                                            |
-   |      |            |                                           |
-   |      |    PROM    |        |ooooo|  JP6                       |
-   |      |____________|        |ooooo|                            |
-   |_____________                                             _   _|
-		|____________________________________________| |__|
-
-
-UM9065L : ARCnet Controller
-
-SW 1    : Shared Memory Address and I/O Base
-
-::
-
-	ON=0
-
-	12345|Memory Address
-	-----|--------------
-	00001|  D4000
-	00010|  CC000
-	00110|  D0000
-	01110|  D1000
-	01101|  D9000
-	10010|  CC800
-	10011|  DC800
-	11110|  D1800
-
-It seems that the bits are considered in reverse order.  Also, you must
-observe that some of those addresses are unusual and I didn't probe them; I
-used a memory dump in DOS to identify them.  For the 00000 configuration and
-some others that I didn't write here the card seems to conflict with the
-video card (an S3 GENDAC). I leave the full decoding of those addresses to
-you.
-
-::
-
-	678| I/O Address
-	---|------------
-	000|    260
-	001|    failed probe
-	010|    2E0
-	011|    380
-	100|    290
-	101|    350
-	110|    failed probe
-	111|    3E0
-
-  SW 2  : Node ID (binary coded)
-
-  JP 4  : Boot PROM enable   CLOSE - enabled
-			     OPEN  - disabled
-
-  JP 6  : IRQ set (ONLY ONE jumper on 1-5 for IRQ 2-6)
-
-
-Acer
-====
-
-8-bit card, Model 5210-003
---------------------------
-
-  - from Vojtech Pavlik <vojtech@suse.cz> using portions of the existing
-    arcnet-hardware file.
-
-This is a 90C26 based card.  Its configuration seems similar to the SMC
-PC100, but has some additional jumpers I don't know the meaning of.
-
-::
-
-	       __
-	      |  |
-   ___________|__|_________________________
-  |         |      |                       |
-  |         | BNC  |                       |
-  |         |______|                    ___|
-  |  _____________________             |___
-  | |                     |                |
-  | | Hybrid IC           |                |
-  | |                     |       o|o J1   |
-  | |_____________________|       8|8      |
-  |                               8|8 J5   |
-  |                               o|o      |
-  |                               8|8      |
-  |__                             8|8      |
- (|__| LED                        o|o      |
-  |                               8|8      |
-  |                               8|8 J15  |
-  |                                        |
-  |                    _____               |
-  |                   |     |   _____      |
-  |                   |     |  |     |  ___|
-  |                   |     |  |     | |
-  |  _____            | ROM |  | UFS | |
-  | |     |           |     |  |     | |
-  | |     |     ___   |     |  |     | |
-  | |     |    |   |  |__.__|  |__.__| |
-  | | NCR |    |XTL|   _____    _____  |
-  | |     |    |___|  |     |  |     | |
-  | |90C26|           |     |  |     | |
-  | |     |           | RAM |  | UFS | |
-  | |     | J17 o|o   |     |  |     | |
-  | |     | J16 o|o   |     |  |     | |
-  | |__.__|           |__.__|  |__.__| |
-  |  ___                               |
-  | |   |8                             |
-  | |SW2|                              |
-  | |   |                              |
-  | |___|1                             |
-  |  ___                               |
-  | |   |10           J18 o|o          |
-  | |   |                 o|o          |
-  | |SW1|                 o|o          |
-  | |   |             J21 o|o          |
-  | |___|1                             |
-  |                                    |
-  |____________________________________|
-
-
-Legend::
-
-  90C26       ARCNET Chip
-  XTL         20 MHz Crystal
-  SW1 1-6     Base I/O Address Select
-      7-10    Memory Address Select
-  SW2 1-8     Node ID Select (ID0-ID7)
-  J1-J5       IRQ Select
-  J6-J21      Unknown (Probably extra timeouts & ROM enable ...)
-  LED1        Activity LED
-  BNC         Coax connector (STAR ARCnet)
-  RAM         2k of SRAM
-  ROM         Boot ROM socket
-  UFS         Unidentified Flying Sockets
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in SW2 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must not be 0.
-Switch 1 (ID0) serves as the least significant bit (LSB).
-
-Setting one of the switches to OFF means "1", ON means "0".
-
-The node ID is the sum of the values of all switches set to "1"
-These values are::
-
-   Switch | Value
-   -------|-------
-     1    |   1
-     2    |   2
-     3    |   4
-     4    |   8
-     5    |  16
-     6    |  32
-     7    |  64
-     8    | 128
-
-Don't set this to 0 or 255; these values are reserved.
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The switches 1 to 6 of switch block SW1 are used to select one
-of 32 possible I/O Base addresses using the following tables::
-
-	  | Hex
-   Switch | Value
-   -------|-------
-     1    | 200
-     2    | 100
-     3    |  80
-     4    |  40
-     5    |  20
-     6    |  10
-
-The I/O address is sum of all switches set to "1". Remember that
-the I/O address space below 0x200 is RESERVED for mainboard, so
-switch 1 should be ALWAYS SET TO OFF.
-
-
-Setting the Base Memory (RAM) buffer Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be
-located in any of sixteen positions. However, the addresses below
-A0000 are likely to cause system hang because there's main RAM.
-
-Jumpers 7-10 of switch block SW1 select the Memory Base address::
-
-   Switch          | Hex RAM
-    7   8   9  10  | Address
-   ----------------|---------
-   OFF OFF OFF OFF |  F0000 (conflicts with main BIOS)
-   OFF OFF OFF ON  |  E0000
-   OFF OFF ON  OFF |  D0000
-   OFF OFF ON  ON  |  C0000 (conflicts with video BIOS)
-   OFF ON  OFF OFF |  B0000 (conflicts with mono video)
-   OFF ON  OFF ON  |  A0000 (conflicts with graphics)
-
-
-Setting the Interrupt Line
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Jumpers 1-5 of the jumper block J1 control the IRQ level. ON means
-shorted, OFF means open::
-
-    Jumper              |  IRQ
-    1   2   3   4   5   |
-   ----------------------------
-    ON  OFF OFF OFF OFF |  7
-    OFF ON  OFF OFF OFF |  5
-    OFF OFF ON  OFF OFF |  4
-    OFF OFF OFF ON  OFF |  3
-    OFF OFF OFF OFF ON  |  2
-
-
-Unknown jumpers & sockets
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-I know nothing about these. I just guess that J16&J17 are timeout
-jumpers and maybe one of J18-J21 selects ROM. Also J6-J10 and
-J11-J15 are connecting IRQ2-7 to some pins on the UFSs. I can't
-guess the purpose.
-
-Datapoint?
-==========
-
-LAN-ARC-8, an 8-bit card
-------------------------
-
-  - from Vojtech Pavlik <vojtech@suse.cz>
-
-This is another SMC 90C65-based ARCnet card. I couldn't identify the
-manufacturer, but it might be DataPoint, because the card has the
-original arcNet logo in its upper right corner.
-
-::
-
-	  _______________________________________________________
-	 |                         _________                     |
-	 |                        |   SW2   | ON      arcNet     |
-	 |                        |_________| OFF             ___|
-	 |  _____________         1 ______  8                |   | 8
-	 | |             | SW1     | XTAL | ____________     | S |
-	 | > RAM (2k)    |         |______||            |    | W |
-	 | |_____________|                 |      H     |    | 3 |
-	 |                        _________|_____ y     |    |___| 1
-	 |  _________            |         |     |b     |        |
-	 | |_________|           |         |     |r     |        |
-	 |                       |     SMC |     |i     |        |
-	 |                       |    90C65|     |d     |        |
-	 |  _________            |         |     |      |        |
-	 | |   SW1   | ON        |         |     |I     |        |
-	 | |_________| OFF       |_________|_____/C     |   _____|
-	 |  1       8                      |            |  |     |___
-	 |  ______________                 |            |  | BNC |___|
-	 | |              |                |____________|  |_____|
-	 | > EPROM SOCKET |              _____________           |
-	 | |______________|             |_____________|          |
-	 |                                         ______________|
-	 |                                        |
-	 |________________________________________|
-
-Legend::
-
-  90C65       ARCNET Chip
-  SW1 1-5:    Base Memory Address Select
-      6-8:    Base I/O Address Select
-  SW2 1-8:    Node ID Select
-  SW3 1-5:    IRQ Select
-      6-7:    Extra Timeout
-      8  :    ROM Enable
-  BNC         Coax connector
-  XTAL        20 MHz Crystal
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in SW3 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must not be 0.
-Switch 1 serves as the least significant bit (LSB).
-
-Setting one of the switches to Off means "1", On means "0".
-
-The node ID is the sum of the values of all switches set to "1"
-These values are::
-
-   Switch | Value
-   -------|-------
-     1    |   1
-     2    |   2
-     3    |   4
-     4    |   8
-     5    |  16
-     6    |  32
-     7    |  64
-     8    | 128
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The last three switches in switch block SW1 are used to select one
-of eight possible I/O Base addresses using the following table::
-
-
-   Switch      | Hex I/O
-    6   7   8  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   OFF ON  ON  |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   OFF OFF ON  |  2F0
-   ON  ON  OFF |  300
-   OFF ON  OFF |  350
-   ON  OFF OFF |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be
-located in any of eight positions. The address of the Boot Prom is
-memory base + 0x2000.
-
-Jumpers 3-5 of switch block SW1 select the Memory Base address.
-
-::
-
-   Switch              | Hex RAM | Hex ROM
-    1   2   3   4   5  | Address | Address *)
-   --------------------|---------|-----------
-   ON  ON  ON  ON  ON  |  C0000  |  C2000
-   ON  ON  OFF ON  ON  |  C4000  |  C6000
-   ON  ON  ON  OFF ON  |  CC000  |  CE000
-   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturer's default)
-   ON  ON  ON  ON  OFF |  D4000  |  D6000
-   ON  ON  OFF ON  OFF |  D8000  |  DA000
-   ON  ON  ON  OFF OFF |  DC000  |  DE000
-   ON  ON  OFF OFF OFF |  E0000  |  E2000
-
-  *) To enable the Boot ROM set the switch 8 of switch block SW3 to position ON.
-
-The switches 1 and 2 probably add 0x0800 and 0x1000 to RAM base address.
-
-
-Setting the Interrupt Line
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Switches 1-5 of the switch block SW3 control the IRQ level::
-
-    Jumper              |  IRQ
-    1   2   3   4   5   |
-   ----------------------------
-    ON  OFF OFF OFF OFF |  3
-    OFF ON  OFF OFF OFF |  4
-    OFF OFF ON  OFF OFF |  5
-    OFF OFF OFF ON  OFF |  7
-    OFF OFF OFF OFF ON  |  2
-
-
-Setting the Timeout Parameters
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The switches 6-7 of the switch block SW3 are used to determine the timeout
-parameters.  These two switches are normally left in the OFF position.
-
-
-Topware
-=======
-
-8-bit card, TA-ARC/10
----------------------
-
-  - from Vojtech Pavlik <vojtech@suse.cz>
-
-This is another very similar 90C65 card. Most of the switches and jumpers
-are the same as on other clones.
-
-::
-
-   _____________________________________________________________________
-  |  ___________   |                         |            ______        |
-  | |SW2 NODE ID|  |                         |           | XTAL |       |
-  | |___________|  |  Hybrid IC              |           |______|       |
-  |  ___________   |                         |                        __|
-  | |SW1 MEM+I/O|  |_________________________|                   LED1|__|)
-  | |___________|           1 2                                         |
-  |                     J3 |o|o| TIMEOUT                          ______|
-  |     ______________     |o|o|                                 |      |
-  |    |              |  ___________________                     | RJ   |
-  |    > EPROM SOCKET | |                   \                    |------|
-  |J2  |______________| |                    |                   |      |
-  ||o|                  |                    |                   |______|
-  ||o| ROM ENABLE       |        SMC         |    _________             |
-  |     _____________   |       90C65        |   |_________|       _____|
-  |    |             |  |                    |                    |     |___
-  |    > RAM (2k)    |  |                    |                    | BNC |___|
-  |    |_____________|  |                    |                    |_____|
-  |                     |____________________|                          |
-  | ________ IRQ 2 3 4 5 7                  ___________                 |
-  ||________|   |o|o|o|o|o|                |___________|                |
-  |________   J1|o|o|o|o|o|                               ______________|
-	   |                                             |
-	   |_____________________________________________|
-
-Legend::
-
-  90C65       ARCNET Chip
-  XTAL        20 MHz Crystal
-  SW1 1-5     Base Memory Address Select
-      6-8     Base I/O Address Select
-  SW2 1-8     Node ID Select (ID0-ID7)
-  J1          IRQ Select
-  J2          ROM Enable
-  J3          Extra Timeout
-  LED1        Activity LED
-  BNC         Coax connector (BUS ARCnet)
-  RJ          Twisted Pair Connector (daisy chain)
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in SW2 are used to set the node ID. Each node attached to
-the network must have an unique node ID which must not be 0.  Switch 1 (ID0)
-serves as the least significant bit (LSB).
-
-Setting one of the switches to Off means "1", On means "0".
-
-The node ID is the sum of the values of all switches set to "1"
-These values are::
-
-   Switch | Label | Value
-   -------|-------|-------
-     1    | ID0   |   1
-     2    | ID1   |   2
-     3    | ID2   |   4
-     4    | ID3   |   8
-     5    | ID4   |  16
-     6    | ID5   |  32
-     7    | ID6   |  64
-     8    | ID7   | 128
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The last three switches in switch block SW1 are used to select one
-of eight possible I/O Base addresses using the following table::
-
-
-   Switch      | Hex I/O
-    6   7   8  | Address
-   ------------|--------
-   ON  ON  ON  |  260  (Manufacturer's default)
-   OFF ON  ON  |  290
-   ON  OFF ON  |  2E0
-   OFF OFF ON  |  2F0
-   ON  ON  OFF |  300
-   OFF ON  OFF |  350
-   ON  OFF OFF |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be
-located in any of eight positions. The address of the Boot Prom is
-memory base + 0x2000.
-
-Jumpers 3-5 of switch block SW1 select the Memory Base address.
-
-::
-
-   Switch              | Hex RAM | Hex ROM
-    1   2   3   4   5  | Address | Address *)
-   --------------------|---------|-----------
-   ON  ON  ON  ON  ON  |  C0000  |  C2000
-   ON  ON  OFF ON  ON  |  C4000  |  C6000  (Manufacturer's default)
-   ON  ON  ON  OFF ON  |  CC000  |  CE000
-   ON  ON  OFF OFF ON  |  D0000  |  D2000
-   ON  ON  ON  ON  OFF |  D4000  |  D6000
-   ON  ON  OFF ON  OFF |  D8000  |  DA000
-   ON  ON  ON  OFF OFF |  DC000  |  DE000
-   ON  ON  OFF OFF OFF |  E0000  |  E2000
-
-   *) To enable the Boot ROM short the jumper J2.
-
-The jumpers 1 and 2 probably add 0x0800 and 0x1000 to RAM address.
-
-
-Setting the Interrupt Line
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Jumpers 1-5 of the jumper block J1 control the IRQ level.  ON means
-shorted, OFF means open::
-
-    Jumper              |  IRQ
-    1   2   3   4   5   |
-   ----------------------------
-    ON  OFF OFF OFF OFF |  2
-    OFF ON  OFF OFF OFF |  3
-    OFF OFF ON  OFF OFF |  4
-    OFF OFF OFF ON  OFF |  5
-    OFF OFF OFF OFF ON  |  7
-
-
-Setting the Timeout Parameters
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The jumpers J3 are used to set the timeout parameters. These two
-jumpers are normally left open.
-
-Thomas-Conrad
-=============
-
-Model #500-6242-0097 REV A (8-bit card)
----------------------------------------
-
-  - from Lars Karlsson <100617.3473@compuserve.com>
-
-::
-
-     ________________________________________________________
-   |          ________   ________                           |_____
-   |         |........| |........|                            |
-   |         |________| |________|                         ___|
-   |            SW 3       SW 1                           |   |
-   |         Base I/O   Base Addr.                Station |   |
-   |                                              address |   |
-   |    ______                                    switch  |   |
-   |   |      |                                           |   |
-   |   |      |                                           |___|
-   |   |      |                                 ______        |___._
-   |   |______|                                |______|         ____| BNC
-   |                                            Jumper-        _____| Connector
-   |   Main chip                                block  _    __|   '
-   |                                                  | |  |    RJ Connector
-   |                                                  |_|  |    with 110 Ohm
-   |                                                       |__  Terminator
-   |    ___________                                         __|
-   |   |...........|                                       |    RJ-jack
-   |   |...........|    _____                              |    (unused)
-   |   |___________|   |_____|                             |__
-   |  Boot PROM socket IRQ-jumpers                            |_  Diagnostic
-   |________                                       __          _| LED (red)
-	    | | | | | | | | | | | | | | | | | | | |  |        |
-	    | | | | | | | | | | | | | | | | | | | |  |________|
-							      |
-							      |
-
-And here are the settings for some of the switches and jumpers on the cards.
-
-::
-
-	    I/O
-
-	   1 2 3 4 5 6 7 8
-
-  2E0----- 0 0 0 1 0 0 0 1
-  2F0----- 0 0 0 1 0 0 0 0
-  300----- 0 0 0 0 1 1 1 1
-  350----- 0 0 0 0 1 1 1 0
-
-"0" in the above example means switch is off "1" means that it is on.
-
-::
-
-      ShMem address.
-
-	1 2 3 4 5 6 7 8
-
-  CX00--0 0 1 1 | |   |
-  DX00--0 0 1 0       |
-  X000--------- 1 1   |
-  X400--------- 1 0   |
-  X800--------- 0 1   |
-  XC00--------- 0 0
-  ENHANCED----------- 1
-  COMPATIBLE--------- 0
-
-::
-
-	 IRQ
-
-
-     3 4 5 7 2
-     . . . . .
-     . . . . .
-
-
-There is a DIP-switch with 8 switches, used to set the shared memory address
-to be used. The first 6 switches set the address, the 7th doesn't have any
-function, and the 8th switch is used to select "compatible" or "enhanced".
-When I got my two cards, one of them had this switch set to "enhanced". That
-card didn't work at all, it wasn't even recognized by the driver. The other
-card had this switch set to "compatible" and it behaved absolutely normally. I
-guess that the switch on one of the cards, must have been changed accidentally
-when the card was taken out of its former host. The question remains
-unanswered, what is the purpose of the "enhanced" position?
-
-[Avery's note: "enhanced" probably either disables shared memory (use IO
-ports instead) or disables IO ports (use memory addresses instead).  This
-varies by the type of card involved.  I fail to see how either of these
-enhance anything.  Send me more detailed information about this mode, or
-just use "compatible" mode instead.]
-
-Waterloo Microsystems Inc. ??
-=============================
-
-8-bit card (C) 1985
--------------------
-  - from Robert Michael Best <rmb117@cs.usask.ca>
-
-[Avery's note: these don't work with my driver for some reason.  These cards
-SEEM to have settings similar to the PDI508Plus, which is
-software-configured and doesn't work with my driver either.  The "Waterloo
-chip" is a boot PROM, probably designed specifically for the University of
-Waterloo.  If you have any further information about this card, please
-e-mail me.]
-
-The probe has not been able to detect the card on any of the J2 settings,
-and I tried them again with the "Waterloo" chip removed.
-
-::
-
-   _____________________________________________________________________
-  | \/  \/              ___  __ __                                      |
-  | C4  C4     |^|     | M ||  ^  ||^|                                  |
-  | --  --     |_|     | 5 ||     || | C3                               |
-  | \/  \/      C10    |___||     ||_|                                  |
-  | C4  C4             _  _ |     |                 ??                  |
-  | --  --            | \/ ||     |                                     |
-  |                   |    ||     |                                     |
-  |                   |    ||  C1 |                                     |
-  |                   |    ||     |  \/                            _____|
-  |                   | C6 ||     |  C9                           |     |___
-  |                   |    ||     |  --                           | BNC |___|
-  |                   |    ||     |          >C7|                 |_____|
-  |                   |    ||     |                                     |
-  | __ __             |____||_____|       1 2 3     6                   |
-  ||  ^  |     >C4|                      |o|o|o|o|o|o| J2    >C4|       |
-  ||     |                               |o|o|o|o|o|o|                  |
-  || C2  |     >C4|                                          >C4|       |
-  ||     |                                   >C8|                       |
-  ||     |       2 3 4 5 6 7  IRQ                            >C4|       |
-  ||_____|      |o|o|o|o|o|o| J3                                        |
-  |_______      |o|o|o|o|o|o|                            _______________|
-	  |                                             |
-	  |_____________________________________________|
-
-  C1 -- "COM9026
-	 SMC 8638"
-	In a chip socket.
-
-  C2 -- "@Copyright
-	 Waterloo Microsystems Inc.
-	 1985"
-	In a chip Socket with info printed on a label covering a round window
-	showing the circuit inside. (The window indicates it is an EPROM chip.)
-
-  C3 -- "COM9032
-	 SMC 8643"
-	In a chip socket.
-
-  C4 -- "74LS"
-	9 total no sockets.
-
-  M5 -- "50006-136
-	 20.000000 MHZ
-	 MTQ-T1-S3
-	 0 M-TRON 86-40"
-	Metallic case with 4 pins, no socket.
-
-  C6 -- "MOSTEK@TC8643
-	 MK6116N-20
-	 MALAYSIA"
-	No socket.
-
-  C7 -- No stamp or label but in a 20 pin chip socket.
-
-  C8 -- "PAL10L8CN
-	 8623"
-	In a 20 pin socket.
-
-  C9 -- "PAl16R4A-2CN
-	 8641"
-	In a 20 pin socket.
-
-  C10 -- "M8640
-	    NMC
-	  9306N"
-	 In an 8 pin socket.
-
-  ?? -- Some components on a smaller board and attached with 20 pins all
-	along the side closest to the BNC connector.  The are coated in a dark
-	resin.
-
-On the board there are two jumper banks labeled J2 and J3. The
-manufacturer didn't put a J1 on the board. The two boards I have both
-came with a jumper box for each bank.
-
-::
-
-  J2 -- Numbered 1 2 3 4 5 6.
-	4 and 5 are not stamped due to solder points.
-
-  J3 -- IRQ 2 3 4 5 6 7
-
-The board itself has a maple leaf stamped just above the irq jumpers
-and "-2 46-86" beside C2. Between C1 and C6 "ASS 'Y 300163" and "@1986
-CORMAN CUSTOM ELECTRONICS CORP." stamped just below the BNC connector.
-Below that "MADE IN CANADA"
-
-No Name
-=======
-
-8-bit cards, 16-bit cards
--------------------------
-
-  - from Juergen Seifert <seifert@htwm.de>
-
-I have named this ARCnet card "NONAME", since there is no name of any
-manufacturer on the Installation manual nor on the shipping box. The only
-hint to the existence of a manufacturer at all is written in copper,
-it is "Made in Taiwan"
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the Original
-
-		    "ARCnet Installation Manual"
-
-::
-
-    ________________________________________________________________
-   | |STAR| BUS| T/P|                                               |
-   | |____|____|____|                                               |
-   |                            _____________________               |
-   |                           |                     |              |
-   |                           |                     |              |
-   |                           |                     |              |
-   |                           |        SMC          |              |
-   |                           |                     |              |
-   |                           |       COM90C65      |              |
-   |                           |                     |              |
-   |                           |                     |              |
-   |                           |__________-__________|              |
-   |                                                           _____|
-   |      _______________                                     |  CN |
-   |     | PROM          |                                    |_____|
-   |     > SOCKET        |                                          |
-   |     |_______________|         1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8 |
-   |                               _______________  _______________ |
-   |           |o|o|o|o|o|o|o|o|  |      SW1      ||      SW2      ||
-   |           |o|o|o|o|o|o|o|o|  |_______________||_______________||
-   |___         2 3 4 5 7 E E R        Node ID       IOB__|__MEM____|
-       |        \ IRQ   / T T O                      |
-       |__________________1_2_M______________________|
-
-Legend::
-
-  COM90C65:       ARCnet Probe
-  S1  1-8:    Node ID Select
-  S2  1-3:    I/O Base Address Select
-      4-6:    Memory Base Address Select
-      7-8:    RAM Offset Select
-  ET1, ET2    Extended Timeout Select
-  ROM     ROM Enable Select
-  CN              RG62 Coax Connector
-  STAR| BUS | T/P Three fields for placing a sign (colored circle)
-		  indicating the topology of the card
-
-Setting one of the switches to Off means "1", On means "0".
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in group SW1 are used to set the node ID.
-Each node attached to the network must have an unique node ID which
-must be different from 0.
-Switch 8 serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"
-These values are::
-
-    Switch | Value
-    -------|-------
-      8    |   1
-      7    |   2
-      6    |   4
-      5    |   8
-      4    |  16
-      3    |  32
-      2    |  64
-      1    | 128
-
-Some Examples::
-
-    Switch         | Hex     | Decimal
-   1 2 3 4 5 6 7 8 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The first three switches in switch group SW2 are used to select one
-of eight possible I/O Base addresses using the following table::
-
-   Switch      | Hex I/O
-    1   2   3  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   ON  ON  OFF |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   ON  OFF OFF |  2F0
-   OFF ON  ON  |  300
-   OFF ON  OFF |  350
-   OFF OFF ON  |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The memory buffer requires 2K of a 16K block of RAM. The base of this
-16K block can be located in any of eight positions.
-Switches 4-6 of switch group SW2 select the Base of the 16K block.
-Within that 16K address space, the buffer may be assigned any one of four
-positions, determined by the offset, switches 7 and 8 of group SW2.
-
-::
-
-   Switch     | Hex RAM | Hex ROM
-   4 5 6  7 8 | Address | Address *)
-   -----------|---------|-----------
-   0 0 0  0 0 |  C0000  |  C2000
-   0 0 0  0 1 |  C0800  |  C2000
-   0 0 0  1 0 |  C1000  |  C2000
-   0 0 0  1 1 |  C1800  |  C2000
-	      |         |
-   0 0 1  0 0 |  C4000  |  C6000
-   0 0 1  0 1 |  C4800  |  C6000
-   0 0 1  1 0 |  C5000  |  C6000
-   0 0 1  1 1 |  C5800  |  C6000
-	      |         |
-   0 1 0  0 0 |  CC000  |  CE000
-   0 1 0  0 1 |  CC800  |  CE000
-   0 1 0  1 0 |  CD000  |  CE000
-   0 1 0  1 1 |  CD800  |  CE000
-	      |         |
-   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
-   0 1 1  0 1 |  D0800  |  D2000
-   0 1 1  1 0 |  D1000  |  D2000
-   0 1 1  1 1 |  D1800  |  D2000
-	      |         |
-   1 0 0  0 0 |  D4000  |  D6000
-   1 0 0  0 1 |  D4800  |  D6000
-   1 0 0  1 0 |  D5000  |  D6000
-   1 0 0  1 1 |  D5800  |  D6000
-	      |         |
-   1 0 1  0 0 |  D8000  |  DA000
-   1 0 1  0 1 |  D8800  |  DA000
-   1 0 1  1 0 |  D9000  |  DA000
-   1 0 1  1 1 |  D9800  |  DA000
-	      |         |
-   1 1 0  0 0 |  DC000  |  DE000
-   1 1 0  0 1 |  DC800  |  DE000
-   1 1 0  1 0 |  DD000  |  DE000
-   1 1 0  1 1 |  DD800  |  DE000
-	      |         |
-   1 1 1  0 0 |  E0000  |  E2000
-   1 1 1  0 1 |  E0800  |  E2000
-   1 1 1  1 0 |  E1000  |  E2000
-   1 1 1  1 1 |  E1800  |  E2000
-
-   *) To enable the 8K Boot PROM install the jumper ROM.
-      The default is jumper ROM not installed.
-
-
-Setting Interrupt Request Lines (IRQ)
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-To select a hardware interrupt level set one (only one!) of the jumpers
-IRQ2, IRQ3, IRQ4, IRQ5 or IRQ7. The manufacturer's default is IRQ2.
-
-
-Setting the Timeouts
-^^^^^^^^^^^^^^^^^^^^
-
-The two jumpers labeled ET1 and ET2 are used to determine the timeout
-parameters (response and reconfiguration time). Every node in a network
-must be set to the same timeout values.
-
-::
-
-   ET1 ET2 | Response Time (us) | Reconfiguration Time (ms)
-   --------|--------------------|--------------------------
-   Off Off |        78          |          840   (Default)
-   Off On  |       285          |         1680
-   On  Off |       563          |         1680
-   On  On  |      1130          |         1680
-
-On means jumper installed, Off means jumper not installed
-
-
-16-BIT ARCNET
--------------
-
-The manual of my 8-Bit NONAME ARCnet Card contains another description
-of a 16-Bit Coax / Twisted Pair Card. This description is incomplete,
-because there are missing two pages in the manual booklet. (The table
-of contents reports pages ... 2-9, 2-11, 2-12, 3-1, ... but inside
-the booklet there is a different way of counting ... 2-9, 2-10, A-1,
-(empty page), 3-1, ..., 3-18, A-1 (again), A-2)
-Also the picture of the board layout is not as good as the picture of
-8-Bit card, because there isn't any letter like "SW1" written to the
-picture.
-
-Should somebody have such a board, please feel free to complete this
-description or to send a mail to me!
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the Original
-
-		    "ARCnet Installation Manual"
-
-::
-
-   ___________________________________________________________________
-  <                    _________________  _________________           |
-  >                   |       SW?       ||      SW?        |          |
-  <                   |_________________||_________________|          |
-  >                       ____________________                        |
-  <                      |                    |                       |
-  >                      |                    |                       |
-  <                      |                    |                       |
-  >                      |                    |                       |
-  <                      |                    |                       |
-  >                      |                    |                       |
-  <                      |                    |                       |
-  >                      |____________________|                       |
-  <                                                               ____|
-  >                       ____________________                   |    |
-  <                      |                    |                  | J1 |
-  >                      |                    <                  |    |
-  <                      |____________________|  ? ? ? ? ? ?     |____|
-  >                                             |o|o|o|o|o|o|         |
-  <                                             |o|o|o|o|o|o|         |
-  >                                                                   |
-  <             __                                         ___________|
-  >            |  |                                       |
-  <____________|  |_______________________________________|
-
-
-Setting one of the switches to Off means "1", On means "0".
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in group SW2 are used to set the node ID.
-Each node attached to the network must have an unique node ID which
-must be different from 0.
-Switch 8 serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"
-These values are::
-
-    Switch | Value
-    -------|-------
-      8    |   1
-      7    |   2
-      6    |   4
-      5    |   8
-      4    |  16
-      3    |  32
-      2    |  64
-      1    | 128
-
-Some Examples::
-
-    Switch         | Hex     | Decimal
-   1 2 3 4 5 6 7 8 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The first three switches in switch group SW1 are used to select one
-of eight possible I/O Base addresses using the following table::
-
-   Switch      | Hex I/O
-    3   2   1  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   ON  ON  OFF |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   ON  OFF OFF |  2F0
-   OFF ON  ON  |  300
-   OFF ON  OFF |  350
-   OFF OFF ON  |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The memory buffer requires 2K of a 16K block of RAM. The base of this
-16K block can be located in any of eight positions.
-Switches 6-8 of switch group SW1 select the Base of the 16K block.
-Within that 16K address space, the buffer may be assigned any one of four
-positions, determined by the offset, switches 4 and 5 of group SW1::
-
-   Switch     | Hex RAM | Hex ROM
-   8 7 6  5 4 | Address | Address
-   -----------|---------|-----------
-   0 0 0  0 0 |  C0000  |  C2000
-   0 0 0  0 1 |  C0800  |  C2000
-   0 0 0  1 0 |  C1000  |  C2000
-   0 0 0  1 1 |  C1800  |  C2000
-	      |         |
-   0 0 1  0 0 |  C4000  |  C6000
-   0 0 1  0 1 |  C4800  |  C6000
-   0 0 1  1 0 |  C5000  |  C6000
-   0 0 1  1 1 |  C5800  |  C6000
-	      |         |
-   0 1 0  0 0 |  CC000  |  CE000
-   0 1 0  0 1 |  CC800  |  CE000
-   0 1 0  1 0 |  CD000  |  CE000
-   0 1 0  1 1 |  CD800  |  CE000
-	      |         |
-   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
-   0 1 1  0 1 |  D0800  |  D2000
-   0 1 1  1 0 |  D1000  |  D2000
-   0 1 1  1 1 |  D1800  |  D2000
-	      |         |
-   1 0 0  0 0 |  D4000  |  D6000
-   1 0 0  0 1 |  D4800  |  D6000
-   1 0 0  1 0 |  D5000  |  D6000
-   1 0 0  1 1 |  D5800  |  D6000
-	      |         |
-   1 0 1  0 0 |  D8000  |  DA000
-   1 0 1  0 1 |  D8800  |  DA000
-   1 0 1  1 0 |  D9000  |  DA000
-   1 0 1  1 1 |  D9800  |  DA000
-	      |         |
-   1 1 0  0 0 |  DC000  |  DE000
-   1 1 0  0 1 |  DC800  |  DE000
-   1 1 0  1 0 |  DD000  |  DE000
-   1 1 0  1 1 |  DD800  |  DE000
-	      |         |
-   1 1 1  0 0 |  E0000  |  E2000
-   1 1 1  0 1 |  E0800  |  E2000
-   1 1 1  1 0 |  E1000  |  E2000
-   1 1 1  1 1 |  E1800  |  E2000
-
-
-Setting Interrupt Request Lines (IRQ)
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-??????????????????????????????????????
-
-
-Setting the Timeouts
-^^^^^^^^^^^^^^^^^^^^
-
-??????????????????????????????????????
-
-
-8-bit cards ("Made in Taiwan R.O.C.")
--------------------------------------
-
-  - from Vojtech Pavlik <vojtech@suse.cz>
-
-I have named this ARCnet card "NONAME", since I got only the card with
-no manual at all and the only text identifying the manufacturer is
-"MADE IN TAIWAN R.O.C" printed on the card.
-
-::
-
-	  ____________________________________________________________
-	 |                 1 2 3 4 5 6 7 8                            |
-	 | |o|o| JP1       o|o|o|o|o|o|o|o| ON                        |
-	 |  +              o|o|o|o|o|o|o|o|                        ___|
-	 |  _____________  o|o|o|o|o|o|o|o| OFF         _____     |   | ID7
-	 | |             | SW1                         |     |    |   | ID6
-	 | > RAM (2k)    |        ____________________ |  H  |    | S | ID5
-	 | |_____________|       |                    ||  y  |    | W | ID4
-	 |                       |                    ||  b  |    | 2 | ID3
-	 |                       |                    ||  r  |    |   | ID2
-	 |                       |                    ||  i  |    |   | ID1
-	 |                       |       90C65        ||  d  |    |___| ID0
-	 |      SW3              |                    ||     |        |
-	 | |o|o|o|o|o|o|o|o| ON  |                    ||  I  |        |
-	 | |o|o|o|o|o|o|o|o|     |                    ||  C  |        |
-	 | |o|o|o|o|o|o|o|o| OFF |____________________||     |   _____|
-	 |  1 2 3 4 5 6 7 8                            |     |  |     |___
-	 |  ______________                             |     |  | BNC |___|
-	 | |              |                            |_____|  |_____|
-	 | > EPROM SOCKET |                                           |
-	 | |______________|                                           |
-	 |                                              ______________|
-	 |                                             |
-	 |_____________________________________________|
-
-Legend::
-
-  90C65       ARCNET Chip
-  SW1 1-5:    Base Memory Address Select
-      6-8:    Base I/O Address Select
-  SW2 1-8:    Node ID Select (ID0-ID7)
-  SW3 1-5:    IRQ Select
-      6-7:    Extra Timeout
-      8  :    ROM Enable
-  JP1         Led connector
-  BNC         Coax connector
-
-Although the jumpers SW1 and SW3 are marked SW, not JP, they are jumpers, not
-switches.
-
-Setting the jumpers to ON means connecting the upper two pins, off the bottom
-two - or - in case of IRQ setting, connecting none of them at all.
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in SW2 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must not be 0.
-Switch 1 (ID0) serves as the least significant bit (LSB).
-
-Setting one of the switches to Off means "1", On means "0".
-
-The node ID is the sum of the values of all switches set to "1"
-These values are::
-
-   Switch | Label | Value
-   -------|-------|-------
-     1    | ID0   |   1
-     2    | ID1   |   2
-     3    | ID2   |   4
-     4    | ID3   |   8
-     5    | ID4   |  16
-     6    | ID5   |  32
-     7    | ID6   |  64
-     8    | ID7   | 128
-
-Some Examples::
-
-    Switch         | Hex     | Decimal
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The last three switches in switch block SW1 are used to select one
-of eight possible I/O Base addresses using the following table::
-
-
-   Switch      | Hex I/O
-    6   7   8  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   OFF ON  ON  |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   OFF OFF ON  |  2F0
-   ON  ON  OFF |  300
-   OFF ON  OFF |  350
-   ON  OFF OFF |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be
-located in any of eight positions. The address of the Boot Prom is
-memory base + 0x2000.
-
-Jumpers 3-5 of jumper block SW1 select the Memory Base address.
-
-::
-
-   Switch              | Hex RAM | Hex ROM
-    1   2   3   4   5  | Address | Address *)
-   --------------------|---------|-----------
-   ON  ON  ON  ON  ON  |  C0000  |  C2000
-   ON  ON  OFF ON  ON  |  C4000  |  C6000
-   ON  ON  ON  OFF ON  |  CC000  |  CE000
-   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturer's default)
-   ON  ON  ON  ON  OFF |  D4000  |  D6000
-   ON  ON  OFF ON  OFF |  D8000  |  DA000
-   ON  ON  ON  OFF OFF |  DC000  |  DE000
-   ON  ON  OFF OFF OFF |  E0000  |  E2000
-
-  *) To enable the Boot ROM set the jumper 8 of jumper block SW3 to position ON.
-
-The jumpers 1 and 2 probably add 0x0800, 0x1000 and 0x1800 to RAM adders.
-
-Setting the Interrupt Line
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Jumpers 1-5 of the jumper block SW3 control the IRQ level::
-
-    Jumper              |  IRQ
-    1   2   3   4   5   |
-   ----------------------------
-    ON  OFF OFF OFF OFF |  2
-    OFF ON  OFF OFF OFF |  3
-    OFF OFF ON  OFF OFF |  4
-    OFF OFF OFF ON  OFF |  5
-    OFF OFF OFF OFF ON  |  7
-
-
-Setting the Timeout Parameters
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The jumpers 6-7 of the jumper block SW3 are used to determine the timeout
-parameters. These two jumpers are normally left in the OFF position.
-
-
-
-(Generic Model 9058)
---------------------
-  - from Andrew J. Kroll <ag784@freenet.buffalo.edu>
-  - Sorry this sat in my to-do box for so long, Andrew! (yikes - over a
-    year!)
-
-::
-
-								      _____
-								     |    <
-								     | .---'
-    ________________________________________________________________ | |
-   |                           |     SW2     |                      |  |
-   |   ___________             |_____________|                      |  |
-   |  |           |              1 2 3 4 5 6                     ___|  |
-   |  >  6116 RAM |         _________                         8 |   |  |
-   |  |___________|        |20MHzXtal|                        7 |   |  |
-   |                       |_________|       __________       6 | S |  |
-   |    74LS373                             |          |-     5 | W |  |
-   |   _________                            |      E   |-     4 |   |  |
-   |   >_______|              ______________|..... P   |-     3 | 3 |  |
-   |                         |              |    : O   |-     2 |   |  |
-   |                         |              |    : X   |-     1 |___|  |
-   |   ________________      |              |    : Y   |-           |  |
-   |  |      SW1       |     |      SL90C65 |    :     |-           |  |
-   |  |________________|     |              |    : B   |-           |  |
-   |    1 2 3 4 5 6 7 8      |              |    : O   |-           |  |
-   |                         |_________o____|..../ A   |-    _______|  |
-   |    ____________________                |      R   |-   |       |------,
-   |   |                    |               |      D   |-   |  BNC  |   #  |
-   |   > 2764 PROM SOCKET   |               |__________|-   |_______|------'
-   |   |____________________|              _________                |  |
-   |                                       >________| <- 74LS245    |  |
-   |                                                                |  |
-   |___                                               ______________|  |
-       |H H H H H H H H H H H H H H H H H H H H H H H|               | |
-       |U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U|               | |
-								      \|
-
-Legend::
-
-  SL90C65 	ARCNET Controller / Transceiver /Logic
-  SW1	1-5:	IRQ Select
-	  6:	ET1
-	  7:	ET2
-	  8:	ROM ENABLE
-  SW2	1-3:    Memory Buffer/PROM Address
-	3-6:	I/O Address Map
-  SW3	1-8:	Node ID Select
-  BNC		BNC RG62/U Connection
-		*I* have had success using RG59B/U with *NO* terminators!
-		What gives?!
-
-SW1: Timeouts, Interrupt and ROM
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-To select a hardware interrupt level set one (only one!) of the dip switches
-up (on) SW1...(switches 1-5)
-IRQ3, IRQ4, IRQ5, IRQ7, IRQ2. The Manufacturer's default is IRQ2.
-
-The switches on SW1 labeled EXT1 (switch 6) and EXT2 (switch 7)
-are used to determine the timeout parameters. These two dip switches
-are normally left off (down).
-
-   To enable the 8K Boot PROM position SW1 switch 8 on (UP) labeled ROM.
-   The default is jumper ROM not installed.
-
-
-Setting the I/O Base Address
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The last three switches in switch group SW2 are used to select one
-of eight possible I/O Base addresses using the following table::
-
-
-   Switch | Hex I/O
-   4 5 6  | Address
-   -------|--------
-   0 0 0  |  260
-   0 0 1  |  290
-   0 1 0  |  2E0  (Manufacturer's default)
-   0 1 1  |  2F0
-   1 0 0  |  300
-   1 0 1  |  350
-   1 1 0  |  380
-   1 1 1  |  3E0
-
-
-Setting the Base Memory Address (RAM & ROM)
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The memory buffer requires 2K of a 16K block of RAM. The base of this
-16K block can be located in any of eight positions.
-Switches 1-3 of switch group SW2 select the Base of the 16K block.
-(0 = DOWN, 1 = UP)
-I could, however, only verify two settings...
-
-
-::
-
-   Switch| Hex RAM | Hex ROM
-   1 2 3 | Address | Address
-   ------|---------|-----------
-   0 0 0 |  E0000  |  E2000
-   0 0 1 |  D0000  |  D2000  (Manufacturer's default)
-   0 1 0 |  ?????  |  ?????
-   0 1 1 |  ?????  |  ?????
-   1 0 0 |  ?????  |  ?????
-   1 0 1 |  ?????  |  ?????
-   1 1 0 |  ?????  |  ?????
-   1 1 1 |  ?????  |  ?????
-
-
-Setting the Node ID
-^^^^^^^^^^^^^^^^^^^
-
-The eight switches in group SW3 are used to set the node ID.
-Each node attached to the network must have an unique node ID which
-must be different from 0.
-Switch 1 serves as the least significant bit (LSB).
-switches in the DOWN position are OFF (0) and in the UP position are ON (1)
-
-The node ID is the sum of the values of all switches set to "1"
-These values are::
-
-    Switch | Value
-    -------|-------
-      1    |   1
-      2    |   2
-      3    |   4
-      4    |   8
-      5    |  16
-      6    |  32
-      7    |  64
-      8    | 128
-
-Some Examples::
-
-      Switch#     |   Hex   | Decimal
-  8 7 6 5 4 3 2 1 | Node ID | Node ID
-  ----------------|---------|---------
-  0 0 0 0 0 0 0 0 |    not allowed  <-.
-  0 0 0 0 0 0 0 1 |    1    |    1    |
-  0 0 0 0 0 0 1 0 |    2    |    2    |
-  0 0 0 0 0 0 1 1 |    3    |    3    |
-      . . .       |         |         |
-  0 1 0 1 0 1 0 1 |   55    |   85    |
-      . . .       |         |         + Don't use 0 or 255!
-  1 0 1 0 1 0 1 0 |   AA    |  170    |
-      . . .       |         |         |
-  1 1 1 1 1 1 0 1 |   FD    |  253    |
-  1 1 1 1 1 1 1 0 |   FE    |  254    |
-  1 1 1 1 1 1 1 1 |   FF    |  255  <-'
-
-
-Tiara
-=====
-
-(model unknown)
----------------
-
-  - from Christoph Lameter <cl@gentwo.org>
-
-
-Here is information about my card as far as I could figure it out::
-
-
-  ----------------------------------------------- tiara
-  Tiara LanCard of Tiara Computer Systems.
-
-  +----------------------------------------------+
-  !           ! Transmitter Unit !               !
-  !           +------------------+             -------
-  !          MEM                              Coax Connector
-  !  ROM    7654321 <- I/O                     -------
-  !  :  :   +--------+                           !
-  !  :  :   ! 90C66LJ!                         +++
-  !  :  :   !        !                         !D  Switch to set
-  !  :  :   !        !                         !I  the Nodenumber
-  !  :  :   +--------+                         !P
-  !                                            !++
-  !         234567 <- IRQ                      !
-  +------------!!!!!!!!!!!!!!!!!!!!!!!!--------+
-	       !!!!!!!!!!!!!!!!!!!!!!!!
-
-- 0 = Jumper Installed
-- 1 = Open
-
-Top Jumper line Bit 7 = ROM Enable 654=Memory location 321=I/O
-
-Settings for Memory Location (Top Jumper Line)
-
-===     ================
-456     Address selected
-===     ================
-000	C0000
-001     C4000
-010     CC000
-011     D0000
-100     D4000
-101     D8000
-110     DC000
-111     E0000
-===     ================
-
-Settings for I/O Address (Top Jumper Line)
-
-===     ====
-123     Port
-===     ====
-000	260
-001	290
-010	2E0
-011	2F0
-100	300
-101	350
-110	380
-111	3E0
-===     ====
-
-Settings for IRQ Selection (Lower Jumper Line)
-
-====== =====
-234567
-====== =====
-011111 IRQ 2
-101111 IRQ 3
-110111 IRQ 4
-111011 IRQ 5
-111110 IRQ 7
-====== =====
-
-Other Cards
-===========
-
-I have no information on other models of ARCnet cards at the moment.
-
-Thanks.
diff --git a/Documentation/networking/arcnet.rst b/Documentation/networking/arcnet.rst
index cd43a18ad149..ce1b009bef96 100644
--- a/Documentation/networking/arcnet.rst
+++ b/Documentation/networking/arcnet.rst
@@ -8,7 +8,7 @@ ARCnet
 
 .. note::
 
-   See also arcnet-hardware.txt in this directory for jumper-setting
+   See also arcnet-hardware.rst in this directory for jumper-setting
    and cabling information if you're like many of us and didn't happen to get a
    manual with your ARCnet card.
 
@@ -88,157 +88,43 @@ versions are available on my WWW page, or via e-mail if you don't have WWW
 access.
 
 
-Installing the Driver
----------------------
 
-All you will need to do in order to install the driver is::
+Supported Hardware
+------------------
 
-	make config
-		(be sure to choose ARCnet in the network devices
-		and at least one chipset driver.)
-	make clean
-	make zImage
+Only PCI and PCI Express devices based on the COM20020 chipset are supported.
+This is the newest chipset from SMC with support for promiscuous mode (packet
+sniffing), extra diagnostic information, etc. These devices use the com20020_pci
+driver.
 
-If you obtained this ARCnet package as an upgrade to the ARCnet driver in
-your current kernel, you will need to first copy arcnet.c over the one in
-the linux/drivers/net directory.
+Support for older chipsets and ISA and PCMCIA devices was previously available
+but has been removed.
 
-You will know the driver is installed properly if you get some ARCnet
-messages when you reboot into the new Linux kernel.
 
-There are four chipset options:
-
- 1. Standard ARCnet COM90xx chipset.
-
-This is the normal ARCnet card, which you've probably got. This is the only
-chipset driver which will autoprobe if not told where the card is.
-It following options on the command line::
-
- com90xx=[<io>[,<irq>[,<shmem>]]][,<name>] | <name>
-
-If you load the chipset support as a module, the options are::
-
- io=<io> irq=<irq> shmem=<shmem> device=<name>
-
-To disable the autoprobe, just specify "com90xx=" on the kernel command line.
-To specify the name alone, but allow autoprobe, just put "com90xx=<name>"
-
- 2. ARCnet COM20020 chipset.
+Configuring the Driver
+----------------------
 
-This is the new chipset from SMC with support for promiscuous mode (packet
-sniffing), extra diagnostic information, etc. Unfortunately, there is no
-sensible method of autoprobing for these cards. You must specify the I/O
-address on the kernel command line.
+The COM20020 driver will be loaded automatically at boot if a supported card is
+detected.
 
-The command line options are::
+If the com20020_pci driver was compiled as a loadable module, the options are::
 
- com20020=<io>[,<irq>[,<node_ID>[,backplane[,CKP[,timeout]]]]][,name]
+ node=<node_ID> backplane=<backplane> clockp=<CKP> clockm=<CKM>
+ timeout=<timeout> device=<interface_name>
 
-If you load the chipset support as a module, the options are::
+If the driver was compiled into the kernel, the same options can be specified on
+the kernel command line by prefixing them with `com20020_pci.`, as in the
+following example::
 
- io=<io> irq=<irq> node=<node_ID> backplane=<backplane> clock=<CKP>
- timeout=<timeout> device=<name>
+ com20020_pci.device=eth1
 
 The COM20020 chipset allows you to set the node ID in software, overriding the
 default which is still set in DIP switches on the card. If you don't have the
-COM20020 data sheets, and you don't know what the other three options refer
+COM20020 data sheets, and you don't know what the other options refer
 to, then they won't interest you - forget them.
 
- 3. ARCnet COM90xx chipset in IO-mapped mode.
-
-This will also work with the normal ARCnet cards, but doesn't use the shared
-memory. It performs less well than the above driver, but is provided in case
-you have a card which doesn't support shared memory, or (strangely) in case
-you have so many ARCnet cards in your machine that you run out of shmem slots.
-If you don't give the IO address on the kernel command line, then the driver
-will not find the card.
-
-The command line options are::
-
- com90io=<io>[,<irq>][,<name>]
-
-If you load the chipset support as a module, the options are:
- io=<io> irq=<irq> device=<name>
-
- 4. ARCnet RIM I cards.
-
-These are COM90xx chips which are _completely_ memory mapped. The support for
-these is not tested. If you have one, please mail the author with a success
-report. All options must be specified, except the device name.
-Command line options::
-
- arcrimi=<shmem>,<irq>,<node_ID>[,<name>]
-
-If you load the chipset support as a module, the options are::
-
- shmem=<shmem> irq=<irq> node=<node_ID> device=<name>
-
-
-Loadable Module Support
------------------------
-
-Configure and rebuild Linux.  When asked, answer 'm' to "Generic ARCnet
-support" and to support for your ARCnet chipset if you want to use the
-loadable module. You can also say 'y' to "Generic ARCnet support" and 'm'
-to the chipset support if you wish.
-
-::
-
-	make config
-	make clean
-	make zImage
-	make modules
-
-If you're using a loadable module, you need to use insmod to load it, and
-you can specify various characteristics of your card on the command
-line.  (In recent versions of the driver, autoprobing is much more reliable
-and works as a module, so most of this is now unnecessary.)
-
-For example::
-
-	cd /usr/src/linux/modules
-	insmod arcnet.o
-	insmod com90xx.o
-	insmod com20020.o io=0x2e0 device=eth1
-
-
-Using the Driver
-----------------
-
-If you build your kernel with ARCnet COM90xx support included, it should
-probe for your card automatically when you boot. If you use a different
-chipset driver complied into the kernel, you must give the necessary options
-on the kernel command line, as detailed above.
-
-Go read the NET-2-HOWTO and ETHERNET-HOWTO for Linux; they should be
-available where you picked up this driver.  Think of your ARCnet as a
-souped-up (or down, as the case may be) Ethernet card.
-
-By the way, be sure to change all references from "eth0" to "arc0" in the
-HOWTOs.  Remember that ARCnet isn't a "true" Ethernet, and the device name
-is DIFFERENT.
-
-
-Multiple Cards in One Computer
-------------------------------
-
-Linux has pretty good support for this now, but since I've been busy, the
-ARCnet driver has somewhat suffered in this respect. COM90xx support, if
-compiled into the kernel, will (try to) autodetect all the installed cards.
-
-If you have other cards, with support compiled into the kernel, then you can
-just repeat the options on the kernel command line, e.g.::
-
-	LILO: linux com20020=0x2e0 com20020=0x380 com90io=0x260
-
-If you have the chipset support built as a loadable module, then you need to
-do something like this::
-
-	insmod -o arc0 com90xx
-	insmod -o arc1 com20020 io=0x2e0
-	insmod -o arc2 com90xx
-
-The ARCnet drivers will now sort out their names automatically.
+Otherwise, ARCnet can be configured in a similar way to Ethernet, with the
+exception that ARCnet interface names begin with `arc`.
 
 
 How do I get it to work with...?
@@ -524,10 +410,6 @@ first!  D_DURING displays 4-5 lines for each packet sent or received.  D_TX,
 D_RX, and D_SKB actually DISPLAY each packet as it is sent or received,
 which is obviously quite big.
 
-Starting with v2.40 ALPHA, the autoprobe routines have changed
-significantly.  In particular, they won't tell you why the card was not
-found unless you turn on the D_INIT_REASONS debugging flag.
-
 Once the driver is running, you can run the arcdump shell script (available
 from me or in the full ARCnet package, if you have it) as root to list the
 contents of the arcnet buffers at any time.  To make any sense at all out of
@@ -548,7 +430,7 @@ out which bytes are being used by a packet.
 You can change the debug level without recompiling the kernel by typing::
 
 	ifconfig arc0 down metric 1xxx
-	/etc/rc.d/rc.inet1
+	ifconfig arc0 up
 
 where "xxx" is the debug level you want.  For example, "metric 1015" would put
 you at debug level 15.  Debug level 7 is currently the default.
diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig
index 3629afbe5d75..1d223be90993 100644
--- a/arch/mips/configs/mtx1_defconfig
+++ b/arch/mips/configs/mtx1_defconfig
@@ -221,12 +221,8 @@ CONFIG_ARCNET_1201=m
 CONFIG_ARCNET_1051=m
 CONFIG_ARCNET_RAW=m
 CONFIG_ARCNET_CAP=m
-CONFIG_ARCNET_COM90xx=m
-CONFIG_ARCNET_COM90xxIO=m
-CONFIG_ARCNET_RIM_I=m
 CONFIG_ARCNET_COM20020=m
 CONFIG_ARCNET_COM20020_PCI=m
-CONFIG_ARCNET_COM20020_CS=m
 CONFIG_VORTEX=m
 CONFIG_TYPHOON=m
 CONFIG_ADAPTEC_STARFIRE=m
diff --git a/drivers/net/arcnet/Kconfig b/drivers/net/arcnet/Kconfig
index d1d07a1d4fbc..4611a37168c4 100644
--- a/drivers/net/arcnet/Kconfig
+++ b/drivers/net/arcnet/Kconfig
@@ -4,7 +4,7 @@
 #
 
 menuconfig ARCNET
-	depends on NETDEVICES && (ISA || PCI || PCMCIA) && HAS_IOPORT
+	depends on NETDEVICES && PCI && HAS_IOPORT
 	tristate "ARCnet support"
 	help
 	  If you have a network card of this type, say Y and check out the
@@ -12,9 +12,7 @@ menuconfig ARCNET
 	  <file:Documentation/networking/arcnet.rst>.
 
 	  You need both this driver, and the driver for the particular ARCnet
-	  chipset of your card. If you don't know, then it's probably a
-	  COM90xx type card, so say Y (or M) to "ARCnet COM90xx chipset
-	  support" below.
+	  chipset of your card.
 
 	  To compile this driver as a module, choose M here. The module will
 	  be called arcnet.
@@ -70,38 +68,6 @@ config ARCNET_CAP
 
 	  Cap only listens to protocol 1-8.
 
-config ARCNET_COM90xx
-	tristate "ARCnet COM90xx (normal) chipset driver"
-	help
-	  This is the chipset driver for the standard COM90xx cards. If you
-	  have always used the old ARCnet driver without knowing what type of
-	  card you had, this is probably the one for you.
-
-	  To compile this driver as a module, choose M here. The module will
-	  be called com90xx.
-
-config ARCNET_COM90xxIO
-	tristate "ARCnet COM90xx (IO mapped) chipset driver"
-	help
-	  This is the chipset driver for the COM90xx cards, using them in
-	  IO-mapped mode instead of memory-mapped mode. This is slower than
-	  the normal driver. Only use it if your card doesn't support shared
-	  memory.
-
-	  To compile this driver as a module, choose M here. The module will
-	  be called com90io.
-
-config ARCNET_RIM_I
-	tristate "ARCnet COM90xx (RIM I) chipset driver"
-	help
-	  This is yet another chipset driver for the COM90xx cards, but this
-	  time only using memory-mapped mode, and no IO ports at all. This
-	  driver is completely untested, so if you have one of these cards,
-	  please mail <dwmw2@infradead.org>, especially if it works!
-
-	  To compile this driver as a module, choose M here. The module will
-	  be called arc-rimi.
-
 config ARCNET_COM20020
 	tristate "ARCnet COM20020 chipset driver"
 	depends on LEDS_CLASS
@@ -113,22 +79,8 @@ config ARCNET_COM20020
 	  To compile this driver as a module, choose M here. The module will
 	  be called com20020.
 
-config ARCNET_COM20020_ISA
-	tristate "Support for COM20020 on ISA"
-	depends on ARCNET_COM20020 && ISA
-
 config ARCNET_COM20020_PCI
 	tristate "Support for COM20020 on PCI"
 	depends on ARCNET_COM20020 && PCI
 
-config ARCNET_COM20020_CS
-	tristate "COM20020 ARCnet PCMCIA support"
-	depends on ARCNET_COM20020 && PCMCIA
-	help
-	  Say Y here if you intend to attach this type of ARCnet PCMCIA card
-	  to your computer.
-
-	  To compile this driver as a module, choose M here: the module will be
-	  called com20020_cs.  If unsure, say N.
-
 endif # ARCNET
diff --git a/drivers/net/arcnet/Makefile b/drivers/net/arcnet/Makefile
index 53525e8ea130..e5df03a08b0e 100644
--- a/drivers/net/arcnet/Makefile
+++ b/drivers/net/arcnet/Makefile
@@ -7,10 +7,5 @@ obj-$(CONFIG_ARCNET_1201) += rfc1201.o
 obj-$(CONFIG_ARCNET_1051) += rfc1051.o
 obj-$(CONFIG_ARCNET_RAW) += arc-rawmode.o
 obj-$(CONFIG_ARCNET_CAP) += capmode.o
-obj-$(CONFIG_ARCNET_COM90xx) += com90xx.o
-obj-$(CONFIG_ARCNET_COM90xxIO) += com90io.o
-obj-$(CONFIG_ARCNET_RIM_I) += arc-rimi.o
 obj-$(CONFIG_ARCNET_COM20020) += com20020.o
-obj-$(CONFIG_ARCNET_COM20020_ISA) += com20020-isa.o
 obj-$(CONFIG_ARCNET_COM20020_PCI) += com20020-pci.o
-obj-$(CONFIG_ARCNET_COM20020_CS) += com20020_cs.o
diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c
deleted file mode 100644
index fb3d3565aa9a..000000000000
--- a/drivers/net/arcnet/arc-rimi.c
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Linux ARCnet driver - "RIM I" (entirely mem-mapped) cards
- *
- * Written 1994-1999 by Avery Pennarun.
- * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
- * Derived from skeleton.c by Donald Becker.
- *
- * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
- *  for sponsoring the further development of this driver.
- *
- * **********************
- *
- * The original copyright of skeleton.c was as follows:
- *
- * skeleton.c Written 1993 by Donald Becker.
- * Copyright 1993 United States Government as represented by the
- * Director, National Security Agency.  This software may only be used
- * and distributed according to the terms of the GNU General Public License as
- * modified by SRC, incorporated herein by reference.
- *
- * **********************
- *
- * For more details, see drivers/net/arcnet.c
- *
- * **********************
- */
-
-#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/memblock.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-
-#include "arcdevice.h"
-#include "com9026.h"
-
-/* Internal function declarations */
-
-static int arcrimi_probe(struct net_device *dev);
-static int arcrimi_found(struct net_device *dev);
-static void arcrimi_command(struct net_device *dev, int command);
-static int arcrimi_status(struct net_device *dev);
-static void arcrimi_setmask(struct net_device *dev, int mask);
-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,
-				 void *buf, int count);
-static void arcrimi_copy_from_card(struct net_device *dev, int bufnum,
-				   int offset, void *buf, int count);
-
-/* Handy defines for ARCnet specific stuff */
-
-/* Amount of I/O memory used by the card */
-#define BUFFER_SIZE	(512)
-#define MIRROR_SIZE	(BUFFER_SIZE * 4)
-
-/* 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
- * need to be passed a specific shmem address, IRQ, and node ID.
- */
-static int __init arcrimi_probe(struct net_device *dev)
-{
-	if (BUGLVL(D_NORMAL)) {
-		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");
-		pr_info("Given: node %02Xh, shmem %lXh, irq %d\n",
-			dev->dev_addr[0], dev->mem_start, dev->irq);
-	}
-
-	if (dev->mem_start <= 0 || dev->irq <= 0) {
-		if (BUGLVL(D_NORMAL))
-			pr_err("No autoprobe for RIM I; you must specify the shmem and irq!\n");
-		return -ENODEV;
-	}
-	if (dev->dev_addr[0] == 0) {
-		if (BUGLVL(D_NORMAL))
-			pr_err("You need to specify your card's station ID!\n");
-		return -ENODEV;
-	}
-	/* Grab the memory region at mem_start for MIRROR_SIZE bytes.
-	 * Later in arcrimi_found() the real size will be determined
-	 * and this reserve will be released and the correct size
-	 * will be taken.
-	 */
-	if (!request_mem_region(dev->mem_start, MIRROR_SIZE, "arcnet (90xx)")) {
-		if (BUGLVL(D_NORMAL))
-			pr_notice("Card memory already allocated\n");
-		return -ENODEV;
-	}
-	return arcrimi_found(dev);
-}
-
-static int check_mirror(unsigned long addr, size_t size)
-{
-	void __iomem *p;
-	int res = -1;
-
-	if (!request_mem_region(addr, size, "arcnet (90xx)"))
-		return -1;
-
-	p = ioremap(addr, size);
-	if (p) {
-		if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue)
-			res = 1;
-		else
-			res = 0;
-		iounmap(p);
-	}
-
-	release_mem_region(addr, size);
-	return res;
-}
-
-/* Set up the struct net_device associated with this card.
- * Called after probing succeeds.
- */
-static int __init arcrimi_found(struct net_device *dev)
-{
-	struct arcnet_local *lp;
-	unsigned long first_mirror, last_mirror, shmem;
-	void __iomem *p;
-	int mirror_size;
-	int err;
-
-	p = ioremap(dev->mem_start, MIRROR_SIZE);
-	if (!p) {
-		release_mem_region(dev->mem_start, MIRROR_SIZE);
-		arc_printk(D_NORMAL, dev, "Can't ioremap\n");
-		return -ENODEV;
-	}
-
-	/* reserve the irq */
-	if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) {
-		iounmap(p);
-		release_mem_region(dev->mem_start, MIRROR_SIZE);
-		arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);
-		return -ENODEV;
-	}
-
-	shmem = dev->mem_start;
-	arcnet_writeb(TESTvalue, p, COM9026_REG_W_INTMASK);
-	arcnet_writeb(TESTvalue, p, COM9026_REG_W_COMMAND);
-					/* actually the station/node ID */
-
-	/* find the real shared memory start/end points, including mirrors */
-
-	/* guess the actual size of one "memory mirror" - the number of
-	 * bytes between copies of the shared memory.  On most cards, it's
-	 * 2k (or there are no mirrors at all) but on some, it's 4k.
-	 */
-	mirror_size = MIRROR_SIZE;
-	if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue &&
-	    check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
-	    check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
-		mirror_size = 2 * MIRROR_SIZE;
-
-	first_mirror = shmem - mirror_size;
-	while (check_mirror(first_mirror, mirror_size) == 1)
-		first_mirror -= mirror_size;
-	first_mirror += mirror_size;
-
-	last_mirror = shmem + mirror_size;
-	while (check_mirror(last_mirror, mirror_size) == 1)
-		last_mirror += mirror_size;
-	last_mirror -= mirror_size;
-
-	dev->mem_start = first_mirror;
-	dev->mem_end = last_mirror + MIRROR_SIZE - 1;
-
-	/* initialize the rest of the device structure. */
-
-	lp = netdev_priv(dev);
-	lp->card_name = "RIM I";
-	lp->hw.command = arcrimi_command;
-	lp->hw.status = arcrimi_status;
-	lp->hw.intmask = arcrimi_setmask;
-	lp->hw.reset = arcrimi_reset;
-	lp->hw.owner = THIS_MODULE;
-	lp->hw.copy_to_card = arcrimi_copy_to_card;
-	lp->hw.copy_from_card = arcrimi_copy_from_card;
-
-	/* re-reserve the memory region - arcrimi_probe() allocated this reqion
-	 * 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
-	 * fail.
-	 */
-	iounmap(p);
-	release_mem_region(shmem, MIRROR_SIZE);
-	if (!request_mem_region(dev->mem_start,
-				dev->mem_end - dev->mem_start + 1,
-				"arcnet (90xx)")) {
-		arc_printk(D_NORMAL, dev, "Card memory already allocated\n");
-		goto err_free_irq;
-	}
-
-	lp->mem_start = ioremap(dev->mem_start,
-				dev->mem_end - dev->mem_start + 1);
-	if (!lp->mem_start) {
-		arc_printk(D_NORMAL, dev, "Can't remap device memory!\n");
-		goto err_release_mem;
-	}
-
-	/* get and check the station ID from offset 1 in shmem */
-	arcnet_set_addr(dev, arcnet_readb(lp->mem_start,
-					  COM9026_REG_R_STATION));
-
-	arc_printk(D_NORMAL, dev, "ARCnet RIM I: station %02Xh found at IRQ %d, ShMem %lXh (%ld*%d bytes)\n",
-		   dev->dev_addr[0],
-		   dev->irq, dev->mem_start,
-		   (dev->mem_end - dev->mem_start + 1) / mirror_size,
-		   mirror_size);
-
-	err = register_netdev(dev);
-	if (err)
-		goto err_unmap;
-
-	return 0;
-
-err_unmap:
-	iounmap(lp->mem_start);
-err_release_mem:
-	release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
-err_free_irq:
-	free_irq(dev->irq, dev);
-	return -EIO;
-}
-
-/* Do a hardware reset on the card, and set up necessary registers.
- *
- * This should be called as little as possible, because it disrupts the
- * token on the network (causes a RECON) and requires a significant delay.
- *
- * However, it does make sure the card is in a defined state.
- */
-static int arcrimi_reset(struct net_device *dev, int really_reset)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	void __iomem *ioaddr = lp->mem_start + 0x800;
-
-	arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",
-		   dev->name, arcnet_readb(ioaddr, COM9026_REG_R_STATUS));
-
-	if (really_reset) {
-		arcnet_writeb(TESTvalue, ioaddr, -0x800);	/* fake reset */
-		return 0;
-	}
-	/* clear flags & end reset */
-	arcnet_writeb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
-	arcnet_writeb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
-
-	/* enable extended (512-byte) packets */
-	arcnet_writeb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
-
-	/* done!  return success. */
-	return 0;
-}
-
-static void arcrimi_setmask(struct net_device *dev, int mask)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	void __iomem *ioaddr = lp->mem_start + 0x800;
-
-	arcnet_writeb(mask, ioaddr, COM9026_REG_W_INTMASK);
-}
-
-static int arcrimi_status(struct net_device *dev)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	void __iomem *ioaddr = lp->mem_start + 0x800;
-
-	return arcnet_readb(ioaddr, COM9026_REG_R_STATUS);
-}
-
-static void arcrimi_command(struct net_device *dev, int cmd)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	void __iomem *ioaddr = lp->mem_start + 0x800;
-
-	arcnet_writeb(cmd, ioaddr, COM9026_REG_W_COMMAND);
-}
-
-static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
-				 void *buf, int count)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
-
-	TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count));
-}
-
-static void arcrimi_copy_from_card(struct net_device *dev, int bufnum,
-				   int offset, void *buf, int count)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
-
-	TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
-}
-
-static int node;
-static int io;			/* use the insmod io= irq= node= options */
-static int irq;
-static char device[9];		/* use eg. device=arc1 to change name */
-
-module_param(node, int, 0);
-module_param(io, int, 0);
-module_param(irq, int, 0);
-module_param_string(device, device, sizeof(device), 0);
-MODULE_DESCRIPTION("ARCnet COM90xx RIM I chipset driver");
-MODULE_LICENSE("GPL");
-
-static struct net_device *my_dev;
-
-static int __init arc_rimi_init(void)
-{
-	struct net_device *dev;
-
-	dev = alloc_arcdev(device);
-	if (!dev)
-		return -ENOMEM;
-
-	if (node && node != 0xff)
-		arcnet_set_addr(dev, node);
-
-	dev->mem_start = io;
-	dev->irq = irq;
-	if (dev->irq == 2)
-		dev->irq = 9;
-
-	if (arcrimi_probe(dev)) {
-		free_arcdev(dev);
-		return -EIO;
-	}
-
-	my_dev = dev;
-	return 0;
-}
-
-static void __exit arc_rimi_exit(void)
-{
-	struct net_device *dev = my_dev;
-	struct arcnet_local *lp = netdev_priv(dev);
-
-	unregister_netdev(dev);
-	iounmap(lp->mem_start);
-	release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
-	free_irq(dev->irq, dev);
-	free_arcdev(dev);
-}
-
-#ifndef MODULE
-static int __init arcrimi_setup(char *s)
-{
-	int ints[8];
-
-	s = get_options(s, 8, ints);
-	if (!ints[0])
-		return 1;
-	switch (ints[0]) {
-	default:		/* ERROR */
-		pr_err("Too many arguments\n");
-		fallthrough;
-	case 3:		/* Node ID */
-		node = ints[3];
-		fallthrough;
-	case 2:		/* IRQ */
-		irq = ints[2];
-		fallthrough;
-	case 1:		/* IO address */
-		io = ints[1];
-	}
-	if (*s)
-		snprintf(device, sizeof(device), "%s", s);
-	return 1;
-}
-__setup("arcrimi=", arcrimi_setup);
-#endif				/* MODULE */
-
-module_init(arc_rimi_init)
-module_exit(arc_rimi_exit)
diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c
deleted file mode 100644
index fef2ac2852a8..000000000000
--- a/drivers/net/arcnet/com20020-isa.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Linux ARCnet driver - COM20020 chipset support
- *
- * Written 1997 by David Woodhouse.
- * Written 1994-1999 by Avery Pennarun.
- * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
- * Derived from skeleton.c by Donald Becker.
- *
- * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
- *  for sponsoring the further development of this driver.
- *
- * **********************
- *
- * The original copyright of skeleton.c was as follows:
- *
- * skeleton.c Written 1993 by Donald Becker.
- * Copyright 1993 United States Government as represented by the
- * Director, National Security Agency.  This software may only be used
- * and distributed according to the terms of the GNU General Public License as
- * modified by SRC, incorporated herein by reference.
- *
- * **********************
- *
- * For more details, see drivers/net/arcnet.c
- *
- * **********************
- */
-
-#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/ioport.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/memblock.h>
-#include <linux/io.h>
-
-#include "arcdevice.h"
-#include "com20020.h"
-
-/* 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.
- */
-static int __init com20020isa_probe(struct net_device *dev)
-{
-	int ioaddr;
-	unsigned long airqmask;
-	struct arcnet_local *lp = netdev_priv(dev);
-	int err;
-
-	if (BUGLVL(D_NORMAL))
-		pr_info("%s\n", "COM20020 ISA support (by David Woodhouse et al.)");
-
-	ioaddr = dev->base_addr;
-	if (!ioaddr) {
-		arc_printk(D_NORMAL, dev, "No autoprobe (yet) for IO mapped cards; you must specify the base address!\n");
-		return -ENODEV;
-	}
-	if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "arcnet (COM20020)")) {
-		arc_printk(D_NORMAL, dev, "IO region %xh-%xh already allocated.\n",
-			   ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
-		return -ENXIO;
-	}
-	if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
-		arc_printk(D_NORMAL, dev, "IO address %x empty\n", ioaddr);
-		err = -ENODEV;
-		goto out;
-	}
-	if (com20020_check(dev)) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	if (!dev->irq) {
-		/* if we do this, we're sure to get an IRQ since the
-		 * card has just reset and the NORXflag is on until
-		 * we tell it to start receiving.
-		 */
-		arc_printk(D_INIT_REASONS, dev, "intmask was %02Xh\n",
-			   arcnet_inb(ioaddr, COM20020_REG_R_STATUS));
-		arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK);
-		airqmask = probe_irq_on();
-		arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK);
-		udelay(1);
-		arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK);
-		dev->irq = probe_irq_off(airqmask);
-
-		if ((int)dev->irq <= 0) {
-			arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed first time\n");
-			airqmask = probe_irq_on();
-			arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK);
-			udelay(5);
-			arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK);
-			dev->irq = probe_irq_off(airqmask);
-			if ((int)dev->irq <= 0) {
-				arc_printk(D_NORMAL, dev, "Autoprobe IRQ failed.\n");
-				err = -ENODEV;
-				goto out;
-			}
-		}
-	}
-
-	lp->card_name = "ISA COM20020";
-
-	err = com20020_found(dev, 0);
-	if (err != 0)
-		goto out;
-
-	return 0;
-
-out:
-	release_region(ioaddr, ARCNET_TOTAL_SIZE);
-	return err;
-}
-
-static int node = 0;
-static int io = 0x0;		/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
-static int irq = 0;		/* or use the insmod io= irq= shmem= options */
-static char device[9];		/* use eg. device="arc1" to change name */
-static int timeout = 3;
-static int backplane = 0;
-static int clockp = 0;
-static int clockm = 0;
-
-module_param(node, int, 0);
-module_param_hw(io, int, ioport, 0);
-module_param_hw(irq, int, irq, 0);
-module_param_string(device, device, sizeof(device), 0);
-module_param(timeout, int, 0);
-module_param(backplane, int, 0);
-module_param(clockp, int, 0);
-module_param(clockm, int, 0);
-
-MODULE_DESCRIPTION("ARCnet COM20020 chipset ISA driver");
-MODULE_LICENSE("GPL");
-
-static struct net_device *my_dev;
-
-static int __init com20020_init(void)
-{
-	struct net_device *dev;
-	struct arcnet_local *lp;
-
-	dev = alloc_arcdev(device);
-	if (!dev)
-		return -ENOMEM;
-
-	if (node && node != 0xff)
-		arcnet_set_addr(dev, node);
-
-	dev->netdev_ops = &com20020_netdev_ops;
-
-	lp = netdev_priv(dev);
-	lp->backplane = backplane;
-	lp->clockp = clockp & 7;
-	lp->clockm = clockm & 3;
-	lp->timeout = timeout & 3;
-	lp->hw.owner = THIS_MODULE;
-
-	dev->base_addr = io;
-	dev->irq = irq;
-
-	if (dev->irq == 2)
-		dev->irq = 9;
-
-	if (com20020isa_probe(dev)) {
-		free_arcdev(dev);
-		return -EIO;
-	}
-
-	my_dev = dev;
-	return 0;
-}
-
-static void __exit com20020_exit(void)
-{
-	unregister_netdev(my_dev);
-	free_irq(my_dev->irq, my_dev);
-	release_region(my_dev->base_addr, ARCNET_TOTAL_SIZE);
-	free_arcdev(my_dev);
-}
-
-#ifndef MODULE
-static int __init com20020isa_setup(char *s)
-{
-	int ints[8];
-
-	s = get_options(s, 8, ints);
-	if (!ints[0])
-		return 1;
-
-	switch (ints[0]) {
-	default:		/* ERROR */
-		pr_info("Too many arguments\n");
-		fallthrough;
-	case 6:		/* Timeout */
-		timeout = ints[6];
-		fallthrough;
-	case 5:		/* CKP value */
-		clockp = ints[5];
-		fallthrough;
-	case 4:		/* Backplane flag */
-		backplane = ints[4];
-		fallthrough;
-	case 3:		/* Node ID */
-		node = ints[3];
-		fallthrough;
-	case 2:		/* IRQ */
-		irq = ints[2];
-		fallthrough;
-	case 1:		/* IO address */
-		io = ints[1];
-	}
-	if (*s)
-		snprintf(device, sizeof(device), "%s", s);
-	return 1;
-}
-
-__setup("com20020=", com20020isa_setup);
-
-#endif				/* MODULE */
-
-module_init(com20020_init)
-module_exit(com20020_exit)
diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c
index 59a4748d051f..21f962357b0b 100644
--- a/drivers/net/arcnet/com20020.c
+++ b/drivers/net/arcnet/com20020.c
@@ -384,9 +384,7 @@ static void com20020_set_rx_mode(struct net_device *dev)
 	}
 }
 
-#if defined(CONFIG_ARCNET_COM20020_PCI_MODULE) || \
-    defined(CONFIG_ARCNET_COM20020_ISA_MODULE) || \
-    defined(CONFIG_ARCNET_COM20020_CS_MODULE)
+#ifdef CONFIG_ARCNET_COM20020_PCI_MODULE
 EXPORT_SYMBOL(com20020_check);
 EXPORT_SYMBOL(com20020_found);
 EXPORT_SYMBOL(com20020_netdev_ops);
diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c
deleted file mode 100644
index 5c3c91677b62..000000000000
--- a/drivers/net/arcnet/com20020_cs.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Linux ARCnet driver - COM20020 PCMCIA support
- *
- * Written 1994-1999 by Avery Pennarun,
- *    based on an ISA version by David Woodhouse.
- * Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4)
- *    which was derived from pcnet_cs.c by David Hinds.
- * Some additional portions derived from skeleton.c by Donald Becker.
- *
- * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
- *  for sponsoring the further development of this driver.
- *
- * **********************
- *
- * The original copyright of skeleton.c was as follows:
- *
- * skeleton.c Written 1993 by Donald Becker.
- * Copyright 1993 United States Government as represented by the
- * Director, National Security Agency.  This software may only be used
- * and distributed according to the terms of the GNU General Public License as
- * modified by SRC, incorporated herein by reference.
- *
- * **********************
- * Changes:
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000
- * - reorganize kmallocs in com20020_attach, checking all for failure
- *   and releasing the previous allocations if one fails
- * **********************
- *
- * For more details, see drivers/net/arcnet.c
- *
- * **********************
- */
-
-#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/io.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ds.h>
-
-#include "arcdevice.h"
-#include "com20020.h"
-
-static void regdump(struct net_device *dev)
-{
-#ifdef DEBUG
-	int ioaddr = dev->base_addr;
-	int count;
-
-	netdev_dbg(dev, "register dump:\n");
-	for (count = 0; count < 16; count++) {
-		if (!(count % 16))
-			pr_cont("%04X:", ioaddr + count);
-		pr_cont(" %02X", arcnet_inb(ioaddr, count));
-	}
-	pr_cont("\n");
-
-	netdev_dbg(dev, "buffer0 dump:\n");
-	/* set up the address register */
-	count = 0;
-	arcnet_outb((count >> 8) | RDDATAflag | AUTOINCflag,
-		    ioaddr, COM20020_REG_W_ADDR_HI);
-	arcnet_outb(count & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
-
-	for (count = 0; count < 256 + 32; count++) {
-		if (!(count % 16))
-			pr_cont("%04X:", count);
-
-		/* copy the data */
-		pr_cont(" %02X", arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA));
-	}
-	pr_cont("\n");
-#endif
-}
-
-/*====================================================================*/
-
-/* Parameters that can be set with 'insmod' */
-
-static int node;
-static int timeout = 3;
-static int backplane;
-static int clockp;
-static int clockm;
-
-module_param(node, int, 0);
-module_param(timeout, int, 0);
-module_param(backplane, int, 0);
-module_param(clockp, int, 0);
-module_param(clockm, int, 0);
-
-MODULE_DESCRIPTION("ARCnet COM20020 chipset PCMCIA driver");
-MODULE_LICENSE("GPL");
-
-/*====================================================================*/
-
-static int com20020_config(struct pcmcia_device *link);
-static void com20020_release(struct pcmcia_device *link);
-
-static void com20020_detach(struct pcmcia_device *p_dev);
-
-/*====================================================================*/
-
-static int com20020_probe(struct pcmcia_device *p_dev)
-{
-	struct com20020_dev *info;
-	struct net_device *dev;
-	struct arcnet_local *lp;
-	int ret = -ENOMEM;
-
-	dev_dbg(&p_dev->dev, "com20020_attach()\n");
-
-	/* Create new network device */
-	info = kzalloc_obj(*info);
-	if (!info)
-		goto fail_alloc_info;
-
-	dev = alloc_arcdev("");
-	if (!dev)
-		goto fail_alloc_dev;
-
-	lp = netdev_priv(dev);
-	lp->timeout = timeout;
-	lp->backplane = backplane;
-	lp->clockp = clockp;
-	lp->clockm = clockm & 3;
-	lp->hw.owner = THIS_MODULE;
-
-	/* fill in our module parameters as defaults */
-	arcnet_set_addr(dev, node);
-
-	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
-	p_dev->resource[0]->end = 16;
-	p_dev->config_flags |= CONF_ENABLE_IRQ;
-
-	info->dev = dev;
-	p_dev->priv = info;
-
-	ret = com20020_config(p_dev);
-	if (ret)
-		goto fail_config;
-
-	return 0;
-
-fail_config:
-	free_arcdev(dev);
-fail_alloc_dev:
-	kfree(info);
-fail_alloc_info:
-	return ret;
-} /* com20020_attach */
-
-static void com20020_detach(struct pcmcia_device *link)
-{
-	struct com20020_dev *info = link->priv;
-	struct net_device *dev = info->dev;
-
-	dev_dbg(&link->dev, "detach...\n");
-
-	dev_dbg(&link->dev, "com20020_detach\n");
-
-	dev_dbg(&link->dev, "unregister...\n");
-
-	unregister_netdev(dev);
-
-	/* this is necessary because we register our IRQ separately
-	 * from card services.
-	 */
-	if (dev->irq)
-		free_irq(dev->irq, dev);
-
-	com20020_release(link);
-
-	/* Unlink device structure, free bits */
-	dev_dbg(&link->dev, "unlinking...\n");
-	if (link->priv) {
-		dev = info->dev;
-		if (dev) {
-			dev_dbg(&link->dev, "kfree...\n");
-			free_arcdev(dev);
-		}
-		dev_dbg(&link->dev, "kfree2...\n");
-		kfree(info);
-	}
-
-} /* com20020_detach */
-
-static int com20020_config(struct pcmcia_device *link)
-{
-	struct arcnet_local *lp;
-	struct com20020_dev *info;
-	struct net_device *dev;
-	int i, ret;
-	int ioaddr;
-
-	info = link->priv;
-	dev = info->dev;
-
-	dev_dbg(&link->dev, "config...\n");
-
-	dev_dbg(&link->dev, "com20020_config\n");
-
-	dev_dbg(&link->dev, "baseport1 is %Xh\n",
-		(unsigned int)link->resource[0]->start);
-
-	i = -ENODEV;
-	link->io_lines = 16;
-
-	if (!link->resource[0]->start) {
-		for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) {
-			link->resource[0]->start = ioaddr;
-			i = pcmcia_request_io(link);
-			if (i == 0)
-				break;
-		}
-	} 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",
-		link->irq);
-	if (!link->irq) {
-		dev_dbg(&link->dev, "requestIRQ failed totally!\n");
-		goto failed;
-	}
-
-	dev->irq = link->irq;
-
-	ret = pcmcia_enable_device(link);
-	if (ret)
-		goto failed;
-
-	if (com20020_check(dev)) {
-		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);
-
-	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:
-	dev_dbg(&link->dev, "com20020_config failed...\n");
-	com20020_release(link);
-	return -ENODEV;
-} /* com20020_config */
-
-static void com20020_release(struct pcmcia_device *link)
-{
-	dev_dbg(&link->dev, "com20020_release\n");
-	pcmcia_disable_device(link);
-}
-
-static int com20020_suspend(struct pcmcia_device *link)
-{
-	struct com20020_dev *info = link->priv;
-	struct net_device *dev = info->dev;
-
-	if (link->open)
-		netif_device_detach(dev);
-
-	return 0;
-}
-
-static int com20020_resume(struct pcmcia_device *link)
-{
-	struct com20020_dev *info = link->priv;
-	struct net_device *dev = info->dev;
-
-	if (link->open) {
-		int ioaddr = dev->base_addr;
-		struct arcnet_local *lp = netdev_priv(dev);
-
-		arcnet_outb(lp->config | 0x80, ioaddr, COM20020_REG_W_CONFIG);
-		udelay(5);
-		arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
-	}
-
-	return 0;
-}
-
-static const struct pcmcia_device_id com20020_ids[] = {
-	PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
-				"PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
-	PCMCIA_DEVICE_PROD_ID12("SoHard AG",
-				"SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
-	PCMCIA_DEVICE_NULL
-};
-MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
-
-static struct pcmcia_driver com20020_cs_driver = {
-	.owner		= THIS_MODULE,
-	.name		= "com20020_cs",
-	.probe		= com20020_probe,
-	.remove		= com20020_detach,
-	.id_table	= com20020_ids,
-	.suspend	= com20020_suspend,
-	.resume		= com20020_resume,
-};
-module_pcmcia_driver(com20020_cs_driver);
diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c
deleted file mode 100644
index 3b463fbc6402..000000000000
--- a/drivers/net/arcnet/com90io.c
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Linux ARCnet driver - COM90xx chipset (IO-mapped buffers)
- *
- * Written 1997 by David Woodhouse.
- * Written 1994-1999 by Avery Pennarun.
- * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
- * Derived from skeleton.c by Donald Becker.
- *
- * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
- *  for sponsoring the further development of this driver.
- *
- * **********************
- *
- * The original copyright of skeleton.c was as follows:
- *
- * skeleton.c Written 1993 by Donald Becker.
- * Copyright 1993 United States Government as represented by the
- * Director, National Security Agency.  This software may only be used
- * and distributed according to the terms of the GNU General Public License as
- * modified by SRC, incorporated herein by reference.
- *
- * **********************
- *
- * For more details, see drivers/net/arcnet.c
- *
- * **********************
- */
-
-#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/memblock.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-
-#include "arcdevice.h"
-#include "com9026.h"
-
-/* Internal function declarations */
-
-static int com90io_found(struct net_device *dev);
-static void com90io_command(struct net_device *dev, int command);
-static int com90io_status(struct net_device *dev);
-static void com90io_setmask(struct net_device *dev, int mask);
-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,
-				 void *buf, int count);
-static void com90io_copy_from_card(struct net_device *dev, int bufnum,
-				   int offset, void *buf, int count);
-
-/* Handy defines for ARCnet specific stuff */
-
-/* The number of low I/O ports used by the card. */
-#define ARCNET_TOTAL_SIZE 16
-
-/****************************************************************************
- *                                                                          *
- * IO-mapped operation routines                                             *
- *                                                                          *
- ****************************************************************************/
-
-#undef ONE_AT_A_TIME_TX
-#undef ONE_AT_A_TIME_RX
-
-static u_char get_buffer_byte(struct net_device *dev, unsigned offset)
-{
-	int ioaddr = dev->base_addr;
-
-	arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);
-	arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);
-
-	return arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);
-}
-
-#ifdef ONE_AT_A_TIME_TX
-static void put_buffer_byte(struct net_device *dev, unsigned offset,
-			    u_char datum)
-{
-	int ioaddr = dev->base_addr;
-
-	arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);
-	arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);
-
-	arcnet_outb(datum, ioaddr, COM9026_REG_RW_MEMDATA);
-}
-
-#endif
-
-static void get_whole_buffer(struct net_device *dev, unsigned offset,
-			     unsigned length, char *dest)
-{
-	int ioaddr = dev->base_addr;
-
-	arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);
-	arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);
-
-	while (length--)
-#ifdef ONE_AT_A_TIME_RX
-		*(dest++) = get_buffer_byte(dev, offset++);
-#else
-		*(dest++) = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);
-#endif
-}
-
-static void put_whole_buffer(struct net_device *dev, unsigned offset,
-			     unsigned length, char *dest)
-{
-	int ioaddr = dev->base_addr;
-
-	arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);
-	arcnet_outb(offset & 0xff, ioaddr,COM9026_REG_W_ADDR_LO);
-
-	while (length--)
-#ifdef ONE_AT_A_TIME_TX
-		put_buffer_byte(dev, offset++, *(dest++));
-#else
-		arcnet_outb(*(dest++), ioaddr, COM9026_REG_RW_MEMDATA);
-#endif
-}
-
-/* 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
- */
-static int __init com90io_probe(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr, status;
-	unsigned long airqmask;
-
-	if (BUGLVL(D_NORMAL)) {
-		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) {
-		arc_printk(D_NORMAL, dev, "No autoprobe for IO mapped cards; you must specify the base address!\n");
-		return -ENODEV;
-	}
-	if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) {
-		arc_printk(D_INIT_REASONS, dev, "IO request_region %x-%x failed\n",
-			   ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
-		return -ENXIO;
-	}
-	if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {
-		arc_printk(D_INIT_REASONS, dev, "IO address %x empty\n",
-			   ioaddr);
-		goto err_out;
-	}
-	arcnet_inb(ioaddr, COM9026_REG_R_RESET);
-	mdelay(RESETtime);
-
-	status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
-
-	if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
-		arc_printk(D_INIT_REASONS, dev, "Status invalid (%Xh)\n",
-			   status);
-		goto err_out;
-	}
-	arc_printk(D_INIT_REASONS, dev, "Status after reset: %X\n", status);
-
-	arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
-		    ioaddr, COM9026_REG_W_COMMAND);
-
-	arc_printk(D_INIT_REASONS, dev, "Status after reset acknowledged: %X\n",
-		   status);
-
-	status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
-
-	if (status & RESETflag) {
-		arc_printk(D_INIT_REASONS, dev, "Eternal reset (status=%Xh)\n",
-			   status);
-		goto err_out;
-	}
-	arcnet_outb((0x16 | IOMAPflag) & ~ENABLE16flag,
-		    ioaddr, COM9026_REG_RW_CONFIG);
-
-	/* Read first loc'n of memory */
-
-	arcnet_outb(AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);
-	arcnet_outb(0, ioaddr,  COM9026_REG_W_ADDR_LO);
-
-	status = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);
-	if (status != 0xd1) {
-		arc_printk(D_INIT_REASONS, dev, "Signature byte not found (%Xh instead).\n",
-			   status);
-		goto err_out;
-	}
-	if (!dev->irq) {
-		/* if we do this, we're sure to get an IRQ since the
-		 * card has just reset and the NORXflag is on until
-		 * we tell it to start receiving.
-		 */
-
-		airqmask = probe_irq_on();
-		arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);
-		udelay(1);
-		arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);
-		dev->irq = probe_irq_off(airqmask);
-
-		if ((int)dev->irq <= 0) {
-			arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed\n");
-			goto err_out;
-		}
-	}
-	release_region(ioaddr, ARCNET_TOTAL_SIZE); /* end of probing */
-	return com90io_found(dev);
-
-err_out:
-	release_region(ioaddr, ARCNET_TOTAL_SIZE);
-	return -ENODEV;
-}
-
-/* Set up the struct net_device associated with this card.  Called after
- * probing succeeds.
- */
-static int __init com90io_found(struct net_device *dev)
-{
-	struct arcnet_local *lp;
-	int ioaddr = dev->base_addr;
-	int err;
-
-	/* Reserve the irq */
-	if (request_irq(dev->irq, arcnet_interrupt, 0,
-			"arcnet (COM90xx-IO)", dev)) {
-		arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);
-		return -ENODEV;
-	}
-	/* Reserve the I/O region */
-	if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE,
-			    "arcnet (COM90xx-IO)")) {
-		free_irq(dev->irq, dev);
-		return -EBUSY;
-	}
-
-	lp = netdev_priv(dev);
-	lp->card_name = "COM90xx I/O";
-	lp->hw.command = com90io_command;
-	lp->hw.status = com90io_status;
-	lp->hw.intmask = com90io_setmask;
-	lp->hw.reset = com90io_reset;
-	lp->hw.owner = THIS_MODULE;
-	lp->hw.copy_to_card = com90io_copy_to_card;
-	lp->hw.copy_from_card = com90io_copy_from_card;
-
-	lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag;
-	arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);
-
-	/* get and check the station ID from offset 1 in shmem */
-
-	arcnet_set_addr(dev, get_buffer_byte(dev, 1));
-
-	err = register_netdev(dev);
-	if (err) {
-		arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag,
-			    ioaddr, COM9026_REG_RW_CONFIG);
-		free_irq(dev->irq, dev);
-		release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
-		return err;
-	}
-
-	arc_printk(D_NORMAL, dev, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n",
-		   dev->dev_addr[0], dev->base_addr, dev->irq);
-
-	return 0;
-}
-
-/* Do a hardware reset on the card, and set up necessary registers.
- *
- * This should be called as little as possible, because it disrupts the
- * token on the network (causes a RECON) and requires a significant delay.
- *
- * However, it does make sure the card is in a defined state.
- */
-static int com90io_reset(struct net_device *dev, int really_reset)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	short ioaddr = dev->base_addr;
-
-	arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",
-		   dev->name, arcnet_inb(ioaddr, COM9026_REG_R_STATUS));
-
-	if (really_reset) {
-		/* reset the card */
-		arcnet_inb(ioaddr, COM9026_REG_R_RESET);
-		mdelay(RESETtime);
-	}
-	/* Set the thing to IO-mapped, 8-bit  mode */
-	lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag;
-	arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);
-
-	arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
-					/* clear flags & end reset */
-	arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
-
-	/* verify that the ARCnet signature byte is present */
-	if (get_buffer_byte(dev, 0) != TESTvalue) {
-		arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
-		return 1;
-	}
-	/* enable extended (512-byte) packets */
-	arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
-	/* done!  return success. */
-	return 0;
-}
-
-static void com90io_command(struct net_device *dev, int cmd)
-{
-	short ioaddr = dev->base_addr;
-
-	arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);
-}
-
-static int com90io_status(struct net_device *dev)
-{
-	short ioaddr = dev->base_addr;
-
-	return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
-}
-
-static void com90io_setmask(struct net_device *dev, int mask)
-{
-	short ioaddr = dev->base_addr;
-
-	arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);
-}
-
-static void com90io_copy_to_card(struct net_device *dev, int bufnum,
-				 int offset, void *buf, int count)
-{
-	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,
-				   int offset, void *buf, int count)
-{
-	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 irq;
-static char device[9];		/* use eg. device=arc1 to change name */
-
-module_param_hw(io, int, ioport, 0);
-module_param_hw(irq, int, irq, 0);
-module_param_string(device, device, sizeof(device), 0);
-MODULE_DESCRIPTION("ARCnet COM90xx IO mapped chipset driver");
-MODULE_LICENSE("GPL");
-
-#ifndef MODULE
-static int __init com90io_setup(char *s)
-{
-	int ints[4];
-
-	s = get_options(s, 4, ints);
-	if (!ints[0])
-		return 0;
-	switch (ints[0]) {
-	default:		/* ERROR */
-		pr_err("Too many arguments\n");
-		fallthrough;
-	case 2:		/* IRQ */
-		irq = ints[2];
-		fallthrough;
-	case 1:		/* IO address */
-		io = ints[1];
-	}
-	if (*s)
-		snprintf(device, sizeof(device), "%s", s);
-	return 1;
-}
-__setup("com90io=", com90io_setup);
-#endif
-
-static struct net_device *my_dev;
-
-static int __init com90io_init(void)
-{
-	struct net_device *dev;
-	int err;
-
-	dev = alloc_arcdev(device);
-	if (!dev)
-		return -ENOMEM;
-
-	dev->base_addr = io;
-	dev->irq = irq;
-	if (dev->irq == 2)
-		dev->irq = 9;
-
-	err = com90io_probe(dev);
-
-	if (err) {
-		free_arcdev(dev);
-		return err;
-	}
-
-	my_dev = dev;
-	return 0;
-}
-
-static void __exit com90io_exit(void)
-{
-	struct net_device *dev = my_dev;
-	int ioaddr = dev->base_addr;
-
-	unregister_netdev(dev);
-
-	/* In case the old driver is loaded later,
-	 * 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);
-	release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
-	free_arcdev(dev);
-}
-
-module_init(com90io_init)
-module_exit(com90io_exit)
diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c
deleted file mode 100644
index b3b287c16561..000000000000
--- a/drivers/net/arcnet/com90xx.c
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- * Linux ARCnet driver - COM90xx chipset (memory-mapped buffers)
- *
- * Written 1994-1999 by Avery Pennarun.
- * Written 1999 by Martin Mares <mj@ucw.cz>.
- * Derived from skeleton.c by Donald Becker.
- *
- * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
- *  for sponsoring the further development of this driver.
- *
- * **********************
- *
- * The original copyright of skeleton.c was as follows:
- *
- * skeleton.c Written 1993 by Donald Becker.
- * Copyright 1993 United States Government as represented by the
- * Director, National Security Agency.  This software may only be used
- * and distributed according to the terms of the GNU General Public License as
- * modified by SRC, incorporated herein by reference.
- *
- * **********************
- *
- * For more details, see drivers/net/arcnet.c
- *
- * **********************
- */
-
-#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-
-#include "arcdevice.h"
-#include "com9026.h"
-
-/* 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
- * other.
- *
- * This is undefined by default because it might not always be true, and the
- * extra check makes the autoprobe even more careful.  Speed demons can turn
- * it on - I think it should be fine if you only have one ARCnet card
- * installed.
- *
- * If no ARCnet cards are installed, this delay never happens anyway and thus
- * the option has no effect.
- */
-#undef FAST_PROBE
-
-/* Internal function declarations */
-static int com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *);
-static void com90xx_command(struct net_device *dev, int command);
-static int com90xx_status(struct net_device *dev);
-static void com90xx_setmask(struct net_device *dev, int mask);
-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,
-				 void *buf, int count);
-static void com90xx_copy_from_card(struct net_device *dev, int bufnum,
-				   int offset, void *buf, int count);
-
-/* Known ARCnet cards */
-
-static struct net_device *cards[16];
-static int numcards;
-
-/* Handy defines for ARCnet specific stuff */
-
-/* The number of low I/O ports used by the card */
-#define ARCNET_TOTAL_SIZE	16
-
-/* Amount of I/O memory used by the card */
-#define BUFFER_SIZE (512)
-#define MIRROR_SIZE (BUFFER_SIZE * 4)
-
-static int com90xx_skip_probe __initdata = 0;
-
-/* Module parameters */
-
-static int io;			/* use the insmod io= irq= shmem= options */
-static int irq;
-static int shmem;
-static char device[9];		/* use eg. device=arc1 to change name */
-
-module_param_hw(io, int, ioport, 0);
-module_param_hw(irq, int, irq, 0);
-module_param(shmem, int, 0);
-module_param_string(device, device, sizeof(device), 0);
-
-static void __init com90xx_probe(void)
-{
-	int count, status, ioaddr, numprint, airq, openparen = 0;
-	unsigned long airqmask;
-	int ports[(0x3f0 - 0x200) / 16 + 1] = {	0 };
-	unsigned long *shmems;
-	void __iomem **iomem;
-	int numports, numshmems, *port;
-	u_long *p;
-	int index;
-
-	if (!io && !irq && !shmem && !*device && com90xx_skip_probe)
-		return;
-
-	shmems = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(unsigned long),
-			 GFP_KERNEL);
-	if (!shmems)
-		return;
-	iomem = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(void __iomem *),
-			GFP_KERNEL);
-	if (!iomem) {
-		kfree(shmems);
-		return;
-	}
-
-	if (BUGLVL(D_NORMAL))
-		pr_info("%s\n", "COM90xx chipset support");
-
-	/* set up the arrays where we'll store the possible probe addresses */
-	numports = numshmems = 0;
-	if (io)
-		ports[numports++] = io;
-	else
-		for (count = 0x200; count <= 0x3f0; count += 16)
-			ports[numports++] = count;
-	if (shmem)
-		shmems[numshmems++] = shmem;
-	else
-		for (count = 0xA0000; count <= 0xFF800; count += 2048)
-			shmems[numshmems++] = count;
-
-	/* Stage 1: abandon any reserved ports, or ones with status==0xFF
-	 * (empty), and reset any others by reading the reset port.
-	 */
-	numprint = -1;
-	for (port = &ports[0]; port - ports < numports; port++) {
-		numprint++;
-		numprint %= 8;
-		if (!numprint) {
-			arc_cont(D_INIT, "\n");
-			arc_cont(D_INIT, "S1: ");
-		}
-		arc_cont(D_INIT, "%Xh ", *port);
-
-		ioaddr = *port;
-
-		if (!request_region(*port, ARCNET_TOTAL_SIZE,
-				    "arcnet (90xx)")) {
-			arc_cont(D_INIT_REASONS, "(request_region)\n");
-			arc_cont(D_INIT_REASONS, "S1: ");
-			if (BUGLVL(D_INIT_REASONS))
-				numprint = 0;
-			*port-- = ports[--numports];
-			continue;
-		}
-		if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {
-			arc_cont(D_INIT_REASONS, "(empty)\n");
-			arc_cont(D_INIT_REASONS, "S1: ");
-			if (BUGLVL(D_INIT_REASONS))
-				numprint = 0;
-			release_region(*port, ARCNET_TOTAL_SIZE);
-			*port-- = ports[--numports];
-			continue;
-		}
-		/* begin resetting card */
-		arcnet_inb(ioaddr, COM9026_REG_R_RESET);
-
-		arc_cont(D_INIT_REASONS, "\n");
-		arc_cont(D_INIT_REASONS, "S1: ");
-		if (BUGLVL(D_INIT_REASONS))
-			numprint = 0;
-	}
-	arc_cont(D_INIT, "\n");
-
-	if (!numports) {
-		arc_cont(D_NORMAL, "S1: No ARCnet cards found.\n");
-		kfree(shmems);
-		kfree(iomem);
-		return;
-	}
-	/* Stage 2: we have now reset any possible ARCnet cards, so we can't
-	 * do anything until they finish.  If D_INIT, print the list of
-	 * cards that are left.
-	 */
-	numprint = -1;
-	for (port = &ports[0]; port < ports + numports; port++) {
-		numprint++;
-		numprint %= 8;
-		if (!numprint) {
-			arc_cont(D_INIT, "\n");
-			arc_cont(D_INIT, "S2: ");
-		}
-		arc_cont(D_INIT, "%Xh ", *port);
-	}
-	arc_cont(D_INIT, "\n");
-	mdelay(RESETtime);
-
-	/* Stage 3: abandon any shmem addresses that don't have the signature
-	 * 0xD1 byte in the right place, or are read-only.
-	 */
-	numprint = -1;
-	for (index = 0, p = &shmems[0]; index < numshmems; p++, index++) {
-		void __iomem *base;
-
-		numprint++;
-		numprint %= 8;
-		if (!numprint) {
-			arc_cont(D_INIT, "\n");
-			arc_cont(D_INIT, "S3: ");
-		}
-		arc_cont(D_INIT, "%lXh ", *p);
-
-		if (!request_mem_region(*p, MIRROR_SIZE, "arcnet (90xx)")) {
-			arc_cont(D_INIT_REASONS, "(request_mem_region)\n");
-			arc_cont(D_INIT_REASONS, "Stage 3: ");
-			if (BUGLVL(D_INIT_REASONS))
-				numprint = 0;
-			goto out;
-		}
-		base = ioremap(*p, MIRROR_SIZE);
-		if (!base) {
-			arc_cont(D_INIT_REASONS, "(ioremap)\n");
-			arc_cont(D_INIT_REASONS, "Stage 3: ");
-			if (BUGLVL(D_INIT_REASONS))
-				numprint = 0;
-			goto out1;
-		}
-		if (arcnet_readb(base, COM9026_REG_R_STATUS) != TESTvalue) {
-			arc_cont(D_INIT_REASONS, "(%02Xh != %02Xh)\n",
-				 arcnet_readb(base, COM9026_REG_R_STATUS),
-				 TESTvalue);
-			arc_cont(D_INIT_REASONS, "S3: ");
-			if (BUGLVL(D_INIT_REASONS))
-				numprint = 0;
-			goto out2;
-		}
-		/* By writing 0x42 to the TESTvalue location, we also make
-		 * sure no "mirror" shmem areas show up - if they occur
-		 * in another pass through this loop, they will be discarded
-		 * because *cptr != TESTvalue.
-		 */
-		arcnet_writeb(0x42, base, COM9026_REG_W_INTMASK);
-		if (arcnet_readb(base, COM9026_REG_R_STATUS) != 0x42) {
-			arc_cont(D_INIT_REASONS, "(read only)\n");
-			arc_cont(D_INIT_REASONS, "S3: ");
-			goto out2;
-		}
-		arc_cont(D_INIT_REASONS, "\n");
-		arc_cont(D_INIT_REASONS, "S3: ");
-		if (BUGLVL(D_INIT_REASONS))
-			numprint = 0;
-		iomem[index] = base;
-		continue;
-	out2:
-		iounmap(base);
-	out1:
-		release_mem_region(*p, MIRROR_SIZE);
-	out:
-		*p-- = shmems[--numshmems];
-		index--;
-	}
-	arc_cont(D_INIT, "\n");
-
-	if (!numshmems) {
-		arc_cont(D_NORMAL, "S3: No ARCnet cards found.\n");
-		for (port = &ports[0]; port < ports + numports; port++)
-			release_region(*port, ARCNET_TOTAL_SIZE);
-		kfree(shmems);
-		kfree(iomem);
-		return;
-	}
-	/* Stage 4: something of a dummy, to report the shmems that are
-	 * still possible after stage 3.
-	 */
-	numprint = -1;
-	for (p = &shmems[0]; p < shmems + numshmems; p++) {
-		numprint++;
-		numprint %= 8;
-		if (!numprint) {
-			arc_cont(D_INIT, "\n");
-			arc_cont(D_INIT, "S4: ");
-		}
-		arc_cont(D_INIT, "%lXh ", *p);
-	}
-	arc_cont(D_INIT, "\n");
-
-	/* Stage 5: for any ports that have the correct status, can disable
-	 * the RESET flag, and (if no irq is given) generate an autoirq,
-	 * register an ARCnet device.
-	 *
-	 * Currently, we can only register one device per probe, so quit
-	 * after the first one is found.
-	 */
-	numprint = -1;
-	for (port = &ports[0]; port < ports + numports; port++) {
-		int found = 0;
-
-		numprint++;
-		numprint %= 8;
-		if (!numprint) {
-			arc_cont(D_INIT, "\n");
-			arc_cont(D_INIT, "S5: ");
-		}
-		arc_cont(D_INIT, "%Xh ", *port);
-
-		ioaddr = *port;
-		status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
-
-		if ((status & 0x9D)
-		    != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
-			arc_cont(D_INIT_REASONS, "(status=%Xh)\n", status);
-			arc_cont(D_INIT_REASONS, "S5: ");
-			if (BUGLVL(D_INIT_REASONS))
-				numprint = 0;
-			release_region(*port, ARCNET_TOTAL_SIZE);
-			*port-- = ports[--numports];
-			continue;
-		}
-		arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
-			    ioaddr, COM9026_REG_W_COMMAND);
-		status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
-		if (status & RESETflag) {
-			arc_cont(D_INIT_REASONS, " (eternal reset, status=%Xh)\n",
-				 status);
-			arc_cont(D_INIT_REASONS, "S5: ");
-			if (BUGLVL(D_INIT_REASONS))
-				numprint = 0;
-			release_region(*port, ARCNET_TOTAL_SIZE);
-			*port-- = ports[--numports];
-			continue;
-		}
-		/* skip this completely if an IRQ was given, because maybe
-		 * we're on a machine that locks during autoirq!
-		 */
-		if (!irq) {
-			/* if we do this, we're sure to get an IRQ since the
-			 * card has just reset and the NORXflag is on until
-			 * we tell it to start receiving.
-			 */
-			airqmask = probe_irq_on();
-			arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);
-			udelay(1);
-			arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);
-			airq = probe_irq_off(airqmask);
-
-			if (airq <= 0) {
-				arc_cont(D_INIT_REASONS, "(airq=%d)\n", airq);
-				arc_cont(D_INIT_REASONS, "S5: ");
-				if (BUGLVL(D_INIT_REASONS))
-					numprint = 0;
-				release_region(*port, ARCNET_TOTAL_SIZE);
-				*port-- = ports[--numports];
-				continue;
-			}
-		} else {
-			airq = irq;
-		}
-
-		arc_cont(D_INIT, "(%d,", airq);
-		openparen = 1;
-
-		/* Everything seems okay.  But which shmem, if any, puts
-		 * back its signature byte when the card is reset?
-		 *
-		 * If there are multiple cards installed, there might be
-		 * multiple shmems still in the list.
-		 */
-#ifdef FAST_PROBE
-		if (numports > 1 || numshmems > 1) {
-			arcnet_inb(ioaddr, COM9026_REG_R_RESET);
-			mdelay(RESETtime);
-		} else {
-			/* just one shmem and port, assume they match */
-			arcnet_writeb(TESTvalue, iomem[0],
-				      COM9026_REG_W_INTMASK);
-		}
-#else
-		arcnet_inb(ioaddr, COM9026_REG_R_RESET);
-		mdelay(RESETtime);
-#endif
-
-		for (index = 0; index < numshmems; index++) {
-			u_long ptr = shmems[index];
-			void __iomem *base = iomem[index];
-
-			if (arcnet_readb(base, COM9026_REG_R_STATUS) == TESTvalue) {	/* found one */
-				arc_cont(D_INIT, "%lXh)\n", *p);
-				openparen = 0;
-
-				/* register the card */
-				if (com90xx_found(*port, airq, ptr, base) == 0)
-					found = 1;
-				numprint = -1;
-
-				/* remove shmem from the list */
-				shmems[index] = shmems[--numshmems];
-				iomem[index] = iomem[numshmems];
-				break;	/* go to the next I/O port */
-			} else {
-				arc_cont(D_INIT_REASONS, "%Xh-",
-					 arcnet_readb(base, COM9026_REG_R_STATUS));
-			}
-		}
-
-		if (openparen) {
-			if (BUGLVL(D_INIT))
-				pr_cont("no matching shmem)\n");
-			if (BUGLVL(D_INIT_REASONS)) {
-				pr_cont("S5: ");
-				numprint = 0;
-			}
-		}
-		if (!found)
-			release_region(*port, ARCNET_TOTAL_SIZE);
-		*port-- = ports[--numports];
-	}
-
-	if (BUGLVL(D_INIT_REASONS))
-		pr_cont("\n");
-
-	/* Now put back TESTvalue on all leftover shmems. */
-	for (index = 0; index < numshmems; index++) {
-		arcnet_writeb(TESTvalue, iomem[index], COM9026_REG_W_INTMASK);
-		iounmap(iomem[index]);
-		release_mem_region(shmems[index], MIRROR_SIZE);
-	}
-	kfree(shmems);
-	kfree(iomem);
-}
-
-static int __init check_mirror(unsigned long addr, size_t size)
-{
-	void __iomem *p;
-	int res = -1;
-
-	if (!request_mem_region(addr, size, "arcnet (90xx)"))
-		return -1;
-
-	p = ioremap(addr, size);
-	if (p) {
-		if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue)
-			res = 1;
-		else
-			res = 0;
-		iounmap(p);
-	}
-
-	release_mem_region(addr, size);
-	return res;
-}
-
-/* Set up the struct net_device associated with this card.  Called after
- * probing succeeds.
- */
-static int __init com90xx_found(int ioaddr, int airq, u_long shmem,
-				void __iomem *p)
-{
-	struct net_device *dev = NULL;
-	struct arcnet_local *lp;
-	u_long first_mirror, last_mirror;
-	int mirror_size;
-
-	/* allocate struct net_device */
-	dev = alloc_arcdev(device);
-	if (!dev) {
-		arc_cont(D_NORMAL, "com90xx: Can't allocate device!\n");
-		iounmap(p);
-		release_mem_region(shmem, MIRROR_SIZE);
-		return -ENOMEM;
-	}
-	lp = netdev_priv(dev);
-	/* find the real shared memory start/end points, including mirrors */
-
-	/* guess the actual size of one "memory mirror" - the number of
-	 * bytes between copies of the shared memory.  On most cards, it's
-	 * 2k (or there are no mirrors at all) but on some, it's 4k.
-	 */
-	mirror_size = MIRROR_SIZE;
-	if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue &&
-	    check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
-	    check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
-		mirror_size = 2 * MIRROR_SIZE;
-
-	first_mirror = shmem - mirror_size;
-	while (check_mirror(first_mirror, mirror_size) == 1)
-		first_mirror -= mirror_size;
-	first_mirror += mirror_size;
-
-	last_mirror = shmem + mirror_size;
-	while (check_mirror(last_mirror, mirror_size) == 1)
-		last_mirror += mirror_size;
-	last_mirror -= mirror_size;
-
-	dev->mem_start = first_mirror;
-	dev->mem_end = last_mirror + MIRROR_SIZE - 1;
-
-	iounmap(p);
-	release_mem_region(shmem, MIRROR_SIZE);
-
-	if (!request_mem_region(dev->mem_start,
-				dev->mem_end - dev->mem_start + 1,
-				"arcnet (90xx)"))
-		goto err_free_dev;
-
-	/* reserve the irq */
-	if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) {
-		arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", airq);
-		goto err_release_mem;
-	}
-	dev->irq = airq;
-
-	/* Initialize the rest of the device structure. */
-	lp->card_name = "COM90xx";
-	lp->hw.command = com90xx_command;
-	lp->hw.status = com90xx_status;
-	lp->hw.intmask = com90xx_setmask;
-	lp->hw.reset = com90xx_reset;
-	lp->hw.owner = THIS_MODULE;
-	lp->hw.copy_to_card = com90xx_copy_to_card;
-	lp->hw.copy_from_card = com90xx_copy_from_card;
-	lp->mem_start = ioremap(dev->mem_start,
-				dev->mem_end - dev->mem_start + 1);
-	if (!lp->mem_start) {
-		arc_printk(D_NORMAL, dev, "Can't remap device memory!\n");
-		goto err_free_irq;
-	}
-
-	/* get and check the station ID from offset 1 in shmem */
-	arcnet_set_addr(dev, arcnet_readb(lp->mem_start,
-					  COM9026_REG_R_STATION));
-
-	dev->base_addr = ioaddr;
-
-	arc_printk(D_NORMAL, dev, "COM90xx station %02Xh found at %03lXh, IRQ %d, ShMem %lXh (%ld*%xh).\n",
-		   dev->dev_addr[0],
-		   dev->base_addr, dev->irq, dev->mem_start,
-		   (dev->mem_end - dev->mem_start + 1) / mirror_size,
-		   mirror_size);
-
-	if (register_netdev(dev))
-		goto err_unmap;
-
-	cards[numcards++] = dev;
-	return 0;
-
-err_unmap:
-	iounmap(lp->mem_start);
-err_free_irq:
-	free_irq(dev->irq, dev);
-err_release_mem:
-	release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
-err_free_dev:
-	free_arcdev(dev);
-	return -EIO;
-}
-
-static void com90xx_command(struct net_device *dev, int cmd)
-{
-	short ioaddr = dev->base_addr;
-
-	arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);
-}
-
-static int com90xx_status(struct net_device *dev)
-{
-	short ioaddr = dev->base_addr;
-
-	return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
-}
-
-static void com90xx_setmask(struct net_device *dev, int mask)
-{
-	short ioaddr = dev->base_addr;
-
-	arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);
-}
-
-/* Do a hardware reset on the card, and set up necessary registers.
- *
- * This should be called as little as possible, because it disrupts the
- * token on the network (causes a RECON) and requires a significant delay.
- *
- * However, it does make sure the card is in a defined state.
- */
-static int com90xx_reset(struct net_device *dev, int really_reset)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	short ioaddr = dev->base_addr;
-
-	arc_printk(D_INIT, dev, "Resetting (status=%02Xh)\n",
-		   arcnet_inb(ioaddr, COM9026_REG_R_STATUS));
-
-	if (really_reset) {
-		/* reset the card */
-		arcnet_inb(ioaddr, COM9026_REG_R_RESET);
-		mdelay(RESETtime);
-	}
-	/* clear flags & end reset */
-	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! */
-	arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) | ENABLE16flag,
-		    ioaddr, COM9026_REG_RW_CONFIG);
-#endif
-
-	/* verify that the ARCnet signature byte is present */
-	if (arcnet_readb(lp->mem_start, COM9026_REG_R_STATUS) != TESTvalue) {
-		if (really_reset)
-			arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
-		return 1;
-	}
-	/* enable extended (512-byte) packets */
-	arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
-
-	/* clean out all the memory to make debugging make more sense :) */
-	if (BUGLVL(D_DURING))
-		memset_io(lp->mem_start, 0x42, 2048);
-
-	/* done!  return success. */
-	return 0;
-}
-
-static void com90xx_copy_to_card(struct net_device *dev, int bufnum,
-				 int offset, void *buf, int count)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
-
-	TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count));
-}
-
-static void com90xx_copy_from_card(struct net_device *dev, int bufnum,
-				   int offset, void *buf, int count)
-{
-	struct arcnet_local *lp = netdev_priv(dev);
-	void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
-
-	TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
-}
-
-MODULE_DESCRIPTION("ARCnet COM90xx normal chipset driver");
-MODULE_LICENSE("GPL");
-
-static int __init com90xx_init(void)
-{
-	if (irq == 2)
-		irq = 9;
-	com90xx_probe();
-	if (!numcards)
-		return -EIO;
-	return 0;
-}
-
-static void __exit com90xx_exit(void)
-{
-	struct net_device *dev;
-	struct arcnet_local *lp;
-	int count;
-
-	for (count = 0; count < numcards; count++) {
-		dev = cards[count];
-		lp = netdev_priv(dev);
-
-		unregister_netdev(dev);
-		free_irq(dev->irq, dev);
-		iounmap(lp->mem_start);
-		release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
-		release_mem_region(dev->mem_start,
-				   dev->mem_end - dev->mem_start + 1);
-		free_arcdev(dev);
-	}
-}
-
-module_init(com90xx_init);
-module_exit(com90xx_exit);
-
-#ifndef MODULE
-static int __init com90xx_setup(char *s)
-{
-	int ints[8];
-
-	s = get_options(s, 8, ints);
-	if (!ints[0] && !*s) {
-		pr_notice("Disabled\n");
-		return 1;
-	}
-
-	switch (ints[0]) {
-	default:		/* ERROR */
-		pr_err("Too many arguments\n");
-		fallthrough;
-	case 3:		/* Mem address */
-		shmem = ints[3];
-		fallthrough;
-	case 2:		/* IRQ */
-		irq = ints[2];
-		fallthrough;
-	case 1:		/* IO address */
-		io = ints[1];
-	}
-
-	if (*s)
-		snprintf(device, sizeof(device), "%s", s);
-
-	return 1;
-}
-
-__setup("com90xx=", com90xx_setup);
-#endif
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH] Docs/{ABI,admin-guide}/damon: fix various typoes
From: SeongJae Park @ 2026-05-18  0:31 UTC (permalink / raw)
  To: SeongJae Park
  Cc: Zenghui Yu, damon, linux-mm, linux-kernel, linux-doc, akpm, david,
	ljs, liam, vbabka, rppt, surenb, mhocko, corbet, skhan
In-Reply-To: <20260517235214.89662-1-sj@kernel.org>

On Sun, 17 May 2026 16:52:13 -0700 SeongJae Park <sj@kernel.org> wrote:

> On Mon, 18 May 2026 02:26:22 +0800 Zenghui Yu <zenghui.yu@linux.dev> wrote:
> 
> > ``damon_target_idx`` was wrongly written as ``target_idx`` in the docs. Fix
> > it all over the place, as well as the wrong directory count, grammar, etc.
> 
> Nice, thank you Zenghui!
> 
> > 
> > Signed-off-by: Zenghui Yu <zenghui.yu@linux.dev>
> 
> Reviewed-by: SeongJae Park <sj@kernel.org>

Note for Andrew.  Seems this patch is not based on mm-new.  I added this patch
to damon/next [1] after resolving the conflict.  Please feel free to pick it
from there if you'd like to.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/sj/linux.git/commit/?h=damon/next&id=011caa6d782382337b598a92aefe3c5db5ed8c0e


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [PATCH] dcache: add fs.dentry-limit sysctl with negative-first reaper
From: NeilBrown @ 2026-05-17 23:55 UTC (permalink / raw)
  To: Horst Birthelmer
  Cc: Miklos Szeredi, Jonathan Corbet, Shuah Khan, Alexander Viro,
	Christian Brauner, Jan Kara, linux-doc, linux-kernel,
	linux-fsdevel, Horst Birthelmer
In-Reply-To: <20260514-limit-dentries-cache-v1-1-431b9eb0c530@ddn.com>

On Fri, 15 May 2026, Horst Birthelmer wrote:
> From: Horst Birthelmer <hbirthelmer@ddn.com>
> 
> The dcache only shrinks under memory pressure, which is rarely reached
> on machines with ample RAM, so cached negative dentries can accumulate
> without bound.  Give administrators a soft cap they can set,
> and a background worker that prefers negative dentries when reclaiming.
> 
> Two new sysctls under /proc/sys/fs/:
> 
>   dentry-limit             -- soft cap on nr_dentry.  0 (default)
>                               disables the feature; behaviour is then
>                               identical to before.

Is a system-wide cap really a suitable tool?  What guidance would you
give to sysadmins who are considering setting a number?

Is there a better approach?

According to the email you linked, a problem arises when a directory has
a great many negative children.  Code which walks the list of children
(such as fsnotify) while holding a lock can suffer unpredictable delays
and result in long lock-hold times.  So maybe a limit on negative
dentries for any parent is what we really want.  That would be clumsy to
implement I imagine.

But what if we move dentries to the end of the list when they become
negative, and to the start of the list when they become positive?  Then
code which walks the child list could simply abort on the first
negative.

I doubt that would be quite as easy as it sounds, but it would at least
be more focused on the observed symptom rather than some whole-system
number which only vaguely correlates with the observed symptom.

Maybe a completely different approach: change children-walking code to
drop and retake the lock (with appropriate validation) periodically.
What too would address the specific symptom.

Thanks for attempting to resolve this issue, but I'm not convinced that
you have found a good solution yet.

NeilBrown



>   dentry-limit-interval-ms -- pacing for the worker while still over
>                               the cap.  Default 1000, minimum 1.
> 
> When the cap is exceeded, a delayed_work runs in two phases:
> 
>   1. iterate_supers() draining only negative dentries from every LRU.
>      Positive entries are rotated past so the walk makes progress.
>      DCACHE_REFERENCED is ignored here on purpose -- an admin-imposed
>      cap should evict even hot negatives before any positive entry.
>   2. If still over the cap, iterate_supers() again with the same
>      isolate callback the memory-pressure shrinker uses.
> 
> Signed-off-by: Horst Birthelmer <hbirthelmer@ddn.com>
> ---
> There was a discussion at LSFMM about servers with too many cached
> negative dentries.
> That gave me the idea to keep the dentries in general limited
> if the system administrator needs it to.
> 
> This is somewhat related to [1] where it would address the same
> symptoms but in a more unobtrusive way, by just garbage collecting
> the negative and then the unused cache entries.
> 
> The other effect I have seen regarding this is that FUSE
> will not forget inodes (no FORGET call to the FUSE server)
> even after the latest reference has been closed until much later.
> 
> In a FUSE server that mirrors the kernel cached inodes in user space
> because it has to keep a lot of private data for every node
> this puts an unnecessarry memory strain on that userspace entity
> especially if the memory is limited for its cgroup.
> 
> [1]: https://lore.kernel.org/linux-fsdevel/20260331012925.74840-1-raven@themaw.net/
> ---
>  Documentation/admin-guide/sysctl/fs.rst |  28 +++++
>  fs/dcache.c                             | 197 ++++++++++++++++++++++++++++++++
>  2 files changed, 225 insertions(+)
> 
> diff --git a/Documentation/admin-guide/sysctl/fs.rst b/Documentation/admin-guide/sysctl/fs.rst
> index 9b7f65c3efd8..0229aea45d85 100644
> --- a/Documentation/admin-guide/sysctl/fs.rst
> +++ b/Documentation/admin-guide/sysctl/fs.rst
> @@ -38,6 +38,34 @@ requests.  ``aio-max-nr`` allows you to change the maximum value
>  ``aio-max-nr`` does not result in the
>  pre-allocation or re-sizing of any kernel data structures.
>  
> +dentry-limit
> +------------
> +
> +Soft cap on the total number of dentries allocated system-wide (i.e. on
> +``nr_dentry`` from ``dentry-state``).  A value of ``0`` (the default)
> +disables the feature and the dcache grows or shrinks only under memory
> +pressure as before.
> +
> +When set to a non-zero value, a background worker is woken whenever
> +the live dentry count exceeds the limit. The worker walks every
> +superblock's LRU and prefers to evict negative dentries first; if it
> +cannot get back under the limit using negative entries alone it falls
> +back to the same LRU policy used by the memory-pressure shrinker.
> +
> +The limit is *soft*: allocations never fail because of it, and brief
> +overshoots while the worker catches up are expected. Set the cap a
> +comfortable margin above your steady-state working set.
> +
> +dentry-limit-interval-ms
> +------------------------
> +
> +How often, in milliseconds, the ``dentry-limit`` worker re-runs while
> +``nr_dentry`` is still above the cap. Defaults to ``1000`` (one
> +second); the minimum accepted value is ``1``. Smaller values trim the
> +cache more aggressively at the cost of more CPU spent walking LRUs;
> +larger values let temporary spikes ride out before any work is done.
> +Has no effect when ``dentry-limit`` is ``0``.
> +
>  dentry-negative
>  ----------------------------
>  
> diff --git a/fs/dcache.c b/fs/dcache.c
> index 2c61aeea41f4..4959d2c011c0 100644
> --- a/fs/dcache.c
> +++ b/fs/dcache.c
> @@ -144,6 +144,19 @@ static DEFINE_PER_CPU(long, nr_dentry_unused);
>  static DEFINE_PER_CPU(long, nr_dentry_negative);
>  static int dentry_negative_policy;
>  
> +/*
> + * Soft cap on the total number of dentries. When non-zero and exceeded,
> + * a background worker prunes unused dentries (preferring negative ones)
> + * until we are back under the limit. Zero (the default) disables the
> + * feature entirely; the fast path in __d_alloc() only pays the cost of
> + * a READ_ONCE and a branch in that case.
> + */
> +static unsigned long sysctl_dentry_limit __read_mostly;
> +static unsigned int sysctl_dentry_limit_interval_ms __read_mostly = 1000;
> +static unsigned long dentry_limit_last_kick;
> +
> +static void dentry_limit_kick(void);
> +
>  #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
>  /* Statistics gathering. */
>  static struct dentry_stat_t dentry_stat = {
> @@ -199,6 +212,20 @@ static int proc_nr_dentry(const struct ctl_table *table, int write, void *buffer
>  	return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
>  }
>  
> +/*
> + * Writing fs.dentry-limit should give prompt feedback to admins
> + * lowering the cap, so kick the worker on every successful write.
> + */
> +static int proc_dentry_limit(const struct ctl_table *table, int write,
> +			     void *buffer, size_t *lenp, loff_t *ppos)
> +{
> +	int ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
> +
> +	if (write && !ret)
> +		dentry_limit_kick();
> +	return ret;
> +}
> +
>  static const struct ctl_table fs_dcache_sysctls[] = {
>  	{
>  		.procname	= "dentry-state",
> @@ -207,6 +234,21 @@ static const struct ctl_table fs_dcache_sysctls[] = {
>  		.mode		= 0444,
>  		.proc_handler	= proc_nr_dentry,
>  	},
> +	{
> +		.procname	= "dentry-limit",
> +		.data		= &sysctl_dentry_limit,
> +		.maxlen		= sizeof(sysctl_dentry_limit),
> +		.mode		= 0644,
> +		.proc_handler	= proc_dentry_limit,
> +	},
> +	{
> +		.procname	= "dentry-limit-interval-ms",
> +		.data		= &sysctl_dentry_limit_interval_ms,
> +		.maxlen		= sizeof(sysctl_dentry_limit_interval_ms),
> +		.mode		= 0644,
> +		.proc_handler	= proc_douintvec_minmax,
> +		.extra1		= SYSCTL_ONE,
> +	},
>  	{
>  		.procname	= "dentry-negative",
>  		.data		= &dentry_negative_policy,
> @@ -1325,6 +1367,160 @@ static enum lru_status dentry_lru_isolate_shrink(struct list_head *item,
>  	return LRU_REMOVED;
>  }
>  
> +#define DENTRY_LIMIT_BATCH	1024UL
> +
> +static void dentry_limit_worker_fn(struct work_struct *work);
> +static DECLARE_DELAYED_WORK(dentry_limit_work, dentry_limit_worker_fn);
> +
> +/*
> + * Variant of dentry_lru_isolate() that only frees negative dentries.
> + * DCACHE_REFERENCED is intentionally not honoured here: the whole point
> + * of an admin-imposed cap on negatives is that even frequently-looked-up
> + * negative entries should be evicted before any positive dentry.
> + * Positive entries are rotated to the tail so the walk continues to
> + * make progress without disturbing their LRU position.
> + */
> +static enum lru_status dentry_lru_isolate_negative(struct list_head *item,
> +		struct list_lru_one *lru, void *arg)
> +{
> +	struct list_head *freeable = arg;
> +	struct dentry *dentry = container_of(item, struct dentry, d_lru);
> +
> +	if (!spin_trylock(&dentry->d_lock))
> +		return LRU_SKIP;
> +
> +	/* Same handling as dentry_lru_isolate() for in-use entries. */
> +	if (dentry->d_lockref.count) {
> +		d_lru_isolate(lru, dentry);
> +		spin_unlock(&dentry->d_lock);
> +		return LRU_REMOVED;
> +	}
> +
> +	if (!d_is_negative(dentry)) {
> +		spin_unlock(&dentry->d_lock);
> +		return LRU_ROTATE;
> +	}
> +
> +	d_lru_shrink_move(lru, dentry, freeable);
> +	spin_unlock(&dentry->d_lock);
> +	return LRU_REMOVED;
> +}
> +
> +struct dentry_limit_ctx {
> +	long over;		/* remaining dentries to evict */
> +	list_lru_walk_cb isolate;
> +};
> +
> +static void dentry_limit_prune_sb(struct super_block *sb, void *arg)
> +{
> +	struct dentry_limit_ctx *ctx = arg;
> +	unsigned long walked = 0;
> +	unsigned long budget;
> +
> +	if (ctx->over <= 0)
> +		return;
> +
> +	/*
> +	 * Walk up to one full pass of this superblock's LRU, in
> +	 * DENTRY_LIMIT_BATCH-sized chunks. The loop matters mainly for
> +	 * phase 1: dentry_lru_isolate_negative() returns LRU_ROTATE for
> +	 * positive dentries, which still counts against list_lru_walk()'s
> +	 * nr_to_walk. A single batch can therefore finish having freed
> +	 * nothing when positives crowd the head of the LRU, and without
> +	 * the inner loop the worker would have to wait a full
> +	 * dentry-limit-interval-ms before retrying never reaching the
> +	 * negatives buried behind a long run of positives.
> +	 *
> +	 * The budget is snapshot at entry so a filesystem allocating
> +	 * dentries faster than we drain them can't keep us spinning here
> +	 * forever; freshly added dentries are picked up on the next
> +	 * worker invocation.
> +	 *
> +	 * Phase 2 normally exits much sooner: its isolate callback frees
> +	 * any non-referenced dentry, so ctx->over typically hits zero
> +	 * inside the first batch. The worst-case over-eviction is one
> +	 * batch past the cap, which is within the soft semantics of
> +	 * fs.dentry-limit.
> +	 */
> +	budget = list_lru_count(&sb->s_dentry_lru);
> +
> +	while (ctx->over > 0 && walked < budget) {
> +		LIST_HEAD(dispose);
> +		unsigned long nr;
> +		long freed;
> +
> +		nr = min(DENTRY_LIMIT_BATCH, budget - walked);
> +		freed = list_lru_walk(&sb->s_dentry_lru, ctx->isolate,
> +				      &dispose, nr);
> +		shrink_dentry_list(&dispose);
> +
> +		ctx->over -= freed;
> +		walked += nr;
> +
> +		cond_resched();
> +	}
> +}
> +
> +static void dentry_limit_worker_fn(struct work_struct *work)
> +{
> +	struct dentry_limit_ctx ctx;
> +	unsigned long limit = READ_ONCE(sysctl_dentry_limit);
> +	unsigned int ms;
> +	long nr;
> +
> +	if (!limit)
> +		return;
> +
> +	nr = get_nr_dentry();
> +	if (nr <= (long)limit)
> +		return;
> +
> +	ctx.over = nr - (long)limit;
> +
> +	/* Phase 1: drain negative dentries across every superblock. */
> +	ctx.isolate = dentry_lru_isolate_negative;
> +	iterate_supers(dentry_limit_prune_sb, &ctx);
> +
> +	/* Phase 2: still over? Apply the ordinary LRU policy. */
> +	if (ctx.over > 0) {
> +		ctx.isolate = dentry_lru_isolate;
> +		iterate_supers(dentry_limit_prune_sb, &ctx);
> +	}
> +
> +	/*
> +	 * Re-arm while still above the limit. Re-read the sysctls in
> +	 * case the admin raised the cap or disabled the feature during
> +	 * the walk.
> +	 */
> +	limit = READ_ONCE(sysctl_dentry_limit);
> +	if (!limit || get_nr_dentry() <= (long)limit)
> +		return;
> +
> +	ms = READ_ONCE(sysctl_dentry_limit_interval_ms);
> +	queue_delayed_work(system_unbound_wq, &dentry_limit_work,
> +			   msecs_to_jiffies(ms));
> +}
> +
> +static void dentry_limit_kick(void)
> +{
> +	unsigned long limit = READ_ONCE(sysctl_dentry_limit);
> +	unsigned long now;
> +
> +	if (!limit)
> +		return;
> +	if (delayed_work_pending(&dentry_limit_work))
> +		return;
> +
> +	now = jiffies;
> +	if (time_before(now, READ_ONCE(dentry_limit_last_kick) + HZ / 10))
> +		return;
> +	WRITE_ONCE(dentry_limit_last_kick, now);
> +
> +	if (get_nr_dentry() <= (long)limit)
> +		return;
> +
> +	queue_delayed_work(system_unbound_wq, &dentry_limit_work, 0);
> +}
>  
>  /**
>   * shrink_dcache_sb - shrink dcache for a superblock
> @@ -1868,6 +2064,7 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
>  	}
>  
>  	this_cpu_inc(nr_dentry);
> +	dentry_limit_kick();
>  
>  	return dentry;
>  }
> 
> ---
> base-commit: 5d6919055dec134de3c40167a490f33c74c12581
> change-id: 20260513-limit-dentries-cache-63685729672b
> 
> Best regards,
> -- 
> Horst Birthelmer <hbirthelmer@ddn.com>
> 
> 
> 


^ permalink raw reply

* Re: [PATCH] Docs/{ABI,admin-guide}/damon: fix various typoes
From: SeongJae Park @ 2026-05-17 23:52 UTC (permalink / raw)
  To: Zenghui Yu
  Cc: SeongJae Park, damon, linux-mm, linux-kernel, linux-doc, akpm,
	david, ljs, liam, vbabka, rppt, surenb, mhocko, corbet, skhan
In-Reply-To: <20260517182624.7167-1-zenghui.yu@linux.dev>

On Mon, 18 May 2026 02:26:22 +0800 Zenghui Yu <zenghui.yu@linux.dev> wrote:

> ``damon_target_idx`` was wrongly written as ``target_idx`` in the docs. Fix
> it all over the place, as well as the wrong directory count, grammar, etc.

Nice, thank you Zenghui!

> 
> Signed-off-by: Zenghui Yu <zenghui.yu@linux.dev>

Reviewed-by: SeongJae Park <sj@kernel.org>


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [RFC PATCH 5/5] mm/damon/paddr: add time budget to migration page walk
From: SeongJae Park @ 2026-05-17 23:43 UTC (permalink / raw)
  To: Ravi Jonnalagadda
  Cc: SeongJae Park, damon, linux-mm, linux-kernel, linux-doc, akpm,
	corbet, bijan311, ajayjoshi, honggyu.kim, yunjeong.mun
In-Reply-To: <20260516210357.2247-6-ravis.opensrc@gmail.com>

On Sat, 16 May 2026 14:03:57 -0700 Ravi Jonnalagadda <ravis.opensrc@gmail.com> wrote:

> On populated physical address ranges the pageblock skip optimization
> alone is insufficient — most pageblocks contain at least one allocated
> page, so the walk still iterates millions of PFNs.

So my questions to the fourth patch of this series are also applied here,
especially about the assumption of systems having most memory free.  I will
hold digging deep here until the high level discussion is completed.


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [RFC PATCH 4/5] mm/damon/paddr: skip free pageblocks in migration walk
From: SeongJae Park @ 2026-05-17 23:37 UTC (permalink / raw)
  To: Ravi Jonnalagadda
  Cc: SeongJae Park, damon, linux-mm, linux-kernel, linux-doc, akpm,
	corbet, bijan311, ajayjoshi, honggyu.kim, yunjeong.mun
In-Reply-To: <20260516210357.2247-5-ravis.opensrc@gmail.com>

On Sat, 16 May 2026 14:03:56 -0700 Ravi Jonnalagadda <ravis.opensrc@gmail.com> wrote:

> damon_pa_migrate() walks every PFN in a region linearly, calling
> damon_get_folio() for each one.  On sparse physical address spaces
> (e.g., CXL-attached memory), a single DAMON region can span hundreds
> of gigabytes where most memory is free and sitting in the buddy
> allocator.  Most page lookups are fruitless and dominate kdamond
> tick time.

On sparse address spaces, the problem would be large DAMON regions of offlined
memory.  The large DAMON regions that nearly all freed memory is another
problem that doesn't require the sparse address spaces.  If I'm not wrong, the
above paragraph could better clarified in my opinion.

> 
> Check at pageblock boundaries (2MB on x86_64) whether the block is
> entirely free.  If the first page of a pageblock is a buddy page at
> pageblock_order or higher, the entire block is free and can be
> skipped.
> Similarly skip pageblocks where pfn_to_online_page() returns
> NULL.
> 
> This reduces the iteration from O(region_sz / PAGE_SIZE) to
> O(region_sz / pageblock_sz) + O(populated_pages).
> 
> buddy_order_unsafe() is used without zone->lock.  A transient false
> positive (block becomes non-free between the PageBuddy and order
> checks) costs at most one tick of missed candidates on that block;
> the next tick re-scans.  No correctness consequence as DAMON walks
> are best-effort.

I was initially thinking this is a good and reasonable optimization approach.
But on the second thought I get below questions.

For large offlined memory space problem, couldn't we simply tune DAMON's
monitoring regions boundary to ignore the holes?

For large free memory area, is it reasonable to assume such situations?  In
production, users will try to utilize as much memory of the system as possible.
Then, wouldn't there be such problematically large free memory area?

Could you please enlighten me?

I will hold digging deep until this high level questions are answered.


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [RFC v3 0/3] add kconfirm
From: Julian Braha @ 2026-05-17 23:21 UTC (permalink / raw)
  To: Demi Marie Obenour, nathan, nsc
  Cc: jani.nikula, akpm, gary, ljs, arnd, gregkh, masahiroy, ojeda,
	corbet, qingfang.deng, yann.prono, ej, linux-kernel,
	rust-for-linux, linux-doc, linux-kbuild
In-Reply-To: <c6e9481c-932b-42f7-a903-b781d38cc1a8@gmail.com>

On 5/17/26 07:14, Demi Marie Obenour wrote:
>> Hi all,
>>
>> kconfirm has shrunk a lot since v2!
> Thanks for dropping so many of the dependencies!
> 
> It might be able to shrink it further by using the existing C Kconfig
> parser.  This has the advantage that it ensures kconfirm and Kconfig
> will interpret the Kconfig files the same way.  I'm not sure if
> that would be too much of a change.  That's up to you.
Hi Demi,

I did look into the in-tree parser after your suggestion in v2. What I
discovered was that this parser performs too much evaluation of the
kconfig code during parsing, making it unsuitable for purposes of static
analysis:

The in-tree parser doesn't actually output a parse tree that we can
traverse and analyze, which is how kconfirm currently works. Instead,
semantic actions directly construct the symbol table *during parsing*,
with that symbol table being different from ours. I think(?) this makes
the overall process faster (which is great for real-world kernel
builds), but for static analysis purposes, we really need to preserve
as much of the underlying code as we can. For example, we don't even
preprocess variables, because this allows us to analyze more regardless
of the host and target. (Well, architecture is the one exception there
because we need to resolve imports/"source".)

I do want to point out that Yann, the author of kconfirm's parsing
library (nom-kconfig) is CC'd on these RFCs and has done an awesome job
of supporting the parser and kconfirm's usage of it.

He also helped reduce the number of indirect dependencies pulled in by
the parser following your feedback in RFC v2.

- Julian Braha

^ permalink raw reply

* [PATCH v2 01/16] kbuild: Bump minimum version of LLVM for building the kernel to 17.0.1
From: Nathan Chancellor @ 2026-05-17 23:05 UTC (permalink / raw)
  To: Nathan Chancellor, Nicolas Schier, Bill Wendling, Justin Stitt,
	Nick Desaulniers
  Cc: linux-kernel, llvm, linux-kbuild, Jonathan Corbet, Shuah Khan,
	linux-doc
In-Reply-To: <20260517-bump-minimum-supported-llvm-version-to-17-v2-0-b3b8cda46bdd@kernel.org>

The current minimum version of LLVM for building the kernel is 15.0.0.
However, there are two deficiencies compared to GCC that were fixed in
LLVM 17 that are starting to become more noticeable.

The first was a bug in LLVM's scope checker [1], where all labels in a
function were validated as potential targets of an asm goto statement,
even if they were not listed in the asm goto statement as targets. This
becomes particularly problematic when the cleanup attribute is used, as

  asm goto(... : label_a);
  ...
label_a:
  ...
  int var __free(foo);
  asm goto(... : label_b);
  ...
label_b:
  ...

will trigger an error since the scope checker will complain that the
cleanup variable would be skipped when jumping from the first asm goto
to label_b (which obviously cannot happen). This issue was the catalyst
for commit e2ffa15b9baa ("kbuild: Disable CC_HAS_ASM_GOTO_OUTPUT on
clang < 17"). Unfortunately, this issue is reproducible with regular asm
goto in addition to asm goto with outputs, so that change was not
entirely sufficient to avoid the issue altogether. As asm goto has
effectively been required since commit a0a12c3ed057 ("asm goto:
eradicate CC_HAS_ASM_GOTO") and the usage of the cleanup attribute
continues to grow across the tree, raising the minimum to a version that
avoids this issue altogether is a better long term solution than
attempting to workaround it at every spot where it happens.

The second issue is an incompatibility with GCC 8.1+ around variables
marked with const being valid constant expressions for _Static_assert
and other macros [2]. With GCC 8.1 being the minimum supported version
since commit 118c40b7b503 ("kbuild: require gcc-8 and binutils-2.30"),
this incompatibility becomes more of a maintenance burden since only
clang-15 and clang-16 are affected by it.

Looking at the clang version of various major distributions through
Docker images, no one should be left behind as a result of this bump, as
the old ones cannot clear the current minimum of 15.0.0.

  archlinux:latest              clang version 22.1.3
  debian:oldoldstable-slim      Debian clang version 11.0.1-2
  debian:oldstable-slim         Debian clang version 14.0.6
  debian:stable-slim            Debian clang version 19.1.7 (3+b1)
  debian:testing-slim           Debian clang version 21.1.8 (3+b1)
  debian:unstable-slim          Debian clang version 21.1.8 (7+b1)
  fedora:42                     clang version 20.1.8 (Fedora 20.1.8-4.fc42)
  fedora:latest                 clang version 21.1.8 (Fedora 21.1.8-4.fc43)
  fedora:44                     clang version 22.1.1 (Fedora 22.1.1-2.fc44)
  fedora:rawhide                clang version 22.1.3 (Fedora 22.1.3-1.fc45)
  opensuse/leap:latest          clang version 17.0.6
  opensuse/tumbleweed:latest    clang version 21.1.8
  ubuntu:jammy                  Ubuntu clang version 14.0.0-1ubuntu1.1
  ubuntu:noble                  Ubuntu clang version 18.1.3 (1ubuntu1)
  ubuntu:questing               Ubuntu clang version 20.1.8 (0ubuntu4)
  ubuntu:resolute               Ubuntu clang version 21.1.8 (6ubuntu1)

17.0.1 is chosen as the minimum instead of 17.0.0 to ensure that the
particular version of LLVM 17 has the two aforementioned bugs fixed, as
the second was fixed during the 17.0.0 release candidate phase and it
was not until LLVM 18 that LLVM adopted the scheme of x.0.0 being a
prerelease version and x.1.0 is a release version [3] to help with
scenarios such as this.

Link: https://github.com/llvm/llvm-project/commit/f023f5cdb2e6c19026f04a15b5a935c041835d14 [1]
Link: https://github.com/llvm/llvm-project/commit/0b2d5b967d98375793897295d651f58f6fbd3034 [2]
Link: https://github.com/llvm/llvm-project/commit/4532617ae420056bf32f6403dde07fb99d276a49 [3]
Acked-by: Nicolas Schier <nsc@kernel.org>
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
---
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: linux-doc@vger.kernel.org
---
 Documentation/process/changes.rst                    | 2 +-
 Documentation/translations/it_IT/process/changes.rst | 2 +-
 Documentation/translations/pt_BR/process/changes.rst | 2 +-
 scripts/min-tool-version.sh                          | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst
index 9a99037270ff..b9afce768446 100644
--- a/Documentation/process/changes.rst
+++ b/Documentation/process/changes.rst
@@ -36,7 +36,7 @@ bindgen (optional)     0.71.1           bindgen --version
 binutils               2.30             ld -v
 bison                  2.0              bison --version
 btrfs-progs            0.18             btrfs --version
-Clang/LLVM (optional)  15.0.0           clang --version
+Clang/LLVM (optional)  17.0.1           clang --version
 e2fsprogs              1.41.4           e2fsck -V
 flex                   2.5.35           flex --version
 gdb                    7.2              gdb --version
diff --git a/Documentation/translations/it_IT/process/changes.rst b/Documentation/translations/it_IT/process/changes.rst
index 7e93833b4511..7ee54c972418 100644
--- a/Documentation/translations/it_IT/process/changes.rst
+++ b/Documentation/translations/it_IT/process/changes.rst
@@ -33,7 +33,7 @@ PC Card, per esempio, probabilmente non dovreste preoccuparvi di pcmciautils.
         Programma       Versione minima       Comando per verificare la versione
 ====================== =================  ========================================
 GNU C                  8.1                gcc --version
-Clang/LLVM (optional)  13.0.0             clang --version
+Clang/LLVM (optional)  17.0.1             clang --version
 Rust (opzionale)       1.78.0             rustc --version
 bindgen (opzionale)    0.65.1             bindgen --version
 GNU make               4.0                make --version
diff --git a/Documentation/translations/pt_BR/process/changes.rst b/Documentation/translations/pt_BR/process/changes.rst
index 1964c1c93b34..6bbfe60fd973 100644
--- a/Documentation/translations/pt_BR/process/changes.rst
+++ b/Documentation/translations/pt_BR/process/changes.rst
@@ -31,7 +31,7 @@ PC Card por exemplo, provavelmente não precisará se preocupar com o pcmciautil
         Programa        Versão mínima       Comando para verificar a versão
 ====================== ===============  ========================================
 GNU C                  8.1              gcc --version
-Clang/LLVM (optional)  15.0.0           clang --version
+Clang/LLVM (optional)  17.0.1           clang --version
 Rust (optional)        1.78.0           rustc --version
 bindgen (optional)     0.65.1           bindgen --version
 GNU make               4.0              make --version
diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh
index b96ec2d379b6..ea2689bc9641 100755
--- a/scripts/min-tool-version.sh
+++ b/scripts/min-tool-version.sh
@@ -27,7 +27,7 @@ llvm)
 	if [ "$SRCARCH" = loongarch ]; then
 		echo 18.0.0
 	else
-		echo 15.0.0
+		echo 17.0.1
 	fi
 	;;
 rustc)

-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 00/16] Bump minimum version of LLVM for building the kernel to 17.0.1
From: Nathan Chancellor @ 2026-05-17 23:05 UTC (permalink / raw)
  To: Nathan Chancellor, Nicolas Schier, Bill Wendling, Justin Stitt,
	Nick Desaulniers
  Cc: linux-kernel, llvm, linux-kbuild, Jonathan Corbet, Shuah Khan,
	linux-doc, Kees Cook, Gustavo A. R. Silva, linux-hardening,
	linux-security-module, Rong Xu, Han Shen, Russell King,
	Arnd Bergmann, linux-arm-kernel, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, linux-riscv, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Ard Biesheuvel, Peter Zijlstra

The current minimum version of LLVM for building the kernel is 15.0.0.
However, there are two deficiencies compared to GCC that were fixed in
LLVM 17 that are starting to become more noticeable.

The first was a bug in LLVM's scope checker [1], where all labels in a
function were validated as potential targets of an asm goto statement,
even if they were not listed in the asm goto statement as targets. This
becomes particularly problematic when the cleanup attribute is used, as

  asm goto(... : label_a);
  ...
label_a:
  ...
  int var __free(foo);
  asm goto(... : label_b);
  ...
label_b:
  ...

will trigger an error since the scope checker will complain that the
cleanup variable would be skipped when jumping from the first asm goto
to label_b (which obviously cannot happen). This issue was the catalyst
for commit e2ffa15b9baa ("kbuild: Disable CC_HAS_ASM_GOTO_OUTPUT on
clang < 17"). Unfortunately, this issue is reproducible with regular asm
goto in addition to asm goto with outputs, so that change was not
entirely sufficient to avoid the issue altogether. As asm goto has
effectively been required since commit a0a12c3ed057 ("asm goto:
eradicate CC_HAS_ASM_GOTO") and the usage of the cleanup attribute
continues to grow across the tree, raising the minimum to a version that
avoids this issue altogether is a better long term solution than
attempting to workaround it at every spot where it happens.

The second issue is an incompatibility with GCC 8.1+ around variables
marked with const being valid constant expressions for _Static_assert
and other macros [2]. With GCC 8.1 being the minimum supported version
since commit 118c40b7b503 ("kbuild: require gcc-8 and binutils-2.30"),
this incompatibility becomes more of a maintenance burden since only
clang-15 and clang-16 are affected by it.

Looking at the clang version of various major distributions through
Docker images, no one should be left behind as a result of this bump, as
the old ones cannot clear the current minimum of 15.0.0.

  archlinux:latest              clang version 22.1.3
  debian:oldoldstable-slim      Debian clang version 11.0.1-2
  debian:oldstable-slim         Debian clang version 14.0.6
  debian:stable-slim            Debian clang version 19.1.7 (3+b1)
  debian:testing-slim           Debian clang version 21.1.8 (3+b1)
  debian:unstable-slim          Debian clang version 21.1.8 (7+b1)
  fedora:42                     clang version 20.1.8 (Fedora 20.1.8-4.fc42)
  fedora:latest                 clang version 21.1.8 (Fedora 21.1.8-4.fc43)
  fedora:44                     clang version 22.1.1 (Fedora 22.1.1-2.fc44)
  fedora:rawhide                clang version 22.1.3 (Fedora 22.1.3-1.fc45)
  opensuse/leap:latest          clang version 17.0.6
  opensuse/tumbleweed:latest    clang version 21.1.8
  ubuntu:jammy                  Ubuntu clang version 14.0.0-1ubuntu1.1
  ubuntu:noble                  Ubuntu clang version 18.1.3 (1ubuntu1)
  ubuntu:questing               Ubuntu clang version 20.1.8 (0ubuntu4)
  ubuntu:resolute               Ubuntu clang version 21.1.8 (6ubuntu1)

17.0.1 is chosen as the minimum instead of 17.0.0 to ensure that the
particular version of LLVM 17 has the two aforementioned bugs fixed, as
the second was fixed during the 17.0.0 release candidate phase and it
was not until LLVM 18 that LLVM adopted the scheme of x.0.0 being a
prerelease version and x.1.0 is a release version [3] to help with
scenarios such as this.

The first patch in the series does the actual bump. The remaining
patches are cleanups of workarounds for various issues that are no
longer needed with the bump.

I plan to take this via the Kbuild tree for 7.2, please provide Acks as
necessary.

[1]: https://github.com/llvm/llvm-project/commit/f023f5cdb2e6c19026f04a15b5a935c041835d14
[2]: https://github.com/llvm/llvm-project/commit/0b2d5b967d98375793897295d651f58f6fbd3034
[3]: https://github.com/llvm/llvm-project/commit/4532617ae420056bf32f6403dde07fb99d276a49

---
Changes in v2:
- Pick up provided tags (thanks everyone!)
- Patch 1: Adjust changes.rst in Documentation/translations
- Patch 11: Adjust commit message based on Sashiko review
- Patch 15-16: New changes
- Link to v1: https://patch.msgid.link/20260428-bump-minimum-supported-llvm-version-to-17-v1-0-81d9b2e8ee75@kernel.org

---
Nathan Chancellor (16):
      kbuild: Bump minimum version of LLVM for building the kernel to 17.0.1
      security/Kconfig.hardening: Remove tautological condition from CC_HAS_ZERO_CALL_USED_REGS
      security/Kconfig.hardening: Remove tautological condition from FORTIFY_SOURCE
      security/Kconfig.hardening: Remove tautological condition from CC_HAS_RANDSTRUCT
      arch/Kconfig: Remove tautological conditions from HAS_LTO_CLANG
      arch/Kconfig: Remove tautological condition from AUTOFDO_CLANG
      ARM: Drop tautological ld.lld conditions from ARCH_MULTI_V4{,T}
      riscv: Remove tautological condition from selection of ARCH_SUPPORTS_CFI
      riscv: Drop tautological condition from TOOLCHAIN_NEEDS_OLD_ISA_SPEC
      scripts/Makefile.warn: Drop -Wformat handling for clang < 16
      x86/build: Drop unnecessary '-ffreestanding' addition to KBUILD_CFLAGS
      x86/module: Revert "Deal with GOT based stack cookie load on Clang < 17"
      x86/entry/vdso32: Remove conditional omission of '.cfi_offset eflags'
      kbuild: Remove check for broken scoping with clang < 17 in CC_HAS_ASM_GOTO_OUTPUT
      compiler-clang.h: Remove __cleanup -Wunused-variable workaround
      compiler-clang.h: Drop explicit version number from "all" diagnostic macro

 Documentation/process/changes.rst                    |  2 +-
 Documentation/translations/it_IT/process/changes.rst |  2 +-
 Documentation/translations/pt_BR/process/changes.rst |  2 +-
 arch/Kconfig                                         |  5 +----
 arch/arm/Kconfig.platforms                           |  4 ----
 arch/riscv/Kconfig                                   | 16 +++++++---------
 arch/x86/Makefile                                    |  5 -----
 arch/x86/entry/vdso/vdso32/sigreturn.S               | 10 ----------
 arch/x86/include/asm/elf.h                           |  5 ++---
 arch/x86/kernel/module.c                             | 15 ---------------
 include/linux/compiler-clang.h                       | 13 ++-----------
 init/Kconfig                                         |  3 ---
 scripts/Makefile.warn                                | 10 ----------
 scripts/min-tool-version.sh                          |  2 +-
 security/Kconfig.hardening                           |  8 --------
 15 files changed, 16 insertions(+), 86 deletions(-)
---
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
change-id: 20260422-bump-minimum-supported-llvm-version-to-17-b4638a58b043

Best regards,
--  
Cheers,
Nathan


^ permalink raw reply

* Re: [RFC PATCH v3 1/3] scripts: add kconfirm
From: Miguel Ojeda @ 2026-05-17 22:53 UTC (permalink / raw)
  To: Demi Marie Obenour
  Cc: Julian Braha, nathan, nsc, jani.nikula, akpm, gary, ljs, arnd,
	gregkh, masahiroy, ojeda, corbet, qingfang.deng, yann.prono, ej,
	linux-kernel, rust-for-linux, linux-doc, linux-kbuild
In-Reply-To: <f77a4858-2bcf-4bfb-95e0-24a5d91e0862@gmail.com>

On Sun, May 17, 2026 at 10:25 PM Demi Marie Obenour
<demiobenour@gmail.com> wrote:
>
> I was hoping for Linux to avoid the Rust trend of downloading tons
> of third-party crates, with all the supply-chain risks that entails.

I completely agree -- it is why I said a well-known, vetted set of crates.

That is, we should decide on e.g. a single CLI arg parser, a single
logger, etc. for most of our tools, and ideally they should be
well-known crates (ideally already trusted via use in the compiler
itself).

Moreover, they should be pinned with `--locked` or similar (like we
already recommend for `bindgen-cli`), so that we only ever use
something that matches the hash in the lockfile that would be
committed in the tree.

Cheers,
Miguel

^ permalink raw reply

* Re: [PATCH] docs: fix typo in uniwill-laptop.rst
From: Cheesecake @ 2026-05-17 22:16 UTC (permalink / raw)
  To: Armin Wolf, corbet; +Cc: skhan, platform-driver-x86, linux-doc, linux-kernel
In-Reply-To: <d693ffc6-08f0-44ad-a274-c25efc3fcfb2@gmx.de>

On 2026/05/18 1:01, Armin Wolf wrote:

> Am 16.05.26 um 09:06 schrieb Cheesecake:
>
>> Replace "benifit" with "benefit".
>
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>
>> Signed-off-by: Cheesecake <cheesecake2960@icloud.com>
>> ---
>>   Documentation/wmi/devices/uniwill-laptop.rst | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/Documentation/wmi/devices/uniwill-laptop.rst 
>> b/Documentation/wmi/devices/uniwill-laptop.rst
>> index e246bf293..65583b239 100644
>> --- a/Documentation/wmi/devices/uniwill-laptop.rst
>> +++ b/Documentation/wmi/devices/uniwill-laptop.rst
>> @@ -189,7 +189,7 @@ Indexed IO
>>     Indexed IO with IO ports with a granularity of a single byte can 
>> be performed using the ``RIOP``
>>   (read) and ``WIOP`` (write) ACPI control methods. Those ACPI 
>> methods are unused because they
>> -provide no benifit when compared to the native IO port access 
>> functions provided by the kernel.
>> +provide no benefit when compared to the native IO port access 
>> functions provided by the kernel.
>>     Special thanks go to github user `pobrn` which developed the
>>   `qc71_laptop <https://github.com/pobrn/qc71_laptop>`_ driver on 
>> which this driver is partly based.

Hello,

I accidentally used my nickname.

I'll resend this patch with a proper signoff.


Thank you for your time.


^ permalink raw reply

* Re: [PATCH v6 1/2] usb: xhci-pci: add AMD Promontory 21 PCI glue
From: Michal Pecio @ 2026-05-17 21:21 UTC (permalink / raw)
  To: Jihong Min
  Cc: Greg Kroah-Hartman, Mathias Nyman, Guenter Roeck, Jonathan Corbet,
	Shuah Khan, Mario Limonciello, Basavaraj Natikar, linux-usb,
	linux-hwmon, linux-doc, linux-pci, linux-kernel,
	Mario Limonciello (AMD), Yaroslav Isakov
In-Reply-To: <20260517130407.795157-2-hurryman2212@gmail.com>

On Sun, 17 May 2026 22:04:06 +0900, Jihong Min wrote:
> AMD Promontory 21 (PROM21) xHCI controllers use generic xHCI
> operation, but the PCI function also exposes optional
> controller-specific sensor functionality. Add a small PROM21 PCI glue
> driver for AMD 1022:43fc and 1022:43fd controllers.
> 
> The driver delegates USB host operation to the common xhci-pci core,
> collects the parent-provided MMIO resource data, and creates a "hwmon"
> auxiliary device for optional child drivers. Failure to create the
> auxiliary device is logged but does not fail the xHCI probe, since the
> auxiliary device is only needed for sensor support.
> 
> Make the PROM21 PCI glue a hidden Kconfig tristate that follows
> USB_XHCI_PCI. This keeps the glue built in with a built-in xhci-pci core
> and builds it as a module with a modular xhci-pci core. A built-in
> xhci-pci core must not hand PROM21 controllers to a PROM21 glue driver
> that is only available as a module, otherwise USB behind those controllers
> can be unavailable during initramfs and PROM21 temperature sensor support
> may not appear until the controller is rebound after the module loads.
> 
> Assisted-by: Codex:gpt-5.5
> Signed-off-by: Jihong Min <hurryman2212@gmail.com>
> Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
> Tested-by: Yaroslav Isakov <yaroslav.isakov@gmail.com>
> ---
>  drivers/usb/host/Kconfig                      |   7 +
>  drivers/usb/host/Makefile                     |   1 +
>  drivers/usb/host/xhci-pci-prom21.c            | 136 ++++++++++++++++++
>  drivers/usb/host/xhci-pci.c                   |  11 ++
>  drivers/usb/host/xhci-pci.h                   |   3 +
>  include/linux/platform_data/usb-xhci-prom21.h |  22 +++
>  6 files changed, 180 insertions(+)
>  create mode 100644 drivers/usb/host/xhci-pci-prom21.c
>  create mode 100644 include/linux/platform_data/usb-xhci-prom21.h
> 
> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
> index 0a277a07cf70..89bf262235e1 100644
> --- a/drivers/usb/host/Kconfig
> +++ b/drivers/usb/host/Kconfig
> @@ -42,6 +42,13 @@ config USB_XHCI_PCI
>  	depends on USB_PCI
>  	default y
>  
> +config USB_XHCI_PCI_PROM21
> +	tristate
> +	depends on X86
> +	depends on USB_XHCI_PCI
> +	default USB_XHCI_PCI
> +	select AUXILIARY_BUS
> +

Instead of the X86 heuristic, would it be possible to build glue
code if and only if SENSORS_PROM21_XHCI is enabled?

This seems to work:

 config SENSORS_PROM21_XHCI
        tristate "AMD Promontory 21 xHCI temperature sensor"
-       depends on USB_XHCI_PCI_PROM21
+       depends on USB_XHCI_PCI

 config USB_XHCI_PCI_PROM21
        tristate
-       depends on X86
        depends on USB_XHCI_PCI
-       default USB_XHCI_PCI
+       default USB_XHCI_PCI if SENSORS_PROM21_XHCI != 'n'
        select AUXILIARY_BUS

I don't know if it's the best way, perhaps it would be preferable for
the hwmon driver to select the glue, but then I'm not sure how to force
glue to become 'y' when xhci-pci is 'y'.

>  config USB_XHCI_PCI_RENESAS
>  	tristate "Support for additional Renesas xHCI controller with firmware"
>  	depends on USB_XHCI_PCI
> diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
> index a07e7ba9cd53..174580c1281a 100644
> --- a/drivers/usb/host/Makefile
> +++ b/drivers/usb/host/Makefile
> @@ -71,6 +71,7 @@ obj-$(CONFIG_USB_UHCI_HCD)	+= uhci-hcd.o
>  obj-$(CONFIG_USB_FHCI_HCD)	+= fhci.o
>  obj-$(CONFIG_USB_XHCI_HCD)	+= xhci-hcd.o
>  obj-$(CONFIG_USB_XHCI_PCI)	+= xhci-pci.o
> +obj-$(CONFIG_USB_XHCI_PCI_PROM21)	+= xhci-pci-prom21.o
>  obj-$(CONFIG_USB_XHCI_PCI_RENESAS)	+= xhci-pci-renesas.o
>  obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
>  obj-$(CONFIG_USB_XHCI_HISTB)	+= xhci-histb.o
> diff --git a/drivers/usb/host/xhci-pci-prom21.c b/drivers/usb/host/xhci-pci-prom21.c
> new file mode 100644
> index 000000000000..be0933ca5c62
> --- /dev/null
> +++ b/drivers/usb/host/xhci-pci-prom21.c
> @@ -0,0 +1,136 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * AMD Promontory 21 xHCI host controller PCI Bus Glue.
> + *
> + * This does not add any PROM21-specific USB or xHCI operation. It exists only
> + * to publish an auxiliary device for integrated temperature sensor support.
> + *
> + * Copyright (C) 2026 Jihong Min <hurryman2212@gmail.com>
> + */
> +
> +#include <linux/auxiliary_bus.h>
> +#include <linux/device/devres.h>
> +#include <linux/errno.h>
> +#include <linux/idr.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/platform_data/usb-xhci-prom21.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +
> +#include "xhci-pci.h"
> +
> +struct prom21_xhci_auxdev {
> +	struct auxiliary_device *auxdev;
> +	struct prom21_xhci_pdata pdata;
> +	int id;
> +};
> +
> +static DEFINE_IDA(prom21_xhci_auxdev_ida);
> +
> +static void prom21_xhci_auxdev_release(struct device *dev, void *res)
> +{
> +	struct prom21_xhci_auxdev *prom21_auxdev = res;
> +
> +	auxiliary_device_destroy(prom21_auxdev->auxdev);
> +	ida_free(&prom21_xhci_auxdev_ida, prom21_auxdev->id);
> +}
> +
> +static int prom21_xhci_create_auxdev(struct pci_dev *pdev)
> +{
> +	struct prom21_xhci_auxdev *prom21_auxdev;
> +	struct usb_hcd *hcd = pci_get_drvdata(pdev);
> +
> +	if (!hcd)
> +		return -ENODEV;

Shouldn't be necessary after successful xhci_pci_common_probe().

> +
> +	prom21_auxdev = devres_alloc(prom21_xhci_auxdev_release,
> +				     sizeof(*prom21_auxdev), GFP_KERNEL);
> +	if (!prom21_auxdev)
> +		return -ENOMEM;
> +
> +	prom21_auxdev->pdata.pdev = pdev;
> +	prom21_auxdev->pdata.regs = hcd->regs;
> +	prom21_auxdev->pdata.rsrc_len = hcd->rsrc_len;
> +
> +	prom21_auxdev->id = ida_alloc(&prom21_xhci_auxdev_ida, GFP_KERNEL);
> +	if (prom21_auxdev->id < 0) {
> +		int ret = prom21_auxdev->id;
> +
> +		devres_free(prom21_auxdev);
> +		return ret;
> +	}
> +
> +	prom21_auxdev->auxdev = auxiliary_device_create(&pdev->dev,
> +							KBUILD_MODNAME, "hwmon",
> +							&prom21_auxdev->pdata,
> +							prom21_auxdev->id);
> +	if (!prom21_auxdev->auxdev) {
> +		ida_free(&prom21_xhci_auxdev_ida, prom21_auxdev->id);
> +		devres_free(prom21_auxdev);
> +		return -ENOMEM;

The usual "goto error" pattern could be used instead of increasingly
long sequences of xxx_free() calls.

> +	}
> +
> +	devres_add(&pdev->dev, prom21_auxdev);
> +	return 0;
> +}
> +
> +static void prom21_xhci_destroy_auxdev(struct pci_dev *pdev)
> +{
> +	devres_release(&pdev->dev, prom21_xhci_auxdev_release, NULL, NULL);
> +}
> +

It seems that these three functions above are everything that you truly
want to add; the rest is boilerplate required by this two-module scheme
to work, plus ID tables which must be duplicated and kept in sync.

I wonder if a separate module is really justified, as opposed to simply
linking this file into xhci_pci.ko when directed by Kconfig.

The downside would be slightly higher memory usage on systems where the
hwmon driver is enabled but not needed. OTOH, same systems would likely
see reduced disk waste.

> +static int prom21_xhci_probe(struct pci_dev *dev,
> +			     const struct pci_device_id *id)
> +{
> +	int retval;
> +
> +	retval = xhci_pci_common_probe(dev, id);
> +	if (retval)
> +		return retval;
> +
> +	retval = prom21_xhci_create_auxdev(dev);
> +	if (retval) {
> +		/*
> +		 * The auxiliary device only provides optional temperature sensor
> +		 * support. Keep the xHCI controller usable if it fails.
> +		 */
> +		dev_err(&dev->dev,
> +			"failed to create PROM21 hwmon auxiliary device: %d\n",
> +			retval);
> +	}
> +
> +	return 0;
> +}
> +
> +static void prom21_xhci_remove(struct pci_dev *dev)
> +{
> +	prom21_xhci_destroy_auxdev(dev);
> +	xhci_pci_remove(dev);
> +}
> +
> +static const struct pci_device_id pci_ids[] = {
> +	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC) },
> +	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD) },
> +	{ /* end: all zeroes */ }
> +};
> +MODULE_DEVICE_TABLE(pci, pci_ids);
> +
> +static struct pci_driver prom21_xhci_driver = {
> +	.name = "xhci-pci-prom21",
> +	.id_table = pci_ids,
> +
> +	.probe = prom21_xhci_probe,
> +	.remove = prom21_xhci_remove,
> +
> +	.shutdown = usb_hcd_pci_shutdown,
> +	.driver = {
> +		.pm = pm_ptr(&usb_hcd_pci_pm_ops),
> +	},
> +};
> +module_pci_driver(prom21_xhci_driver);
> +
> +MODULE_AUTHOR("Jihong Min <hurryman2212@gmail.com>");
> +MODULE_DESCRIPTION("AMD Promontory 21 xHCI PCI Host Controller Driver");
> +MODULE_IMPORT_NS("xhci");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
> index 585b2f3117b0..039c26b241d0 100644
> --- a/drivers/usb/host/xhci-pci.c
> +++ b/drivers/usb/host/xhci-pci.c
> @@ -696,12 +696,23 @@ static const struct pci_device_id pci_ids_renesas[] = {
>  	{ /* end: all zeroes */ }
>  };
>  
> +/* handled by xhci-pci-prom21 if enabled */
> +static const struct pci_device_id pci_ids_prom21[] = {
> +	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC) },
> +	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD) },
> +	{ /* end: all zeroes */ }
> +};
> +
>  static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
>  {
>  	if (IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS) &&
>  			pci_match_id(pci_ids_renesas, dev))
>  		return -ENODEV;
>  
> +	if (IS_ENABLED(CONFIG_USB_XHCI_PCI_PROM21) &&
> +	    pci_match_id(pci_ids_prom21, dev))
> +		return -ENODEV;
> +
>  	return xhci_pci_common_probe(dev, id);
>  }
>  
> diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h
> index e87c7d9d76b8..11f435f94322 100644
> --- a/drivers/usb/host/xhci-pci.h
> +++ b/drivers/usb/host/xhci-pci.h
> @@ -4,6 +4,9 @@
>  #ifndef XHCI_PCI_H
>  #define XHCI_PCI_H
>  
> +#define PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC	0x43fc
> +#define PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD	0x43fd
> +
>  int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id);
>  void xhci_pci_remove(struct pci_dev *dev);
>  
> diff --git a/include/linux/platform_data/usb-xhci-prom21.h b/include/linux/platform_data/usb-xhci-prom21.h
> new file mode 100644
> index 000000000000..ee672ad452a8
> --- /dev/null
> +++ b/include/linux/platform_data/usb-xhci-prom21.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * AMD Promontory 21 xHCI auxiliary device platform data.
> + *
> + * Copyright (C) 2026 Jihong Min <hurryman2212@gmail.com>
> + */
> +
> +#ifndef _LINUX_PLATFORM_DATA_USB_XHCI_PROM21_H
> +#define _LINUX_PLATFORM_DATA_USB_XHCI_PROM21_H
> +
> +#include <linux/compiler_types.h>
> +#include <linux/types.h>
> +
> +struct pci_dev;
> +
> +struct prom21_xhci_pdata {
> +	struct pci_dev *pdev;
> +	void __iomem *regs;
> +	resource_size_t rsrc_len;
> +};
> +
> +#endif
> -- 
> 2.53.0
> 

^ permalink raw reply

* Re: [PATCH v6 03/11] dt-bindings: mfd: add documentation for S2MU005 PMIC
From: Conor Dooley @ 2026-05-17 20:52 UTC (permalink / raw)
  To: Kaustabh Chakraborty
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet, Shuah Khan, Nam Tran,
	Łukasz Lebiedziński, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-samsung-soc, linux-rtc, linux-doc
In-Reply-To: <DIKZ5L2HC2CV.YL3MZUJQ2EV6@disroot.org>

[-- Attachment #1: Type: text/plain, Size: 961 bytes --]

On Sun, May 17, 2026 at 06:39:37PM +0530, Kaustabh Chakraborty wrote:
>> >> >> +
> >> >> +    properties:
> >> >> +      compatible:
> >> >> +        const: samsung,s2mu005-rgb
> >> >> +
> >> >> +    required:
> >> >> +      - compatible
> >> >> +
> >> >> +    unevaluatedProperties: false
> >> >> +
> >> >> +  reg:
> >> >> +    maxItems: 1
> >> >
> >> > Move this above the child nodes please.
> >> 
> >> But properties are sorted in lex order?
> >
> > Typically the binding is sorted in the same order as properties go in
> > nodes. Common stuff like reg/clocks/interrupts therefore send up above
> > child nodes.
> 
> So, do I change this? For one, I don't see the same being followed in
> other schemas of samsung in the same dir (not that I'm trying to pose it
> as an argument against your suggestion), and this was reviewed by
> Krzysztof and is adderssed in v7.

If Krzysztof doesn't care, then I won't ask you to change it.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [RFC PATCH v3 1/3] scripts: add kconfirm
From: Demi Marie Obenour @ 2026-05-17 20:25 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Julian Braha, nathan, nsc, jani.nikula, akpm, gary, ljs, arnd,
	gregkh, masahiroy, ojeda, corbet, qingfang.deng, yann.prono, ej,
	linux-kernel, rust-for-linux, linux-doc, linux-kbuild
In-Reply-To: <CANiq72k_tXGSCd1BEg8XmTr+acZHfdRbcFOVD7=O6yAbmv-nHw@mail.gmail.com>


[-- Attachment #1.1.1: Type: text/plain, Size: 1229 bytes --]

On 5/17/26 05:58, Miguel Ojeda wrote:
> On Sun, May 17, 2026 at 8:10 AM Demi Marie Obenour
> <demiobenour@gmail.com> wrote:
>>
>> I think it is simpler to just inline all of this code into its
>> single call-site.  The safety of the code is obvious in context,
>> and you can avoid checking for impossible errors.  For instance,
>> since all of the options have required arguments, it really is safe
>> to dereference optarg without any null check.
> 
> If we are going to have unsafe code, then let's please build safe
> abstractions wherever possible, just like we do elsewhere. We should
> also write `// SAFETY` comments and enable the lints that catch that
> etc., just like elsewhere too.
> 
> (This is not to say we should use `getopt` instead of something like
> `clap` -- as soon as we start using `cargo vendor`, then it makes
> sense to at least consider having a set of vetted, well-known crates
> to write Rust tools in-tree, as I mentioned in v1.)

I was hoping for Linux to avoid the Rust trend of downloading tons
of third-party crates, with all the supply-chain risks that entails.
Hence the idea of using getopt and system C libraries.
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)

[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 7253 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* [PATCH v5 2/2] docs: spi: add documentation for userspace device instantiation
From: Vishwaroop A @ 2026-05-17 20:16 UTC (permalink / raw)
  To: broonie, linux-spi
  Cc: smangipudi, jonathanh, thierry.reding, corbet, linux-doc, va
In-Reply-To: <20260517201602.498135-1-va@nvidia.com>

Document the new_device and delete_device sysfs attributes on SPI
controllers:

  - Documentation/spi/instantiating-devices.rst: describes when and
    why this interface is needed, accepted parameters, usage examples,
    and limitations.
  - Documentation/ABI/testing/sysfs-class-spi-master: formal ABI
    entry for both attributes.

Signed-off-by: Vishwaroop A <va@nvidia.com>
---
 .../ABI/testing/sysfs-class-spi-master        | 34 +++++++
 Documentation/spi/index.rst                   |  1 +
 Documentation/spi/instantiating-devices.rst   | 88 +++++++++++++++++++
 3 files changed, 123 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-class-spi-master
 create mode 100644 Documentation/spi/instantiating-devices.rst

diff --git a/Documentation/ABI/testing/sysfs-class-spi-master b/Documentation/ABI/testing/sysfs-class-spi-master
new file mode 100644
index 000000000000..b498be128bad
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-spi-master
@@ -0,0 +1,34 @@
+What:		/sys/class/spi_master/spiB/new_device
+Date:		April 2026
+KernelVersion:	7.2
+Contact:	linux-spi@vger.kernel.org
+Description:	(WO) Instantiate a new SPI device on bus B, where B
+		is the bus number (0, 1, 2, ...). Takes parameters
+		in the format:
+
+		<modalias> <chip_select> [<max_speed_hz> [<mode>]]
+
+		where modalias is the driver name, chip_select is the
+		CS line number, and max_speed_hz and mode are optional.
+
+		The device can later be removed with delete_device.
+
+		Only devices created via this interface can be removed
+		with delete_device; platform and DT devices are not
+		affected.
+
+		Example:
+		# echo spidev 0 > /sys/class/spi_master/spi0/new_device
+		# echo spidev 0 10000000 > /sys/class/spi_master/spi0/new_device
+		# echo spidev 0 10000000 3 > /sys/class/spi_master/spi0/new_device
+
+What:		/sys/class/spi_master/spiB/delete_device
+Date:		April 2026
+KernelVersion:	7.2
+Contact:	linux-spi@vger.kernel.org
+Description:	(WO) Remove a SPI device previously created via
+		new_device. Takes a single parameter: the chip select
+		number of the device to remove.
+
+		Example:
+		# echo 0 > /sys/class/spi_master/spi0/delete_device
diff --git a/Documentation/spi/index.rst b/Documentation/spi/index.rst
index ac0c2233ce48..3f723e2c07da 100644
--- a/Documentation/spi/index.rst
+++ b/Documentation/spi/index.rst
@@ -8,6 +8,7 @@ Serial Peripheral Interface (SPI)
    :maxdepth: 1
 
    spi-summary
+   instantiating-devices
    spidev
    multiple-data-lanes
    butterfly
diff --git a/Documentation/spi/instantiating-devices.rst b/Documentation/spi/instantiating-devices.rst
new file mode 100644
index 000000000000..9ed08d94ae01
--- /dev/null
+++ b/Documentation/spi/instantiating-devices.rst
@@ -0,0 +1,88 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+How to instantiate SPI devices
+==============================
+
+SPI devices are normally declared statically via device-tree, ACPI, or
+board files. When the SPI controller is registered, these devices are
+instantiated automatically by the SPI core. This is the preferred method
+for any device with a proper kernel driver.
+
+Instantiate from user-space
+---------------------------
+
+In certain cases a SPI device cannot be declared statically:
+
+* The ``spidev`` driver, which provides raw userspace access to SPI
+  buses, explicitly rejects the bare ``"spidev"`` compatible string in
+  device-tree because spidev is a Linux implementation detail, not a
+  hardware description. Vendor-specific compatible strings for spidev
+  (e.g. ``"vendor,board-spidev"``) are also generally not accepted
+  upstream. Device-tree overlays do not help here either, since the
+  spidev driver performs the same compatible check regardless of how
+  the DT node was loaded.
+
+* You are developing or testing a SPI device on a development board
+  where the SPI bus is exposed on expansion headers, and the connected
+  device may change frequently.
+
+For these cases, a sysfs interface is provided on each SPI controller
+(similar to the I2C ``new_device``/``delete_device`` interface described
+in Documentation/i2c/instantiating-devices.rst). Two write-only
+attribute files are created in every SPI controller directory:
+``new_device`` and ``delete_device``.
+
+File ``new_device`` takes 2 to 4 parameters: the name of the SPI
+device (a string), the chip select number, and optionally
+``max_speed_hz`` and ``mode``::
+
+  <modalias> <chip_select> [<max_speed_hz> [<mode>]]
+
+The modalias is set both as the device's ``modalias`` field and as its
+``driver_override``. This ensures that the device binds to the named
+driver directly, bypassing the normal bus matching logic (OF, ACPI,
+and ``id_table``). This is necessary because drivers like ``spidev``
+deliberately exclude generic names from their ``id_table``.
+
+If ``max_speed_hz`` is omitted or 0, ``spi_setup()`` clamps it to
+the controller's maximum speed. If ``mode`` is omitted, SPI mode 0
+(CPOL=0, CPHA=0) is used.
+
+File ``delete_device`` takes a single parameter: the chip select
+number. As no two devices can share a chip select on a given SPI bus,
+the chip select is sufficient to uniquely identify the device.
+
+Examples::
+
+  # Create a spidev device on SPI bus 0, chip select 0
+  echo spidev 0 > /sys/class/spi_master/spi0/new_device
+
+  # Create with explicit clock rate and SPI mode
+  echo spidev 0 10000000 3 > /sys/class/spi_master/spi0/new_device
+
+  # Remove the device
+  echo 0 > /sys/class/spi_master/spi0/delete_device
+
+On systems that need spidev access at boot, a systemd service or
+udev rule can write to ``new_device`` after the SPI controller is
+available.
+
+Limitations
+^^^^^^^^^^^
+
+Devices created through this interface have the following limitations
+compared to devices declared via device-tree:
+
+* No interrupt (IRQ) support.
+* No additional properties such as ``spi-max-frequency`` DT bindings
+  or controller-specific configuration.
+* No platform data or software nodes.
+
+For ``spidev`` usage these limitations are not relevant, since spidev
+provides a raw byte-level interface that does not require any of these
+features.
+
+Only devices created via ``new_device`` can be removed through
+``delete_device``. Devices declared via device-tree, ACPI, or board
+files are not affected by this interface.
-- 
2.17.1


^ permalink raw reply related

* [PATCH v5 1/2] spi: add new_device/delete_device sysfs interface
From: Vishwaroop A @ 2026-05-17 20:16 UTC (permalink / raw)
  To: broonie, linux-spi
  Cc: smangipudi, jonathanh, thierry.reding, corbet, linux-doc, va
In-Reply-To: <20260517201602.498135-1-va@nvidia.com>

Development boards such as the Jetson AGX Orin expose SPI buses
on expansion headers (e.g. the 40-pin header) so that users can
connect and interact with SPI peripherals from userspace. The
standard way to get /dev/spidevB.C character device nodes for
this purpose is to register spi_device instances backed by the
spidev driver.

Today there is no viable way to do this on upstream kernels:

  - The spidev driver rejects the bare "spidev" compatible
    string in DT, since spidev is a Linux software interface
    and not a description of real hardware.
  - Vendor-specific compatible strings (e.g. "nvidia,tegra-spidev")
    have been rejected by DT maintainers for the same reason.

The I2C subsystem solved an analogous problem by exposing
new_device/delete_device sysfs attributes on each adapter. Add
the same interface to SPI host controllers, so that userspace
(e.g. a systemd unit at boot) can instantiate SPI devices at
runtime without needing anything in device-tree.

The new_device file accepts:

  <modalias> <chip_select> [<max_speed_hz> [<mode>]]

where chip_select is required, while max_speed_hz and mode are
optional and default to 0 if omitted. max_speed_hz == 0 is
clamped to the controller's maximum by spi_setup(); mode == 0
selects SPI mode 0 (CPOL=0, CPHA=0).

The modalias is used both as the device identifier and as a
driver_override, so that the device binds to the named driver
directly. This is necessary because some drivers like spidev
deliberately exclude generic names from their id_table.

Devices created this way are limited compared to those declared
via DT or board files:

  - No IRQ is assigned (the device gets IRQ 0 / no interrupt).
  - No platform_data or device properties are attached.
  - No OF node is associated with the device.

These limitations are acceptable for spidev, which only needs a
registered spi_device to expose a character device to userspace.

Only devices created via new_device can be removed through
delete_device; DT and platform devices are unaffected.

The sysfs attributes are gated behind CONFIG_SPI_DYNAMIC since
this feature adds a new way of dynamically instantiating and
removing SPI devices, and the add_lock locking in
spi_unregister_controller() is already conditional on
CONFIG_SPI_DYNAMIC.

A 'dead' flag on spi_controller prevents new device registration
and list insertion after spi_unregister_controller() begins
tearing down the controller. This avoids:

  1. An ABBA deadlock between add_lock and the kernfs active
     reference held by sysfs store callbacks. add_lock is
     released before device_del() so that in-flight sysfs
     operations can drain.

  2. Orphaned devices that could slip through after the
     userspace_clients cleanup but before device_del().

  3. Use-after-free if __unregister frees a device that
     new_device_store() still references. An extra get_device()
     before spi_add_device() holds the device alive.

Link: https://lore.kernel.org/linux-tegra/909f0c92-d110-4253-903e-5c81e21e12c9@nvidia.com/

Signed-off-by: Vishwaroop A <va@nvidia.com>
---
 drivers/spi/spi.c       | 217 ++++++++++++++++++++++++++++++++++++++--
 include/linux/spi/spi.h |  13 +++
 2 files changed, 224 insertions(+), 6 deletions(-)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 7001f5dce8bd..9042c19746b8 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -296,11 +296,188 @@ static const struct attribute_group spi_controller_statistics_group = {
 	.attrs  = spi_controller_statistics_attrs,
 };
 
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+
+/*
+ * new_device_store - instantiate a new SPI device from userspace
+ *
+ * Takes parameters: <modalias> <chip_select> [<max_speed_hz> [<mode>]]
+ *
+ * Examples:
+ *   echo spidev 0 > new_device
+ *   echo spidev 0 10000000 > new_device
+ *   echo spidev 0 10000000 3 > new_device
+ */
+static ssize_t
+new_device_store(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+	struct spi_controller *ctlr = container_of(dev, struct spi_controller,
+						   dev);
+	struct spi_device *spi;
+	char modalias[SPI_NAME_SIZE];
+	u16 chip_select;
+	u32 max_speed_hz = 0;
+	u32 mode = 0;
+	char *blank;
+	int n, res, status;
+
+	blank = strchr(buf, ' ');
+	if (!blank) {
+		dev_err(dev, "%s: Missing parameters\n", "new_device");
+		return -EINVAL;
+	}
+
+	if (blank - buf > SPI_NAME_SIZE - 1) {
+		dev_err(dev, "%s: Invalid device name\n", "new_device");
+		return -EINVAL;
+	}
+
+	memset(modalias, 0, sizeof(modalias));
+	memcpy(modalias, buf, blank - buf);
+
+	/*
+	 * sscanf fills only the fields it matches; unmatched optional
+	 * fields (max_speed_hz, mode) stay zero from initialisation above.
+	 * max_speed_hz == 0 is clamped to the controller max by spi_setup().
+	 * mode == 0 selects SPI mode 0 (CPOL=0, CPHA=0).
+	 */
+	res = sscanf(++blank, "%hu %u %u%n",
+		     &chip_select, &max_speed_hz, &mode, &n);
+	if (res < 1) {
+		dev_err(dev, "%s: Can't parse chip select\n", "new_device");
+		return -EINVAL;
+	}
+
+	if (chip_select >= ctlr->num_chipselect) {
+		dev_err(dev, "%s: Chip select %u >= max %u\n", "new_device",
+			chip_select, ctlr->num_chipselect);
+		return -EINVAL;
+	}
+
+	spi = spi_alloc_device(ctlr);
+	if (!spi)
+		return -ENOMEM;
+
+	spi_set_chipselect(spi, 0, chip_select);
+	spi->max_speed_hz = max_speed_hz;
+	spi->mode = mode;
+	spi->cs_index_mask = BIT(0);
+	strscpy(spi->modalias, modalias, sizeof(spi->modalias));
+
+	/*
+	 * Set driver_override so that the device binds to the driver
+	 * named by modalias regardless of whether that driver's
+	 * id_table contains a matching entry.  This is needed because
+	 * some drivers (e.g. spidev) deliberately omit generic names
+	 * from their id_table.
+	 */
+	status = device_set_driver_override(&spi->dev, modalias);
+	if (status) {
+		spi_dev_put(spi);
+		return status;
+	}
+
+	/* Extra ref so concurrent __unregister cannot free the device */
+	get_device(&spi->dev);
+
+	status = spi_add_device(spi);
+	if (status) {
+		put_device(&spi->dev);
+		spi_dev_put(spi);
+		return status;
+	}
+
+	mutex_lock(&ctlr->userspace_clients_lock);
+	if (!ctlr->dead) {
+		list_add_tail(&spi->userspace_node, &ctlr->userspace_clients);
+		mutex_unlock(&ctlr->userspace_clients_lock);
+		put_device(&spi->dev);
+		dev_info(dev, "%s: Instantiated device %s at CS%u\n",
+			 "new_device", modalias, chip_select);
+		return count;
+	}
+	mutex_unlock(&ctlr->userspace_clients_lock);
+
+	/*
+	 * Controller is dying; __unregister will clean up the device.
+	 * Drop our extra ref and bail.
+	 */
+	put_device(&spi->dev);
+	return -ENODEV;
+}
+static DEVICE_ATTR_WO(new_device);
+
+static ssize_t
+delete_device_store(struct device *dev, struct device_attribute *attr,
+		    const char *buf, size_t count)
+{
+	struct spi_controller *ctlr = container_of(dev, struct spi_controller,
+						   dev);
+	struct spi_device *spi, *next;
+	unsigned short cs;
+	char end;
+	int res;
+
+	res = sscanf(buf, "%hu%c", &cs, &end);
+	if (res < 1) {
+		dev_err(dev, "%s: Can't parse chip select\n", "delete_device");
+		return -EINVAL;
+	}
+	if (res > 1 && end != '\n') {
+		dev_err(dev, "%s: Extra parameters\n", "delete_device");
+		return -EINVAL;
+	}
+
+	res = -ENOENT;
+	mutex_lock(&ctlr->userspace_clients_lock);
+	list_for_each_entry_safe(spi, next, &ctlr->userspace_clients,
+				 userspace_node) {
+		if (spi_get_chipselect(spi, 0) == cs) {
+			dev_info(dev, "%s: Deleting device %s at CS%u\n",
+				 "delete_device", spi->modalias, cs);
+
+			list_del(&spi->userspace_node);
+			spi_unregister_device(spi);
+			res = count;
+			break;
+		}
+	}
+	mutex_unlock(&ctlr->userspace_clients_lock);
+
+	if (res < 0)
+		dev_err(dev, "%s: Can't find device in list\n",
+			"delete_device");
+	return res;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
+				   delete_device_store);
+
+static struct attribute *spi_controller_userspace_attrs[] = {
+	&dev_attr_new_device.attr,
+	&dev_attr_delete_device.attr,
+	NULL,
+};
+
+static const struct attribute_group spi_controller_userspace_group = {
+	.attrs = spi_controller_userspace_attrs,
+};
+
 static const struct attribute_group *spi_controller_groups[] = {
 	&spi_controller_statistics_group,
+	&spi_controller_userspace_group,
 	NULL,
 };
 
+#else /* !CONFIG_SPI_DYNAMIC */
+
+static const struct attribute_group *spi_controller_groups[] = {
+	&spi_controller_statistics_group,
+	NULL,
+};
+
+#endif /* CONFIG_SPI_DYNAMIC */
+
 static void spi_statistics_add_transfer_stats(struct spi_statistics __percpu *pcpu_stats,
 					      struct spi_transfer *xfer,
 					      struct spi_message *msg)
@@ -724,10 +901,10 @@ static int __spi_add_device(struct spi_device *spi, struct spi_device *parent)
 		return status;
 
 	/* Controller may unregister concurrently */
-	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) &&
-	    !device_is_registered(&ctlr->dev)) {
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+	if (ctlr->dead)
 		return -ENODEV;
-	}
+#endif
 
 	if (ctlr->cs_gpiods) {
 		u8 cs;
@@ -3256,6 +3433,10 @@ struct spi_controller *__spi_alloc_controller(struct device *dev,
 	mutex_init(&ctlr->bus_lock_mutex);
 	mutex_init(&ctlr->io_mutex);
 	mutex_init(&ctlr->add_lock);
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+	mutex_init(&ctlr->userspace_clients_lock);
+	INIT_LIST_HEAD(&ctlr->userspace_clients);
+#endif
 	ctlr->bus_num = -1;
 	ctlr->num_chipselect = 1;
 	ctlr->num_data_lanes = 1;
@@ -3633,8 +3814,35 @@ void spi_unregister_controller(struct spi_controller *ctlr)
 	if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
 		mutex_lock(&ctlr->add_lock);
 
+	/*
+	 * Mark dead and drain userspace_clients before __unregister,
+	 * since spi_unregister_device() doesn't do list_del() itself.
+	 */
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+	mutex_lock(&ctlr->userspace_clients_lock);
+	ctlr->dead = true;
+	while (!list_empty(&ctlr->userspace_clients)) {
+		struct spi_device *spi;
+
+		spi = list_first_entry(&ctlr->userspace_clients,
+				       struct spi_device,
+				       userspace_node);
+		list_del(&spi->userspace_node);
+		spi_unregister_device(spi);
+	}
+	mutex_unlock(&ctlr->userspace_clients_lock);
+#endif
+
 	device_for_each_child(&ctlr->dev, NULL, __unregister);
 
+	/*
+	 * Release add_lock before device_del(): holding it would
+	 * deadlock against kernfs_drain waiting for in-flight sysfs
+	 * stores.  ctlr->dead prevents new device registration.
+	 */
+	if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
+		mutex_unlock(&ctlr->add_lock);
+
 	/* First make sure that this controller was ever added */
 	mutex_lock(&board_lock);
 	found = idr_find(&spi_controller_idr, id);
@@ -3655,9 +3863,6 @@ void spi_unregister_controller(struct spi_controller *ctlr)
 		idr_remove(&spi_controller_idr, id);
 	mutex_unlock(&board_lock);
 
-	if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
-		mutex_unlock(&ctlr->add_lock);
-
 	/*
 	 * Release the last reference on the controller if its driver
 	 * has not yet been converted to devm_spi_alloc_host/target().
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 7587b1c5d7ec..7a86749d2701 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -250,6 +250,10 @@ struct spi_device {
 	u8			rx_lane_map[SPI_DEVICE_DATA_LANE_CNT_MAX];
 	u8			num_rx_lanes;
 
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+	struct list_head	userspace_node;
+#endif
+
 	/*
 	 * Likely need more hooks for more protocol options affecting how
 	 * the controller talks to each chip, like:
@@ -554,6 +558,9 @@ extern struct spi_device *devm_spi_new_ancillary_device(struct spi_device *spi,
  * @defer_optimize_message: set to true if controller cannot pre-optimize messages
  *	and needs to defer the optimization step until the message is actually
  *	being transferred
+ * @userspace_clients: list of SPI devices instantiated from userspace via
+ *	the sysfs new_device interface
+ * @userspace_clients_lock: mutex protecting @userspace_clients
  *
  * Each SPI controller can communicate with one or more @spi_device
  * children.  These make a small bus, sharing MOSI, MISO and SCK signals
@@ -809,6 +816,12 @@ struct spi_controller {
 	bool			queue_empty;
 	bool			must_async;
 	bool			defer_optimize_message;
+
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+	struct list_head	userspace_clients;
+	struct mutex		userspace_clients_lock;
+	bool			dead;
+#endif
 };
 
 static inline void *spi_controller_get_devdata(struct spi_controller *ctlr)
-- 
2.17.1


^ permalink raw reply related

* [PATCH v5 0/2] spi: add new_device/delete_device sysfs interface
From: Vishwaroop A @ 2026-05-17 20:16 UTC (permalink / raw)
  To: broonie, linux-spi
  Cc: smangipudi, jonathanh, thierry.reding, corbet, linux-doc, va

Add I2C-style new_device/delete_device sysfs attributes to SPI host
controllers, allowing userspace to instantiate and remove SPI devices
at runtime without device-tree changes.

Changes since v4:
  - Removed spi_unregister_device() call from new_device_store()'s
    ctlr->dead teardown path.  That call raced with
    device_for_each_child(__unregister) in spi_unregister_controller(),
    causing a double-free.  The extra get_device() ref keeps the struct
    alive; __unregister handles the actual cleanup.

Changes since v3:
  - Replaced holding add_lock across __spi_add_device() + list
    insertion (which caused an ABBA deadlock between add_lock and the
    kernfs active reference during concurrent unbind) with:
    * A 'dead' flag on spi_controller, set in
      spi_unregister_controller() under both add_lock and
      userspace_clients_lock.
    * __spi_add_device() checks ctlr->dead under add_lock to reject
      new devices after teardown begins.
    * new_device_store() checks ctlr->dead under userspace_clients_lock
      before list insertion, falling back to cleanup + ENODEV.
    * add_lock is released before device_del() so in-flight sysfs
      stores can drain without deadlocking.
    * get_device() taken before spi_add_device() prevents
      use-after-free if __unregister runs concurrently.
  - Used #if IS_ENABLED() preprocessor guard (not runtime IS_ENABLED())
    for the ctlr->dead check in __spi_add_device(), since the dead
    field is conditionally compiled.

Changes since v2:
  - Gated sysfs attributes and locking on CONFIG_SPI_DYNAMIC.

Changes since v1:
  - Added locking to prevent races between new_device_store() and
    concurrent spi_unregister_controller().

Link: https://lore.kernel.org/linux-tegra/909f0c92-d110-4253-903e-5c81e21e12c9@nvidia.com/

Vishwaroop A (2):
  spi: add new_device/delete_device sysfs interface
  docs: spi: add documentation for userspace device instantiation

 .../ABI/testing/sysfs-class-spi-master        |  34 +++
 Documentation/spi/index.rst                   |   1 +
 Documentation/spi/instantiating-devices.rst   |  88 +++++++
 drivers/spi/spi.c                             | 217 +++++++++++++++++-
 include/linux/spi/spi.h                       |  13 ++
 5 files changed, 347 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-spi-master
 create mode 100644 Documentation/spi/instantiating-devices.rst

-- 
2.17.1


^ permalink raw reply

* Re: [PATCH v3] killswitch: add per-function short-circuit mitigation primitive
From: Brandon Taylor @ 2026-05-17 19:19 UTC (permalink / raw)
  To: Sasha Levin, linux-kernel
  Cc: linux-doc, linux-kselftest, bpf, live-patching,
	Greg Kroah-Hartman, Andrew Morton, Jonathan Corbet,
	Mathieu Desnoyers, Joshua Peisach, Florian Weimer, Breno Leitao,
	Anthony Iliopoulos, Michal Hocko, Jiri Olsa
In-Reply-To: <20260517134858.146569-1-sashal@kernel.org>

Have we learned NOTHING from just over 9 and a half years ago?!

I do not pretend to be a prophet of Linus, but I cannot for the life of 
me help but get flashbacks from kernel version 4.8 when Linus himself 
did not explain, but EXPLODED, in saying "there is NO F*CKING EXCUSE to 
knowingly kill the kernel."

So for me to hear about THIS from a YouTube video, the fact that we are 
still--STILL!--coming up with new ways to do something which we ought to 
KNOW to be ABSOLUTELY UNACCEPTABLE and DOWNRIGHT INTOLERABLE, BOILS MY 
BLOOD TO NO END.

You ought to consider yourself lucky that it's ME writing this and not 
Linus, because he'd be saying the exact same thing, and making it God 
knows how many times worse. He would break his foot off in somebody's 
BEHIND over this "killswitch" idiocy, and NEVER MIND that it was 
supposedly "designed" to prevent exploits like Fragnesia, Copy Fail, and 
Dirty Frag from creating havoc in Linux distributions, ESPECIALLY his 
go-to in Fedora!

Forgive me (especially you, Master Linus) for blowing my stack over 
this, but we all ought to take a lesson from the past:

Killing the Linux kernel is NOT an acceptable method to mitigate exploits.

I don't care HOW long it takes, but we HAVE TO PATCH THOSE 
VULNERABILITIES, and we HAVE to do it the RIGHT WAY, NOT just introduce 
some kernel-killing "failsafe" just because somebody doesn't know how to 
plug those holes.

I don't care--and neither will Linus--about the so-called "simplest 
mitigation," and neither should you. We should all care that we get the 
code RIGHT.

Brandon

On 5/17/2026 8:48 AM, Sasha Levin wrote:
> When a kernel (security) issue goes public, fleets stay exposed until a patched
> kernel is built, distributed, and rebooted into.
>
> For many such issues the simplest mitigation is to stop calling the buggy
> function. Killswitch provides that. An admin writes:
>
>      echo "engage af_alg_sendmsg -1" \
>          > /sys/kernel/security/killswitch/control
>
> After this, af_alg_sendmsg() returns -EPERM on every call without
> running its body. The mitigation takes effect immediately, and is dropped on
> the next reboot -- by which point a patched kernel is hopefully in place.
>
> A lot of recent kernel issues sit in code paths most installs only have enabled
> to support a relative minority of users: AF_ALG, ksmbd, nf_tables, vsock, ax25,
> and friends.
>
> For most users, the cost of "this socket family stops working for the day" is
> much smaller than the cost of running a known vulnerable kernel until the fix
> lands.
>
> Why not an existing facility:
>
> * livepatch needs a built, signed, per-kernel-version module per CVE.
>    Under Secure Boot the operator can't sign their own, so they wait
>    for the vendor, and only a minority of vendors actually ship
>    livepatches. Killswitch covers the days before that module shows
>    up.
>
> * fail_function (CONFIG_FUNCTION_ERROR_INJECTION) is disabled in
>    most production kernels. Even where enabled, it only works on
>    functions pre-annotated with ALLOW_ERROR_INJECTION() in source -
>    no help for a freshly-disclosed CVE. The debugfs UI is blocked by
>    lockdown=integrity and the override is probabilistic.
>
> * BPF override (bpf_override_return) honors the same
>    ALLOW_ERROR_INJECTION() whitelist, and BPF itself is off in many
>    production kernels. Even where on, the operator interface is
>    "load a verified BPF program," not a one-line write.
>
> * Module blacklist only helps when the bug is in a loadable module.
>
> Killswitch fills the gap: write a symbol to securityfs, function
> returns the chosen value until disengage or reboot.
>
> Assisted-by: Claude:claude-opus-4-7
> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---
>
> Changes since v2:
> - Fix LLVM=1 build: gate __noipa__ on __has_attribute() (Breno)
> - Admin guide: do-not-engage list, pre-soak workflow, relation to
>    livepatch/fail_function/BPF (Michal, Mathieu, Joshua)
> - Add CVE-2026-43284 (esp_input) worked example + netns selftest
> - Drop unused [reason] token from Kconfig help and cmdline comment
> - Commit message: spell out why livepatch / fail_function / BPF
>    override / module-blacklist don't cover this window.
>
>   Documentation/admin-guide/index.rst           |   1 +
>   Documentation/admin-guide/killswitch.rst      | 229 +++++
>   Documentation/admin-guide/tainted-kernels.rst |   8 +
>   MAINTAINERS                                   |  11 +
>   include/linux/killswitch.h                    |  19 +
>   include/linux/panic.h                         |   3 +-
>   include/linux/security.h                      |   1 +
>   init/Kconfig                                  |   2 +
>   kernel/Kconfig.killswitch                     |  31 +
>   kernel/Makefile                               |   1 +
>   kernel/killswitch.c                           | 863 ++++++++++++++++++
>   kernel/panic.c                                |   1 +
>   lib/Kconfig.debug                             |  13 +
>   lib/Makefile                                  |   1 +
>   lib/test_killswitch.c                         |  85 ++
>   security/security.c                           |   1 +
>   tools/testing/selftests/Makefile              |   1 +
>   tools/testing/selftests/killswitch/.gitignore |   1 +
>   tools/testing/selftests/killswitch/Makefile   |   8 +
>   .../selftests/killswitch/cve_31431_test.c     | 162 ++++
>   .../selftests/killswitch/cve_43284_test.c     |  88 ++
>   .../selftests/killswitch/killswitch_test.sh   | 254 ++++++
>   22 files changed, 1783 insertions(+), 1 deletion(-)
>   create mode 100644 Documentation/admin-guide/killswitch.rst
>   create mode 100644 include/linux/killswitch.h
>   create mode 100644 kernel/Kconfig.killswitch
>   create mode 100644 kernel/killswitch.c
>   create mode 100644 lib/test_killswitch.c
>   create mode 100644 tools/testing/selftests/killswitch/.gitignore
>   create mode 100644 tools/testing/selftests/killswitch/Makefile
>   create mode 100644 tools/testing/selftests/killswitch/cve_31431_test.c
>   create mode 100644 tools/testing/selftests/killswitch/cve_43284_test.c
>   create mode 100755 tools/testing/selftests/killswitch/killswitch_test.sh
>
> diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst
> index cd28dfe91b060..ca37dd70f108d 100644
> --- a/Documentation/admin-guide/index.rst
> +++ b/Documentation/admin-guide/index.rst
> @@ -70,6 +70,7 @@ problems and bugs in particular.
>      bug-hunting
>      bug-bisect
>      tainted-kernels
> +   killswitch
>      ramoops
>      dynamic-debug-howto
>      init
> diff --git a/Documentation/admin-guide/killswitch.rst b/Documentation/admin-guide/killswitch.rst
> new file mode 100644
> index 0000000000000..a524cc9ee23ca
> --- /dev/null
> +++ b/Documentation/admin-guide/killswitch.rst
> @@ -0,0 +1,229 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +..
> +.. Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
> +
> +============
> +Killswitch
> +============
> +
> +Killswitch lets a privileged operator make a chosen kernel function
> +return a fixed value without executing its body, as a temporary
> +mitigation for a security bug while a real fix is being prepared.
> +
> +The function returns the operator-supplied value and nothing else
> +runs in its place. There is no allowlist, no return-type check; if
> +the kprobe layer accepts the symbol, killswitch engages it. Once
> +engaged, the change is in effect on every CPU until ``disengage`` is
> +written or the system reboots.
> +
> +Configuration
> +=============
> +
> +``CONFIG_KILLSWITCH``
> +  Enables the feature. Depends on ``SECURITYFS``, ``KPROBES`` (with
> +  ftrace support), and ``FUNCTION_ERROR_INJECTION``.
> +
> +The interface
> +=============
> +
> +::
> +
> +    /sys/kernel/security/killswitch/
> +        engaged                 RO  currently-engaged functions
> +        control                 WO  command sink
> +        taint                   RO  0 or 1
> +        fn/<name>/              per-function directory, created on engage
> +            retval              RW  return value
> +            hits                RO  per-cpu summed call count
> +
> +Three commands are accepted by ``control``::
> +
> +    engage <symbol> <retval>
> +    disengage <symbol>
> +    disengage_all
> +
> +Each engage and disengage emits a single ``KERN_WARNING`` line to
> +dmesg with the symbol, retval, hit count (on disengage), and the
> +operator's identity (uid/auid/sessionid/comm, or ``source=cmdline``).
> +
> +Engagement is rejected when:
> +
> +* the symbol is unknown, in a non-traceable section, on the kprobe
> +  blacklist, or otherwise refused by ``register_kprobe`` (the error
> +  from the kprobe layer is logged and returned to userspace);
> +* the symbol is already engaged (``-EBUSY``);
> +* the operator does not hold ``CAP_SYS_ADMIN``.
> +
> +Whatever value the operator writes is what the function returns.
> +Writing the wrong type or wrong value lands in the caller as-is.
> +
> +Boot parameter
> +==============
> +
> +``killswitch=fn1=<val>,fn2=<val>,...``
> +
> +Parsed early; engagements are applied at the end of kernel init
> +once the kprobe subsystem is up. Parse failures emit a warning and
> +skip the offending entry; they never panic.
> +
> +Useful for fleet rollout: when an issue drops, ship the mitigation
> +in the bootloader / PXE config and roll the fleet through reboots
> +while the real fix is being prepared.
> +
> +Tainting
> +========
> +
> +The first successful engagement (runtime or boot-time) sets
> +``TAINT_KILLSWITCH`` (bit 20, char ``H``). The taint persists across
> +``disengage`` until reboot, so an oops on a killswitch-modified
> +kernel is identifiable from the banner: ``Tainted: ... H`` tells a
> +maintainer to consult ``engaged`` before further triage.
> +
> +Module unload
> +=============
> +
> +If a module containing an engaged target is unloaded, killswitch
> +auto-disengages the entry and emits a ``KERN_WARNING`` so the loss
> +of mitigation is visible. Reloading the module does not silently
> +re-arm the killswitch; the operator re-engages explicitly.
> +
> +Choosing the right target
> +=========================
> +
> +A function that *looks* skippable may be relied on by callers for a
> +side effect (a lock the caller releases, a refcount the caller
> +drops, a scatterlist the caller consumes). The rule of thumb:
> +
> +  Pick the **highest-level** entry point that contains the bug.
> +
> +That gives callers no chance to dereference half-initialised state
> +from a function whose body was skipped. Two illustrative examples
> +from ``crypto/af_alg.c``:
> +
> +Anti-pattern: ``af_alg_count_tsgl``
> +-----------------------------------
> +
> +``af_alg_count_tsgl()`` returns ``unsigned int`` (the number of TX
> +SG entries). Engaging it with retval ``0`` causes the caller in
> +``algif_aead.c`` to allocate a 1-entry scatterlist (its
> +``if (!entries) entries = 1`` guard) and then walk the *real* TX
> +SGL into that undersized destination via ``af_alg_pull_tsgl``,
> +producing out-of-bounds writes. **Killswitching here introduces a
> +worse bug than the one being mitigated.**
> +
> +Anti-pattern: ``af_alg_pull_tsgl``
> +----------------------------------
> +
> +``af_alg_pull_tsgl()`` returns ``void``, so any retval is accepted.
> +But its caller depends on the per-request SGL being filled in.
> +Skipping the body leaves the per-request SGL with NULL pages; the
> +next-stage ``memcpy_sglist`` dereferences them and the kernel
> +oopses.
> +
> +Correct pattern: ``af_alg_sendmsg``
> +-----------------------------------
> +
> +``af_alg_sendmsg()`` is the highest-level entry into the AF_ALG
> +send path. Engaging it with retval ``-EPERM`` causes every send
> +attempt to return -EPERM to userspace; no caller ever sees
> +half-initialised state, and any AF_ALG-reachable bug downstream of
> +``sendmsg`` is unreachable until the killswitch is disengaged.
> +
> +The canonical pattern: pick a syscall-handler-shaped function whose
> +return value already encodes "this operation didn't happen", and
> +let userspace handle the error as it would any other failed
> +syscall.
> +
> +Correct pattern: ``esp_input`` (CVE-2026-43284)
> +-----------------------------------------------
> +
> +The IPsec ESP receive-path bug fixed by ``xfrm: esp: avoid in-place
> +decrypt on shared skb frags`` is reachable through ``esp_input()``
> +in ``net/ipv4/esp4.c`` (and ``esp6_input()`` for IPv6). Engage these
> +with retval ``-EINVAL``:
> +
> +::
> +
> +    echo "engage esp_input -22"  > /sys/kernel/security/killswitch/control
> +    echo "engage esp6_input -22" > /sys/kernel/security/killswitch/control
> +
> +Inbound ESP packets are then dropped before decapsulation, neutering
> +any bug downstream of the ESP receive path. IPsec tunnels stop
> +working; other networking is unaffected.
> +
> +Do not engage
> +=============
> +
> +Do not killswitch:
> +
> +* process or memory primitives the rest of the kernel needs to
> +  function: ``fork``, ``do_exit``, ``__alloc_pages``, ``kmalloc``,
> +  ``schedule``, anything in ``mm/`` reached by every allocation.
> +* hot paths in the scheduler, timekeeping, RCU, or interrupt entry.
> +* functions invoked from the killswitch path itself (``securityfs``,
> +  ``lockdown``, ``audit``, ``kprobe`` registration) -- the system
> +  may livelock or refuse to disengage.
> +* functions whose return value is read structurally (size, count,
> +  pointer-to-allocated-thing) rather than as success/failure.
> +  See the AF_ALG anti-patterns above for what goes wrong.
> +
> +When in doubt, measure first.
> +
> +Pre-soak before engaging
> +========================
> +
> +If the target's call rate is unknown, attach a counter for a few
> +seconds first. With perf::
> +
> +    perf probe --add 'esp_input'
> +    perf stat -a -e probe:esp_input -- sleep 5
> +
> +Or with bpftrace::
> +
> +    bpftrace -e 'kprobe:esp_input { @hits = count(); } interval:s:5 { exit(); }'
> +
> +A target with ten thousand hits per second is not a candidate -- the
> +kernel will not survive five seconds with that path returning a
> +fixed error.
> +
> +Relation to other facilities
> +============================
> +
> +* ``CONFIG_FUNCTION_ERROR_INJECTION`` provides the same architecture
> +  trampoline (``override_function_with_return``), which killswitch
> +  reuses. fail_function is debug-oriented: targets must be
> +  pre-annotated with ``ALLOW_ERROR_INJECTION()`` in source, the
> +  override is probabilistic, and the interface is on debugfs (blocked
> +  under ``lockdown=integrity``). Killswitch is the production cousin:
> +  no whitelist, deterministic, securityfs-visible under integrity
> +  lockdown, with audit and taint.
> +* livepatch can do everything killswitch can and more, at the cost
> +  of building, signing, and shipping a kernel module per mitigation.
> +  Killswitch is for the window before that module exists.
> +* BPF override (``bpf_override_return``) needs a BPF program and
> +  ``CONFIG_BPF_KPROBE_OVERRIDE``; appropriate when the policy is
> +  conditional, overkill for "always return -EPERM".
> +
> +Safety notes
> +============
> +
> +* In-flight calls during ``write()`` to ``control`` may run either
> +  the original body or the override. The override is ``return X``,
> +  which has no preconditions to violate.
> +* SMP visibility comes from ``text_poke_bp()``. ``write()`` to
> +  ``control`` returns only after every CPU sees the new path.
> +* The ftrace ops unregister waits for in-flight pre-handlers, so
> +  freeing the engagement attribute on disengage is safe.
> +* Inline functions, freed ``__init`` symbols, and anything compiled
> +  away cannot be killswitched. ``register_kprobe`` rejects them
> +  with whatever error the kprobe layer chooses.
> +
> +Diagnostics
> +===========
> +
> +Per-call hits are aggregated in a per-cpu counter readable at
> +``/sys/kernel/security/killswitch/fn/<name>/hits``. Per-hit logging
> +is not provided to avoid log storms on hot paths.
> +
> +A ``KILLSWITCH`` entry appears in the kernel taint vector once any
> +engagement succeeds (also visible as ``H`` in the oops banner).
> diff --git a/Documentation/admin-guide/tainted-kernels.rst b/Documentation/admin-guide/tainted-kernels.rst
> index 9ead927a37c0f..71a6e3364eddc 100644
> --- a/Documentation/admin-guide/tainted-kernels.rst
> +++ b/Documentation/admin-guide/tainted-kernels.rst
> @@ -102,6 +102,7 @@ Bit  Log  Number  Reason that got the kernel tainted
>    17  _/T  131072  kernel was built with the struct randomization plugin
>    18  _/N  262144  an in-kernel test has been run
>    19  _/J  524288  userspace used a mutating debug operation in fwctl
> + 20  _/H 1048576  killswitch override engaged (function short-circuited)
>   ===  ===  ======  ========================================================
>   
>   Note: The character ``_`` is representing a blank in this table to make reading
> @@ -189,3 +190,10 @@ More detailed explanation for tainting
>    19) ``J`` if userspace opened /dev/fwctl/* and performed a FWTCL_RPC_DEBUG_WRITE
>        to use the devices debugging features. Device debugging features could
>        cause the device to malfunction in undefined ways.
> +
> + 20) ``H`` if the killswitch primitive (see
> +     Documentation/admin-guide/killswitch.rst) has been engaged on at least
> +     one function. The kernel is no longer running its source: at least one
> +     function has been short-circuited to return a fixed value. The taint
> +     persists across ``disengage`` until the next reboot — once the running
> +     image has been modified, oops triage must reflect that.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index b2040011a3865..b4005b61d444f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14350,6 +14350,17 @@ F:	lib/Kconfig.kmsan
>   F:	mm/kmsan/
>   F:	scripts/Makefile.kmsan
>   
> +KILLSWITCH (function short-circuit mitigation)
> +M:	Sasha Levin <sashal@kernel.org>
> +L:	linux-kernel@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/admin-guide/killswitch.rst
> +F:	include/linux/killswitch.h
> +F:	kernel/Kconfig.killswitch
> +F:	kernel/killswitch.c
> +F:	lib/test_killswitch.c
> +F:	tools/testing/selftests/killswitch/
> +
>   KPROBES
>   M:	Naveen N Rao <naveen@kernel.org>
>   M:	"David S. Miller" <davem@davemloft.net>
> diff --git a/include/linux/killswitch.h b/include/linux/killswitch.h
> new file mode 100644
> index 0000000000000..3fad49e180ddf
> --- /dev/null
> +++ b/include/linux/killswitch.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
> + */
> +#ifndef _LINUX_KILLSWITCH_H
> +#define _LINUX_KILLSWITCH_H
> +
> +#ifdef CONFIG_KILLSWITCH
> +int killswitch_engage(const char *symbol, long retval);
> +int killswitch_disengage(const char *symbol);
> +bool killswitch_is_engaged(const char *symbol);
> +#else
> +static inline int killswitch_engage(const char *symbol, long retval)
> +{ return -EOPNOTSUPP; }
> +static inline int killswitch_disengage(const char *symbol) { return -EOPNOTSUPP; }
> +static inline bool killswitch_is_engaged(const char *symbol) { return false; }
> +#endif
> +
> +#endif /* _LINUX_KILLSWITCH_H */
> diff --git a/include/linux/panic.h b/include/linux/panic.h
> index f1dd417e54b29..6699261a61f13 100644
> --- a/include/linux/panic.h
> +++ b/include/linux/panic.h
> @@ -88,7 +88,8 @@ static inline void set_arch_panic_timeout(int timeout, int arch_default_timeout)
>   #define TAINT_RANDSTRUCT		17
>   #define TAINT_TEST			18
>   #define TAINT_FWCTL			19
> -#define TAINT_FLAGS_COUNT		20
> +#define TAINT_KILLSWITCH		20
> +#define TAINT_FLAGS_COUNT		21
>   #define TAINT_FLAGS_MAX			((1UL << TAINT_FLAGS_COUNT) - 1)
>   
>   struct taint_flag {
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 41d7367cf4036..038027c33ba1a 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -146,6 +146,7 @@ enum lockdown_reason {
>   	LOCKDOWN_DBG_WRITE_KERNEL,
>   	LOCKDOWN_RTAS_ERROR_INJECTION,
>   	LOCKDOWN_XEN_USER_ACTIONS,
> +	LOCKDOWN_KILLSWITCH,
>   	LOCKDOWN_INTEGRITY_MAX,
>   	LOCKDOWN_KCORE,
>   	LOCKDOWN_KPROBES,
> diff --git a/init/Kconfig b/init/Kconfig
> index 2937c4d308aec..5368dd4b5c65b 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -2278,6 +2278,8 @@ config ASN1
>   
>   source "kernel/Kconfig.locks"
>   
> +source "kernel/Kconfig.killswitch"
> +
>   config ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
>   	bool
>   
> diff --git a/kernel/Kconfig.killswitch b/kernel/Kconfig.killswitch
> new file mode 100644
> index 0000000000000..a33f7ecb2861e
> --- /dev/null
> +++ b/kernel/Kconfig.killswitch
> @@ -0,0 +1,31 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Killswitch: per-function short-circuit mitigation primitive.
> +#
> +# Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
> +#
> +
> +config KILLSWITCH
> +	bool "Killswitch: short-circuit a kernel function as a CVE mitigation"
> +	depends on SECURITYFS
> +	depends on KPROBES && HAVE_KPROBES_ON_FTRACE
> +	depends on HAVE_FUNCTION_ERROR_INJECTION
> +	select FUNCTION_ERROR_INJECTION
> +	help
> +	  Provide an admin-facing mechanism to make a chosen kernel function
> +	  return a fixed value without executing its body, as a temporary
> +	  mitigation for a security bug before a real fix is available.
> +
> +	  Operators write "engage <symbol> <retval>" to
> +	  /sys/kernel/security/killswitch/control. The function entry is
> +	  redirected via a kprobe whose pre-handler sets the chosen return
> +	  value and short-circuits the call. There is no allowlist,
> +	  denylist, or return-type validation: if the kprobe layer accepts
> +	  the symbol the engagement proceeds, otherwise its error is
> +	  returned to userspace.
> +
> +	  This is *not* livepatch: there is no replacement implementation,
> +	  the function simply returns the chosen value. Engaging a killswitch
> +	  taints the kernel (TAINT_KILLSWITCH, 'H'). Requires CAP_SYS_ADMIN.
> +
> +	  If unsure, say N.
> diff --git a/kernel/Makefile b/kernel/Makefile
> index 6785982013dce..b3e408d9f275e 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -100,6 +100,7 @@ obj-$(CONFIG_GCOV_KERNEL) += gcov/
>   obj-$(CONFIG_KCOV) += kcov.o
>   obj-$(CONFIG_KPROBES) += kprobes.o
>   obj-$(CONFIG_FAIL_FUNCTION) += fail_function.o
> +obj-$(CONFIG_KILLSWITCH) += killswitch.o
>   obj-$(CONFIG_KGDB) += debug/
>   obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
>   obj-$(CONFIG_LOCKUP_DETECTOR) += watchdog.o
> diff --git a/kernel/killswitch.c b/kernel/killswitch.c
> new file mode 100644
> index 0000000000000..7f509c62ea748
> --- /dev/null
> +++ b/kernel/killswitch.c
> @@ -0,0 +1,863 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Per-function short-circuit mitigation.
> + *
> + * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
> + *
> + * Engaging a killswitch installs a kprobe at the function's entry
> + * whose pre-handler sets the return register and skips the body via
> + * override_function_with_return().  Operator interface lives at
> + * /sys/kernel/security/killswitch/.
> + */
> +
> +#include <linux/audit.h>
> +#include <linux/capability.h>
> +#include <linux/cred.h>
> +#include <linux/ctype.h>
> +#include <linux/error-injection.h>
> +#include <linux/init.h>
> +#include <linux/killswitch.h>
> +#include <linux/kprobes.h>
> +#include <linux/kref.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/notifier.h>
> +#include <linux/panic.h>
> +#include <linux/percpu.h>
> +#include <linux/printk.h>
> +#include <linux/sched.h>
> +#include <linux/security.h>
> +#include <linux/seq_file.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/uaccess.h>
> +#include <linux/uidgid.h>
> +
> +struct ks_attr {
> +	struct list_head	list;
> +	struct kprobe		kp;
> +	/* atomic so a writer racing an in-flight call can't tear the long. */
> +	atomic_long_t		retval;
> +	/* false once disengaged; per-fn file ops then return -EIDRM. */
> +	bool			engaged;
> +	unsigned long __percpu	*hits;
> +	struct dentry		*dir;
> +	/* engaged_list holds one ref; each open per-fn fd holds one. */
> +	struct kref		refcnt;
> +};
> +
> +static DEFINE_MUTEX(ks_lock);
> +static LIST_HEAD(ks_engaged_list);
> +static struct dentry *ks_root_dir;
> +static struct dentry *ks_fn_dir;	/* parent for per-fn directories */
> +
> +/* ------------------------------------------------------------------ *
> + * Pre-handler: the actual override                                   *
> + * ------------------------------------------------------------------ */
> +
> +static int ks_kprobe_pre_handler(struct kprobe *kp, struct pt_regs *regs)
> +{
> +	struct ks_attr *attr = container_of(kp, struct ks_attr, kp);
> +
> +	this_cpu_inc(*attr->hits);
> +	regs_set_return_value(regs, (unsigned long)atomic_long_read(&attr->retval));
> +	override_function_with_return(regs);
> +	return 1;
> +}
> +NOKPROBE_SYMBOL(ks_kprobe_pre_handler);
> +
> +/* Defined non-NULL so the kprobe layer keeps the IPMODIFY ops. */
> +static void ks_kprobe_post_handler(struct kprobe *kp, struct pt_regs *regs,
> +				   unsigned long flags)
> +{
> +}
> +
> +/* ------------------------------------------------------------------ *
> + * Attribute lifecycle                                                *
> + * ------------------------------------------------------------------ */
> +
> +static struct ks_attr *ks_attr_lookup(const char *symbol)
> +{
> +	struct ks_attr *attr;
> +
> +	list_for_each_entry(attr, &ks_engaged_list, list)
> +		if (!strcmp(attr->kp.symbol_name, symbol))
> +			return attr;
> +	return NULL;
> +}
> +
> +static unsigned long ks_attr_hits(const struct ks_attr *attr)
> +{
> +	unsigned long total = 0;
> +	int cpu;
> +
> +	for_each_possible_cpu(cpu)
> +		total += *per_cpu_ptr(attr->hits, cpu);
> +	return total;
> +}
> +
> +static void ks_attr_destroy(struct ks_attr *attr)
> +{
> +	if (!attr)
> +		return;
> +	free_percpu(attr->hits);
> +	kfree(attr->kp.symbol_name);
> +	kfree(attr);
> +}
> +
> +static void ks_attr_kref_release(struct kref *kref)
> +{
> +	ks_attr_destroy(container_of(kref, struct ks_attr, refcnt));
> +}
> +
> +static void ks_attr_get(struct ks_attr *attr)
> +{
> +	kref_get(&attr->refcnt);
> +}
> +
> +static void ks_attr_put(struct ks_attr *attr)
> +{
> +	kref_put(&attr->refcnt, ks_attr_kref_release);
> +}
> +
> +static struct ks_attr *ks_attr_alloc(const char *symbol)
> +{
> +	struct ks_attr *attr;
> +
> +	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
> +	if (!attr)
> +		return NULL;
> +
> +	attr->kp.symbol_name = kstrdup(symbol, GFP_KERNEL);
> +	if (!attr->kp.symbol_name)
> +		goto err;
> +
> +	attr->hits = alloc_percpu(unsigned long);
> +	if (!attr->hits)
> +		goto err;
> +
> +	attr->kp.pre_handler = ks_kprobe_pre_handler;
> +	attr->kp.post_handler = ks_kprobe_post_handler;
> +	INIT_LIST_HEAD(&attr->list);
> +	kref_init(&attr->refcnt);
> +	return attr;
> +
> +err:
> +	ks_attr_destroy(attr);
> +	return NULL;
> +}
> +
> +/* ------------------------------------------------------------------ *
> + * Securityfs: per-fn attribute files                                 *
> + * ------------------------------------------------------------------ */
> +
> +/*
> + * Look up by symbol name (the parent dentry's basename) under
> + * ks_lock and confirm attr->dir is the file's parent dentry.  This
> + * binds the fd to the engagement it was opened against and avoids
> + * dereferencing inode->i_private, which a racing disengage may have
> + * freed.  d_parent is stable for the open's lifetime via the file's
> + * dentry reference.
> + */
> +static int ks_attr_open(struct inode *inode, struct file *file)
> +{
> +	struct dentry *parent = file->f_path.dentry->d_parent;
> +	const char *name = parent->d_name.name;
> +	struct ks_attr *attr;
> +
> +	mutex_lock(&ks_lock);
> +	attr = ks_attr_lookup(name);
> +	if (attr && attr->dir == parent)
> +		ks_attr_get(attr);
> +	else
> +		attr = NULL;
> +	mutex_unlock(&ks_lock);
> +	if (!attr)
> +		return -ENOENT;
> +	file->private_data = attr;
> +	return 0;
> +}
> +
> +static int ks_attr_release(struct inode *inode, struct file *file)
> +{
> +	ks_attr_put(file->private_data);
> +	file->private_data = NULL;
> +	return 0;
> +}
> +
> +/* Caller must hold ks_lock. */
> +static int ks_attr_check_live(const struct ks_attr *attr)
> +{
> +	return attr->engaged ? 0 : -EIDRM;
> +}
> +
> +static ssize_t ks_retval_read(struct file *file, char __user *ubuf,
> +			      size_t count, loff_t *ppos)
> +{
> +	struct ks_attr *attr = file->private_data;
> +	char buf[32];
> +	long val;
> +	int ret, len;
> +
> +	mutex_lock(&ks_lock);
> +	ret = ks_attr_check_live(attr);
> +	val = atomic_long_read(&attr->retval);
> +	mutex_unlock(&ks_lock);
> +	if (ret)
> +		return ret;
> +	len = scnprintf(buf, sizeof(buf), "%ld\n", val);
> +	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
> +}
> +
> +static ssize_t ks_retval_write(struct file *file, const char __user *ubuf,
> +			       size_t count, loff_t *ppos)
> +{
> +	struct ks_attr *attr = file->private_data;
> +	char buf[32];
> +	long val;
> +	int ret;
> +
> +	if (count >= sizeof(buf))
> +		return -EINVAL;
> +	if (copy_from_user(buf, ubuf, count))
> +		return -EFAULT;
> +	buf[count] = '\0';
> +	strim(buf);
> +
> +	ret = kstrtol(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&ks_lock);
> +	ret = ks_attr_check_live(attr);
> +	if (!ret)
> +		atomic_long_set(&attr->retval, val);
> +	mutex_unlock(&ks_lock);
> +
> +	return ret ? ret : count;
> +}
> +
> +static const struct file_operations ks_retval_fops = {
> +	.open		= ks_attr_open,
> +	.release	= ks_attr_release,
> +	.read		= ks_retval_read,
> +	.write	= ks_retval_write,
> +	.llseek	= default_llseek,
> +};
> +
> +static ssize_t ks_hits_read(struct file *file, char __user *ubuf,
> +			    size_t count, loff_t *ppos)
> +{
> +	struct ks_attr *attr = file->private_data;
> +	char buf[32];
> +	unsigned long hits;
> +	int ret, len;
> +
> +	mutex_lock(&ks_lock);
> +	ret = ks_attr_check_live(attr);
> +	hits = ks_attr_hits(attr);
> +	mutex_unlock(&ks_lock);
> +	if (ret)
> +		return ret;
> +	len = scnprintf(buf, sizeof(buf), "%lu\n", hits);
> +	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
> +}
> +
> +static const struct file_operations ks_hits_fops = {
> +	.open		= ks_attr_open,
> +	.release	= ks_attr_release,
> +	.read		= ks_hits_read,
> +	.llseek		= default_llseek,
> +};
> +
> +static int ks_create_attr_dir(struct ks_attr *attr)
> +{
> +	struct dentry *d;
> +
> +	attr->dir = securityfs_create_dir(attr->kp.symbol_name, ks_fn_dir);
> +	if (IS_ERR(attr->dir))
> +		return PTR_ERR(attr->dir);
> +
> +	/* ks_attr_open looks the attr up by name; i_private is unused. */
> +	d = securityfs_create_file("retval", 0600, attr->dir,
> +				   NULL, &ks_retval_fops);
> +	if (IS_ERR(d))
> +		goto err;
> +	d = securityfs_create_file("hits", 0400, attr->dir,
> +				   NULL, &ks_hits_fops);
> +	if (IS_ERR(d))
> +		goto err;
> +	return 0;
> +err:
> +	securityfs_remove(attr->dir);
> +	attr->dir = NULL;
> +	return PTR_ERR(d);
> +}
> +
> +/* ------------------------------------------------------------------ *
> + * Engage / disengage                                                 *
> + * ------------------------------------------------------------------ */
> +
> +static int __ks_engage(const char *symbol, long retval, bool from_cmdline)
> +{
> +	struct ks_attr *attr;
> +	int ret;
> +
> +	if (!symbol || !*symbol)
> +		return -EINVAL;
> +
> +	if (!from_cmdline) {
> +		ret = security_locked_down(LOCKDOWN_KILLSWITCH);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	mutex_lock(&ks_lock);
> +
> +	if (ks_attr_lookup(symbol)) {
> +		ret = -EBUSY;
> +		goto out_unlock;
> +	}
> +
> +	attr = ks_attr_alloc(symbol);
> +	if (!attr) {
> +		ret = -ENOMEM;
> +		goto out_unlock;
> +	}
> +
> +	atomic_long_set(&attr->retval, retval);
> +
> +	ret = register_kprobe(&attr->kp);
> +	if (ret) {
> +		pr_warn("killswitch: register_kprobe(%s) failed: %d\n",
> +			symbol, ret);
> +		ks_attr_put(attr);
> +		goto out_unlock;
> +	}
> +
> +	ret = ks_create_attr_dir(attr);
> +	if (ret) {
> +		unregister_kprobe(&attr->kp);
> +		ks_attr_put(attr);
> +		goto out_unlock;
> +	}
> +
> +	list_add_tail(&attr->list, &ks_engaged_list);
> +	attr->engaged = true;
> +	add_taint(TAINT_KILLSWITCH, LOCKDEP_STILL_OK);
> +
> +	if (from_cmdline) {
> +		pr_warn("killswitch: engage %s=%ld source=cmdline\n",
> +			symbol, retval);
> +	} else {
> +		pr_warn("killswitch: engage %s=%ld uid=%u auid=%u ses=%u comm=%s\n",
> +			symbol, retval,
> +			from_kuid(&init_user_ns, current_uid()),
> +			from_kuid(&init_user_ns, audit_get_loginuid(current)),
> +			audit_get_sessionid(current),
> +			current->comm);
> +	}
> +	ret = 0;
> +
> +out_unlock:
> +	mutex_unlock(&ks_lock);
> +	return ret;
> +}
> +
> +int killswitch_engage(const char *symbol, long retval)
> +{
> +	return __ks_engage(symbol, retval, false);
> +}
> +
> +static int __ks_disengage(const char *symbol)
> +{
> +	struct ks_attr *attr;
> +	unsigned long hits;
> +	int ret = 0;
> +
> +	mutex_lock(&ks_lock);
> +	attr = ks_attr_lookup(symbol);
> +	if (!attr) {
> +		ret = -ENOENT;
> +		goto out_unlock;
> +	}
> +
> +	unregister_kprobe(&attr->kp);
> +	attr->engaged = false;
> +	list_del(&attr->list);
> +	hits = ks_attr_hits(attr);
> +	securityfs_remove(attr->dir);
> +
> +	pr_warn("killswitch: disengage %s hits=%lu uid=%u auid=%u ses=%u comm=%s\n",
> +		symbol, hits,
> +		from_kuid(&init_user_ns, current_uid()),
> +		from_kuid(&init_user_ns, audit_get_loginuid(current)),
> +		audit_get_sessionid(current),
> +		current->comm);
> +
> +	/* unregister_kprobe() already waited out in-flight pre-handlers. */
> +	ks_attr_put(attr);
> +
> +out_unlock:
> +	mutex_unlock(&ks_lock);
> +	return ret;
> +}
> +
> +int killswitch_disengage(const char *symbol)
> +{
> +	return __ks_disengage(symbol);
> +}
> +
> +bool killswitch_is_engaged(const char *symbol)
> +{
> +	bool engaged;
> +
> +	mutex_lock(&ks_lock);
> +	engaged = ks_attr_lookup(symbol) != NULL;
> +	mutex_unlock(&ks_lock);
> +	return engaged;
> +}
> +
> +static void ks_disengage_all_locked(void)
> +{
> +	struct ks_attr *attr, *n;
> +
> +	list_for_each_entry_safe(attr, n, &ks_engaged_list, list) {
> +		unregister_kprobe(&attr->kp);
> +		attr->engaged = false;
> +		list_del(&attr->list);
> +		securityfs_remove(attr->dir);
> +		pr_warn("killswitch: disengage %s hits=%lu (disengage_all)\n",
> +			attr->kp.symbol_name, ks_attr_hits(attr));
> +		ks_attr_put(attr);
> +	}
> +}
> +
> +/* ------------------------------------------------------------------ *
> + * Module unload: drop engagements on functions in the going module   *
> + * ------------------------------------------------------------------ */
> +
> +static int ks_module_notify(struct notifier_block *nb, unsigned long action,
> +			    void *data)
> +{
> +	struct module *mod = data;
> +	struct ks_attr *attr, *n;
> +
> +	if (action != MODULE_STATE_GOING)
> +		return NOTIFY_DONE;
> +
> +	mutex_lock(&ks_lock);
> +	list_for_each_entry_safe(attr, n, &ks_engaged_list, list) {
> +		if (!attr->kp.addr ||
> +		    __module_address((unsigned long)attr->kp.addr) != mod)
> +			continue;
> +
> +		pr_warn("killswitch: %s mitigation lost: module %s unloading; re-engage after reload if still needed\n",
> +			attr->kp.symbol_name, mod->name);
> +		unregister_kprobe(&attr->kp);
> +		attr->engaged = false;
> +		list_del(&attr->list);
> +		securityfs_remove(attr->dir);
> +		ks_attr_put(attr);
> +	}
> +	mutex_unlock(&ks_lock);
> +	return NOTIFY_DONE;
> +}
> +
> +static struct notifier_block ks_module_nb = {
> +	.notifier_call = ks_module_notify,
> +};
> +
> +/* ------------------------------------------------------------------ *
> + * Top-level securityfs files: control / engaged / taint              *
> + * ------------------------------------------------------------------ */
> +
> +static int ks_engaged_show(struct seq_file *m, void *v)
> +{
> +	struct ks_attr *attr;
> +
> +	mutex_lock(&ks_lock);
> +	list_for_each_entry(attr, &ks_engaged_list, list) {
> +		seq_printf(m, "%s retval=%ld hits=%lu\n",
> +			   attr->kp.symbol_name,
> +			   atomic_long_read(&attr->retval),
> +			   ks_attr_hits(attr));
> +	}
> +	mutex_unlock(&ks_lock);
> +	return 0;
> +}
> +
> +static int ks_engaged_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, ks_engaged_show, NULL);
> +}
> +
> +static const struct file_operations ks_engaged_fops = {
> +	.open		= ks_engaged_open,
> +	.read		= seq_read,
> +	.llseek		= seq_lseek,
> +	.release	= single_release,
> +};
> +
> +static ssize_t ks_taint_read(struct file *file, char __user *ubuf,
> +			     size_t count, loff_t *ppos)
> +{
> +	char buf[4];
> +	int len;
> +
> +	len = scnprintf(buf, sizeof(buf), "%d\n",
> +			test_taint(TAINT_KILLSWITCH) ? 1 : 0);
> +	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
> +}
> +
> +static const struct file_operations ks_taint_fops = {
> +	.open	= simple_open,
> +	.read	= ks_taint_read,
> +	.llseek	= default_llseek,
> +};
> +
> +/*
> + * control: parse one of:
> + *   engage <symbol> <retval>
> + *   disengage <symbol>
> + *   disengage_all
> + */
> +static ssize_t ks_control_write(struct file *file, const char __user *ubuf,
> +				size_t count, loff_t *ppos)
> +{
> +	char *buf, *cur, *verb, *sym, *retstr;
> +	long retval = 0;
> +	int ret;
> +
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	if (count == 0 || count > 4096)
> +		return -EINVAL;
> +
> +	buf = memdup_user_nul(ubuf, count);
> +	if (IS_ERR(buf))
> +		return PTR_ERR(buf);
> +
> +	cur = strim(buf);
> +	verb = strsep(&cur, " \t\n");
> +	if (!verb || !*verb) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (!strcmp(verb, "disengage_all")) {
> +		mutex_lock(&ks_lock);
> +		ks_disengage_all_locked();
> +		mutex_unlock(&ks_lock);
> +		ret = count;
> +		goto out;
> +	}
> +
> +	sym = strsep(&cur, " \t\n");
> +	if (!sym || !*sym) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (!strcmp(verb, "disengage")) {
> +		ret = __ks_disengage(sym);
> +		ret = ret ? ret : count;
> +		goto out;
> +	}
> +
> +	if (strcmp(verb, "engage")) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	retstr = strsep(&cur, " \t\n");
> +	if (!retstr || !*retstr) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +	if (kstrtol(retstr, 0, &retval)) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	ret = killswitch_engage(sym, retval);
> +	if (!ret)
> +		ret = count;
> +
> +out:
> +	kfree(buf);
> +	return ret;
> +}
> +
> +static const struct file_operations ks_control_fops = {
> +	.open	= simple_open,
> +	.write	= ks_control_write,
> +	.llseek	= noop_llseek,
> +};
> +
> +/* ------------------------------------------------------------------ *
> + * Boot parameter:                                                    *
> + *   killswitch=fn1=-1,fn2=0,fn3=-22                                  *
> + * ------------------------------------------------------------------ */
> +
> +#define KS_BOOT_BUF 1024
> +static char ks_boot_buf[KS_BOOT_BUF] __initdata;
> +static bool ks_boot_present __initdata;
> +
> +static int __init ks_boot_setup(char *str)
> +{
> +	if (!str)
> +		return 0;
> +	strscpy(ks_boot_buf, str, sizeof(ks_boot_buf));
> +	ks_boot_present = true;
> +	return 1;
> +}
> +__setup("killswitch=", ks_boot_setup);
> +
> +static void __init ks_apply_boot_params(void)
> +{
> +	char *cur, *tok;
> +	long retval;
> +
> +	if (!ks_boot_present)
> +		return;
> +
> +	cur = ks_boot_buf;
> +	while ((tok = strsep(&cur, ",")) != NULL) {
> +		char *eq, *sym, *retstr;
> +
> +		if (!*tok)
> +			continue;
> +		eq = strchr(tok, '=');
> +		if (!eq) {
> +			pr_warn("killswitch: cmdline missing '=': %s\n", tok);
> +			continue;
> +		}
> +		*eq++ = '\0';
> +		sym = tok;
> +		retstr = eq;
> +
> +		if (kstrtol(retstr, 0, &retval)) {
> +			pr_warn("killswitch: cmdline bad retval %s=%s\n",
> +				sym, retstr);
> +			continue;
> +		}
> +
> +		if (__ks_engage(sym, retval, true))
> +			pr_warn("killswitch: cmdline engage %s failed\n", sym);
> +	}
> +}
> +
> +/* ------------------------------------------------------------------ *
> + * Init                                                               *
> + * ------------------------------------------------------------------ */
> +
> +static int __init killswitch_init(void)
> +{
> +	struct dentry *d;
> +
> +	ks_root_dir = securityfs_create_dir("killswitch", NULL);
> +	if (IS_ERR(ks_root_dir))
> +		return PTR_ERR(ks_root_dir);
> +
> +	d = securityfs_create_file("control", 0200, ks_root_dir,
> +				   NULL, &ks_control_fops);
> +	if (IS_ERR(d))
> +		goto err;
> +	d = securityfs_create_file("engaged", 0444, ks_root_dir,
> +				   NULL, &ks_engaged_fops);
> +	if (IS_ERR(d))
> +		goto err;
> +	d = securityfs_create_file("taint", 0444, ks_root_dir,
> +				   NULL, &ks_taint_fops);
> +	if (IS_ERR(d))
> +		goto err;
> +
> +	ks_fn_dir = securityfs_create_dir("fn", ks_root_dir);
> +	if (IS_ERR(ks_fn_dir)) {
> +		d = ks_fn_dir;
> +		goto err;
> +	}
> +
> +	register_module_notifier(&ks_module_nb);
> +	ks_apply_boot_params();
> +
> +	pr_info("killswitch: ready (sysfs at /sys/kernel/security/killswitch/)\n");
> +	return 0;
> +
> +err:
> +	securityfs_remove(ks_root_dir);
> +	return PTR_ERR(d);
> +}
> +late_initcall(killswitch_init);
> +
> +/* ------------------------------------------------------------------ *
> + * KUnit tests                                                        *
> + * ------------------------------------------------------------------ */
> +
> +#if IS_ENABLED(CONFIG_KUNIT)
> +#include <kunit/test.h>
> +
> +/* Non-static so kallsyms resolves them without CONFIG_KALLSYMS_ALL. */
> +int ks_kunit_target_int(int x);
> +void *ks_kunit_target_ptr(int x);
> +
> +#if __has_attribute(__noipa__)
> +# define KS_KUNIT_NOIPA __attribute__((__noipa__))
> +#else
> +# define KS_KUNIT_NOIPA noinline __noclone
> +#endif
> +
> +KS_KUNIT_NOIPA int ks_kunit_target_int(int x)
> +{
> +	return x + 1;
> +}
> +
> +KS_KUNIT_NOIPA void *ks_kunit_target_ptr(int x)
> +{
> +	return ERR_PTR(-EIO);
> +}
> +
> +static int ks_kunit_init(struct kunit *test)
> +{
> +	if (security_locked_down(LOCKDOWN_KILLSWITCH))
> +		kunit_skip(test, "integrity lockdown blocks killswitch_engage()");
> +	return 0;
> +}
> +
> +static int ks_kunit_init_lockdown(struct kunit *test)
> +{
> +	if (!security_locked_down(LOCKDOWN_KILLSWITCH))
> +		kunit_skip(test, "requires lockdown=integrity");
> +	return 0;
> +}
> +
> +static void ks_disengage_quiet(const char *sym)
> +{
> +	if (killswitch_is_engaged(sym))
> +		killswitch_disengage(sym);
> +}
> +
> +static void ks_test_engage_int(struct kunit *test)
> +{
> +	int ret;
> +
> +	ret = killswitch_engage("ks_kunit_target_int", -EPERM);
> +	KUNIT_EXPECT_EQ(test, ret, 0);
> +	KUNIT_EXPECT_EQ(test, ks_kunit_target_int(7), -EPERM);
> +	KUNIT_EXPECT_EQ(test, killswitch_disengage("ks_kunit_target_int"), 0);
> +	KUNIT_EXPECT_EQ(test, ks_kunit_target_int(7), 8);
> +}
> +
> +static void ks_test_double_engage(struct kunit *test)
> +{
> +	KUNIT_ASSERT_EQ(test,
> +		killswitch_engage("ks_kunit_target_int", 0), 0);
> +	KUNIT_EXPECT_EQ(test,
> +		killswitch_engage("ks_kunit_target_int", 0), -EBUSY);
> +	ks_disengage_quiet("ks_kunit_target_int");
> +}
> +
> +static void ks_test_disengage_unknown(struct kunit *test)
> +{
> +	KUNIT_EXPECT_EQ(test,
> +		killswitch_disengage("ks_kunit_target_int"), -ENOENT);
> +}
> +
> +static void ks_test_pointer_target(struct kunit *test)
> +{
> +	long retval = (long)(unsigned long)ERR_PTR(-EACCES);
> +
> +	KUNIT_ASSERT_EQ(test,
> +		killswitch_engage("ks_kunit_target_ptr", retval), 0);
> +	KUNIT_EXPECT_TRUE(test, IS_ERR(ks_kunit_target_ptr(0)));
> +	KUNIT_EXPECT_EQ(test, PTR_ERR(ks_kunit_target_ptr(0)), -EACCES);
> +	ks_disengage_quiet("ks_kunit_target_ptr");
> +}
> +
> +static void ks_test_taint_set(struct kunit *test)
> +{
> +	KUNIT_ASSERT_EQ(test,
> +		killswitch_engage("ks_kunit_target_int", 0), 0);
> +	KUNIT_EXPECT_TRUE(test, test_taint(TAINT_KILLSWITCH));
> +	ks_disengage_quiet("ks_kunit_target_int");
> +	/* taint must persist even after disengage */
> +	KUNIT_EXPECT_TRUE(test, test_taint(TAINT_KILLSWITCH));
> +}
> +
> +static void ks_test_hits_counter(struct kunit *test)
> +{
> +	struct ks_attr *attr;
> +	int i;
> +
> +	KUNIT_ASSERT_EQ(test,
> +		killswitch_engage("ks_kunit_target_int", 0), 0);
> +
> +	for (i = 0; i < 17; i++)
> +		(void)ks_kunit_target_int(i);
> +
> +	mutex_lock(&ks_lock);
> +	attr = ks_attr_lookup("ks_kunit_target_int");
> +	KUNIT_EXPECT_NOT_NULL(test, attr);
> +	if (attr)
> +		KUNIT_EXPECT_EQ(test, ks_attr_hits(attr), 17UL);
> +	mutex_unlock(&ks_lock);
> +
> +	ks_disengage_quiet("ks_kunit_target_int");
> +}
> +
> +static struct kunit_case ks_kunit_cases[] = {
> +	KUNIT_CASE(ks_test_engage_int),
> +	KUNIT_CASE(ks_test_double_engage),
> +	KUNIT_CASE(ks_test_disengage_unknown),
> +	KUNIT_CASE(ks_test_pointer_target),
> +	KUNIT_CASE(ks_test_taint_set),
> +	KUNIT_CASE(ks_test_hits_counter),
> +	{}
> +};
> +
> +static struct kunit_suite ks_kunit_suite = {
> +	.name = "killswitch",
> +	.init = ks_kunit_init,
> +	.test_cases = ks_kunit_cases,
> +};
> +
> +/*
> + * Lockdown suite. Skipped unless the kernel was booted with
> + * lockdown=integrity (or higher). Run together with
> + * killswitch=ks_kunit_target_int=... on the same cmdline to also
> + * exercise the cmdline-bypass and disengage-under-lockdown paths.
> + */
> +static void ks_test_lockdown_runtime_engage(struct kunit *test)
> +{
> +	KUNIT_EXPECT_EQ(test,
> +		killswitch_engage("ks_kunit_target_int", 0), -EPERM);
> +}
> +
> +static void ks_test_lockdown_cmdline_disengage(struct kunit *test)
> +{
> +	if (!killswitch_is_engaged("ks_kunit_target_int"))
> +		kunit_skip(test,
> +			   "requires killswitch=ks_kunit_target_int=... on cmdline");
> +	KUNIT_EXPECT_EQ(test,
> +		killswitch_disengage("ks_kunit_target_int"), 0);
> +}
> +
> +static struct kunit_case ks_kunit_lockdown_cases[] = {
> +	KUNIT_CASE(ks_test_lockdown_runtime_engage),
> +	KUNIT_CASE(ks_test_lockdown_cmdline_disengage),
> +	{}
> +};
> +
> +static struct kunit_suite ks_kunit_lockdown_suite = {
> +	.name = "killswitch_lockdown",
> +	.init = ks_kunit_init_lockdown,
> +	.test_cases = ks_kunit_lockdown_cases,
> +};
> +
> +kunit_test_suites(&ks_kunit_suite, &ks_kunit_lockdown_suite);
> +
> +#endif /* CONFIG_KUNIT */
> +
> diff --git a/kernel/panic.c b/kernel/panic.c
> index 20feada5319d4..8ee174c7b7dd0 100644
> --- a/kernel/panic.c
> +++ b/kernel/panic.c
> @@ -825,6 +825,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = {
>   	TAINT_FLAG(RANDSTRUCT,			'T', ' '),
>   	TAINT_FLAG(TEST,			'N', ' '),
>   	TAINT_FLAG(FWCTL,			'J', ' '),
> +	TAINT_FLAG(KILLSWITCH,			'H', ' '),
>   };
>   
>   #undef TAINT_FLAG
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 8ff5adcfe1e0a..5770639c7b0ea 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -3349,6 +3349,19 @@ config TEST_HMM
>   
>   	  If unsure, say N.
>   
> +config TEST_KILLSWITCH
> +	tristate "Test module for the killswitch mitigation primitive"
> +	depends on KILLSWITCH && DEBUG_FS
> +	depends on m
> +	help
> +	  Build a module that exposes a deliberately-vulnerable function
> +	  ks_test_vuln() and a debugfs trigger /sys/kernel/debug/test_killswitch/fire.
> +	  The killswitch selftest in tools/testing/selftests/killswitch/
> +	  uses this to confirm engaging a killswitch suppresses the BUG()
> +	  the function would otherwise hit.
> +
> +	  If unsure, say N.
> +
>   config TEST_FREE_PAGES
>   	tristate "Test freeing pages"
>   	help
> diff --git a/lib/Makefile b/lib/Makefile
> index f33a24bf1c19a..d763225340674 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -100,6 +100,7 @@ obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o
>   obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o
>   obj-$(CONFIG_TEST_MEMINIT) += test_meminit.o
>   obj-$(CONFIG_TEST_LOCKUP) += test_lockup.o
> +obj-$(CONFIG_TEST_KILLSWITCH) += test_killswitch.o
>   obj-$(CONFIG_TEST_HMM) += test_hmm.o
>   obj-$(CONFIG_TEST_FREE_PAGES) += test_free_pages.o
>   obj-$(CONFIG_TEST_REF_TRACKER) += test_ref_tracker.o
> diff --git a/lib/test_killswitch.c b/lib/test_killswitch.c
> new file mode 100644
> index 0000000000000..cc2584ad652ff
> --- /dev/null
> +++ b/lib/test_killswitch.c
> @@ -0,0 +1,85 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Test target for the killswitch selftest.  ks_test_vuln() returns
> + * -EBADMSG on a magic input, standing in for "the buggy path runs
> + * and produces a bad outcome".  Engaging killswitch on this function
> + * with retval 0 is the mitigation.
> + *
> + * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
> + */
> +
> +#include <linux/debugfs.h>
> +#include <linux/fs.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/uaccess.h>
> +
> +#define KS_TEST_MAGIC	0xC0FFEEL
> +
> +int ks_test_vuln(long magic);
> +
> +/*
> + * Returns -EBADMSG on the magic input -- stands in for "the buggy
> + * path runs and produces a bad outcome".  Engaging a killswitch on
> + * this function with retval 0 represents the mitigation: even on
> + * the magic input, callers see success because the body never runs.
> + *
> + * noipa prevents inlining/IPA so the call actually reaches the
> + * kprobe-instrumented entry point.
> + */
> +noinline int ks_test_vuln(long magic)
> +{
> +	if (magic == KS_TEST_MAGIC)
> +		return -EBADMSG;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(ks_test_vuln);
> +
> +static struct dentry *ks_test_dir;
> +
> +static ssize_t ks_test_fire_write(struct file *file, const char __user *ubuf,
> +				  size_t count, loff_t *ppos)
> +{
> +	char buf[32];
> +	long magic;
> +	int ret;
> +
> +	if (count == 0 || count >= sizeof(buf))
> +		return -EINVAL;
> +	if (copy_from_user(buf, ubuf, count))
> +		return -EFAULT;
> +	buf[count] = '\0';
> +
> +	ret = kstrtol(strim(buf), 0, &magic);
> +	if (ret)
> +		return ret;
> +
> +	ret = ks_test_vuln(magic);
> +	return ret ? ret : count;
> +}
> +
> +static const struct file_operations ks_test_fire_fops = {
> +	.write	= ks_test_fire_write,
> +	.open	= simple_open,
> +	.llseek	= noop_llseek,
> +};
> +
> +static int __init test_killswitch_init(void)
> +{
> +	ks_test_dir = debugfs_create_dir("test_killswitch", NULL);
> +	debugfs_create_file("fire", 0200, ks_test_dir, NULL,
> +			    &ks_test_fire_fops);
> +	pr_info("test_killswitch: loaded (magic=0x%lx)\n", KS_TEST_MAGIC);
> +	return 0;
> +}
> +module_init(test_killswitch_init);
> +
> +static void __exit test_killswitch_exit(void)
> +{
> +	debugfs_remove_recursive(ks_test_dir);
> +}
> +module_exit(test_killswitch_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Deliberately-vulnerable target for killswitch selftest");
> diff --git a/security/security.c b/security/security.c
> index 4e999f0236516..bf700abc911a9 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -62,6 +62,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
>   	[LOCKDOWN_DBG_WRITE_KERNEL] = "use of kgdb/kdb to write kernel RAM",
>   	[LOCKDOWN_RTAS_ERROR_INJECTION] = "RTAS error injection",
>   	[LOCKDOWN_XEN_USER_ACTIONS] = "Xen guest user action",
> +	[LOCKDOWN_KILLSWITCH] = "engaging a killswitch",
>   	[LOCKDOWN_INTEGRITY_MAX] = "integrity",
>   	[LOCKDOWN_KCORE] = "/proc/kcore access",
>   	[LOCKDOWN_KPROBES] = "use of kprobes",
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index 6e59b8f63e416..04c3f8c5ff229 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -53,6 +53,7 @@ TARGETS += ipc
>   TARGETS += ir
>   TARGETS += kcmp
>   TARGETS += kexec
> +TARGETS += killswitch
>   TARGETS += kselftest_harness
>   TARGETS += kvm
>   TARGETS += landlock
> diff --git a/tools/testing/selftests/killswitch/.gitignore b/tools/testing/selftests/killswitch/.gitignore
> new file mode 100644
> index 0000000000000..cbf204ce18615
> --- /dev/null
> +++ b/tools/testing/selftests/killswitch/.gitignore
> @@ -0,0 +1 @@
> +cve_31431_test
> diff --git a/tools/testing/selftests/killswitch/Makefile b/tools/testing/selftests/killswitch/Makefile
> new file mode 100644
> index 0000000000000..ccf41165cb73d
> --- /dev/null
> +++ b/tools/testing/selftests/killswitch/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
> +TEST_GEN_PROGS := cve_31431_test cve_43284_test
> +TEST_PROGS := killswitch_test.sh
> +
> +CFLAGS += -O2 -g -std=gnu99 -Wall $(KHDR_INCLUDES)
> +
> +include ../lib.mk
> diff --git a/tools/testing/selftests/killswitch/cve_31431_test.c b/tools/testing/selftests/killswitch/cve_31431_test.c
> new file mode 100644
> index 0000000000000..1ff817c51d881
> --- /dev/null
> +++ b/tools/testing/selftests/killswitch/cve_31431_test.c
> @@ -0,0 +1,162 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * AF_ALG AEAD round-trip prober.  The killswitch selftest uses this
> + * to demonstrate that engaging a killswitch on af_alg_sendmsg
> + * neuters AF_ALG operations (sendmsg returns -EPERM), mitigating
> + * any AF_ALG-reachable bug whose exploit primitive runs from the
> + * send path.
> + *
> + * Exit codes:
> + *   0  AEAD round-trip succeeded (function intact)
> + *   1  AEAD round-trip refused (mitigation engaged)
> + *   2  setup error (no AF_ALG, missing aead/gcm(aes), etc.) -> SKIP
> + *
> + * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
> + */
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/socket.h>
> +#include <unistd.h>
> +#include <linux/if_alg.h>
> +
> +#define KEY_LEN		16
> +#define IV_LEN		12
> +#define AAD_LEN		16
> +#define PT_LEN		64
> +#define TAG_LEN		16
> +#define EXPECTED_LEN	(AAD_LEN + PT_LEN + TAG_LEN)
> +
> +#ifndef AF_ALG
> +#define AF_ALG		38
> +#endif
> +#ifndef SOL_ALG
> +#define SOL_ALG		279
> +#endif
> +
> +int main(void)
> +{
> +	struct sockaddr_alg sa = {
> +		.salg_family = AF_ALG,
> +		.salg_type   = "aead",
> +		.salg_name   = "gcm(aes)",
> +	};
> +	unsigned char key[KEY_LEN] = { 0 };
> +	unsigned char iv[IV_LEN]   = { 0 };
> +	unsigned char buf[1024]    = { 0 };
> +	struct msghdr msg = { 0 };
> +	struct iovec iov;
> +	struct cmsghdr *cmsg;
> +	struct af_alg_iv *aiv;
> +	char cbuf[256] = { 0 };
> +	int *p_op, *p_assoclen;
> +	int sk, opfd;
> +	ssize_t n;
> +
> +	sk = socket(AF_ALG, SOCK_SEQPACKET, 0);
> +	if (sk < 0) {
> +		fprintf(stderr, "AF_ALG socket: %s -- skip\n", strerror(errno));
> +		return 2;
> +	}
> +	if (bind(sk, (struct sockaddr *)&sa, sizeof(sa))) {
> +		fprintf(stderr, "bind aead/gcm(aes): %s -- skip\n",
> +			strerror(errno));
> +		close(sk);
> +		return 2;
> +	}
> +	if (setsockopt(sk, SOL_ALG, ALG_SET_KEY, key, KEY_LEN)) {
> +		fprintf(stderr, "ALG_SET_KEY: %s -- skip\n", strerror(errno));
> +		close(sk);
> +		return 2;
> +	}
> +	if (setsockopt(sk, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, TAG_LEN)) {
> +		fprintf(stderr, "ALG_SET_AEAD_AUTHSIZE: %s -- skip\n",
> +			strerror(errno));
> +		close(sk);
> +		return 2;
> +	}
> +
> +	opfd = accept(sk, NULL, 0);
> +	if (opfd < 0) {
> +		fprintf(stderr, "accept: %s -- skip\n", strerror(errno));
> +		close(sk);
> +		return 2;
> +	}
> +
> +	/* control message: ENCRYPT op + IV + assoclen */
> +	msg.msg_control    = cbuf;
> +	msg.msg_controllen = CMSG_SPACE(sizeof(int))
> +			   + CMSG_SPACE(sizeof(*aiv) + IV_LEN)
> +			   + CMSG_SPACE(sizeof(int));
> +
> +	cmsg = CMSG_FIRSTHDR(&msg);
> +	cmsg->cmsg_level = SOL_ALG;
> +	cmsg->cmsg_type  = ALG_SET_OP;
> +	cmsg->cmsg_len   = CMSG_LEN(sizeof(int));
> +	p_op = (int *)CMSG_DATA(cmsg);
> +	*p_op = ALG_OP_ENCRYPT;
> +
> +	cmsg = CMSG_NXTHDR(&msg, cmsg);
> +	cmsg->cmsg_level = SOL_ALG;
> +	cmsg->cmsg_type  = ALG_SET_IV;
> +	cmsg->cmsg_len   = CMSG_LEN(sizeof(*aiv) + IV_LEN);
> +	aiv = (struct af_alg_iv *)CMSG_DATA(cmsg);
> +	aiv->ivlen = IV_LEN;
> +	memcpy(aiv->iv, iv, IV_LEN);
> +
> +	cmsg = CMSG_NXTHDR(&msg, cmsg);
> +	cmsg->cmsg_level = SOL_ALG;
> +	cmsg->cmsg_type  = ALG_SET_AEAD_ASSOCLEN;
> +	cmsg->cmsg_len   = CMSG_LEN(sizeof(int));
> +	p_assoclen = (int *)CMSG_DATA(cmsg);
> +	*p_assoclen = AAD_LEN;
> +
> +	/* AAD || plaintext */
> +	memset(buf, 0xaa, AAD_LEN);
> +	memset(buf + AAD_LEN, 0x55, PT_LEN);
> +	iov.iov_base = buf;
> +	iov.iov_len  = AAD_LEN + PT_LEN;
> +	msg.msg_iov    = &iov;
> +	msg.msg_iovlen = 1;
> +
> +	n = sendmsg(opfd, &msg, 0);
> +	if (n < 0) {
> +		/*
> +		 * sendmsg refused: this is exactly the killswitch
> +		 * af_alg_sendmsg=-EPERM mitigation outcome.  Distinct
> +		 * exit code from setup failure so the test script can
> +		 * tell them apart.
> +		 */
> +		fprintf(stderr, "sendmsg: %s -- mitigation engaged?\n",
> +			strerror(errno));
> +		close(opfd); close(sk);
> +		return 1;
> +	}
> +
> +	/* recv: AAD echoed, plus ciphertext + tag */
> +	memset(buf, 0, sizeof(buf));
> +	n = read(opfd, buf, EXPECTED_LEN);
> +	close(opfd); close(sk);
> +
> +	if (n == 0) {
> +		printf("AEAD returned 0 bytes -- killswitch mitigation engaged\n");
> +		return 1;
> +	}
> +	if (n != EXPECTED_LEN) {
> +		fprintf(stderr,
> +			"AEAD short read: got %zd, expected %d -- mitigated?\n",
> +			n, EXPECTED_LEN);
> +		return 1;
> +	}
> +
> +	/* sanity: ciphertext (after AAD) shouldn't equal the plaintext bytes */
> +	if (memcmp(buf + AAD_LEN, buf + AAD_LEN + 1, PT_LEN - 1) == 0) {
> +		fprintf(stderr, "AEAD output looks unencrypted\n");
> +		return 2;
> +	}
> +
> +	printf("AEAD round-trip OK (%zd bytes)\n", n);
> +	return 0;
> +}
> diff --git a/tools/testing/selftests/killswitch/cve_43284_test.c b/tools/testing/selftests/killswitch/cve_43284_test.c
> new file mode 100644
> index 0000000000000..4771cb0957dc1
> --- /dev/null
> +++ b/tools/testing/selftests/killswitch/cve_43284_test.c
> @@ -0,0 +1,88 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * UDP loopback round-trip prober.  Wrapped by killswitch_test.sh with
> + * an IPsec ESP SA + policy pair on loopback, this demonstrates that
> + * engaging a killswitch on esp_input drops inbound ESP packets before
> + * decapsulation, mitigating CVE-2026-43284 ("Dirty Frag", upstream fix
> + * xfrm: esp: avoid in-place decrypt on shared skb frags).
> + *
> + * The binary itself knows nothing about ESP -- it sends one UDP
> + * datagram to itself and waits up to a second for delivery.
> + *
> + * Exit codes:
> + *   0  UDP round-trip succeeded (no mitigation in effect)
> + *   1  UDP recv timed out (mitigation engaged)
> + *   2  setup error -> SKIP
> + *
> + * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
> + */
> +
> +#include <arpa/inet.h>
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/socket.h>
> +#include <sys/time.h>
> +#include <unistd.h>
> +
> +#define UDP_PORT 53435
> +#define PROBE    "ks-43284-probe"
> +
> +int main(void)
> +{
> +	struct sockaddr_in addr = {
> +		.sin_family      = AF_INET,
> +		.sin_port        = htons(UDP_PORT),
> +		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
> +	};
> +	struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
> +	char buf[64];
> +	int sk;
> +	ssize_t n;
> +
> +	sk = socket(AF_INET, SOCK_DGRAM, 0);
> +	if (sk < 0) {
> +		fprintf(stderr, "socket: %s -- skip\n", strerror(errno));
> +		return 2;
> +	}
> +	if (bind(sk, (struct sockaddr *)&addr, sizeof(addr))) {
> +		fprintf(stderr, "bind: %s -- skip\n", strerror(errno));
> +		close(sk);
> +		return 2;
> +	}
> +	if (setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
> +		fprintf(stderr, "SO_RCVTIMEO: %s -- skip\n", strerror(errno));
> +		close(sk);
> +		return 2;
> +	}
> +
> +	if (sendto(sk, PROBE, sizeof(PROBE) - 1, 0,
> +		   (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> +		fprintf(stderr, "sendto: %s -- skip\n", strerror(errno));
> +		close(sk);
> +		return 2;
> +	}
> +
> +	memset(buf, 0, sizeof(buf));
> +	n = recvfrom(sk, buf, sizeof(buf), 0, NULL, NULL);
> +	close(sk);
> +
> +	if (n < 0) {
> +		if (errno == EAGAIN || errno == EWOULDBLOCK) {
> +			fprintf(stderr,
> +				"recvfrom: timeout -- mitigation engaged?\n");
> +			return 1;
> +		}
> +		fprintf(stderr, "recvfrom: %s\n", strerror(errno));
> +		return 2;
> +	}
> +	if (n != (ssize_t)(sizeof(PROBE) - 1) ||
> +	    memcmp(buf, PROBE, sizeof(PROBE) - 1)) {
> +		fprintf(stderr, "recvfrom: bad payload (%zd bytes)\n", n);
> +		return 2;
> +	}
> +
> +	printf("UDP round-trip OK (%zd bytes)\n", n);
> +	return 0;
> +}
> diff --git a/tools/testing/selftests/killswitch/killswitch_test.sh b/tools/testing/selftests/killswitch/killswitch_test.sh
> new file mode 100755
> index 0000000000000..ea3fd394a984f
> --- /dev/null
> +++ b/tools/testing/selftests/killswitch/killswitch_test.sh
> @@ -0,0 +1,254 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# End-to-end killswitch selftest.  Drives the test_killswitch module
> +# through an engage/disengage cycle and confirms each transition
> +# behaves as expected.  Also runs the AF_ALG mitigation proof.
> +#
> +# Requirements (see Documentation/admin-guide/killswitch.rst):
> +#   - CONFIG_KILLSWITCH=y
> +#   - CONFIG_TEST_KILLSWITCH=m
> +#   - run as root (CAP_SYS_ADMIN)
> +#
> +# Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
> +#
> +
> +set -u
> +
> +KS=/sys/kernel/security/killswitch
> +TRIG=/sys/kernel/debug/test_killswitch/fire
> +
> +NOMOD=0
> +SKIP_RC=4
> +N=0
> +FAIL=0
> +
> +ksft_pass() { N=$((N+1));    echo "ok $N - $*"; }
> +ksft_fail() { N=$((N+1)); FAIL=$((FAIL+1)); echo "not ok $N - $*"; }
> +ksft_skip() { echo "ok 1 - SKIP $*"; echo "1..1"; exit $SKIP_RC; }
> +
> +[[ $EUID -eq 0 ]] || ksft_skip "must be root"
> +[[ -d $KS    ]] || ksft_skip "$KS not present (CONFIG_KILLSWITCH disabled?)"
> +
> +if ! modprobe test_killswitch 2>/dev/null; then
> +	NOMOD=1
> +fi
> +[[ -e $TRIG ]] || ksft_skip "$TRIG missing (test_killswitch.ko not installed?)"
> +
> +cleanup() {
> +	echo "disengage_all" > $KS/control 2>/dev/null || true
> +	[[ $NOMOD -eq 0 ]] && rmmod test_killswitch 2>/dev/null || true
> +}
> +trap cleanup EXIT
> +
> +# --- pre-engage: bad path runs, write fails with EBADMSG ---
> +if echo 0xC0FFEE > $TRIG 2>/dev/null; then
> +	ksft_fail "pre-engage: write should have failed (-EBADMSG)"
> +else
> +	[[ $? -ne 0 ]] && ksft_pass "pre-engage: bad path returns error" \
> +	             || ksft_fail "pre-engage: unexpected outcome"
> +fi
> +
> +# --- engage ---
> +echo "engage ks_test_vuln 0" > $KS/control
> +grep -q "^ks_test_vuln" $KS/engaged \
> +	&& ksft_pass "engage: ks_test_vuln in engaged list" \
> +	|| ksft_fail "engage: missing from engaged list"
> +
> +[[ $(cat $KS/taint) == 1 ]] \
> +	&& ksft_pass "engage: taint set" \
> +	|| ksft_fail "engage: taint not set"
> +
> +[[ -d $KS/fn/ks_test_vuln ]] \
> +	&& ksft_pass "engage: per-fn dir created" \
> +	|| ksft_fail "engage: per-fn dir missing"
> +
> +# --- post-engage: BUG suppressed; write returns successfully ---
> +if echo 0xC0FFEE > $TRIG 2>/dev/null; then
> +	ksft_pass "post-engage: BUG suppressed, write succeeded"
> +else
> +	ksft_fail "post-engage: write should succeed"
> +fi
> +
> +[[ $(cat $KS/fn/ks_test_vuln/hits) -ge 1 ]] \
> +	&& ksft_pass "post-engage: hits counter incremented" \
> +	|| ksft_fail "post-engage: hits counter did not move"
> +
> +# --- retval rewrite is a plain write (no validation) ---
> +echo 7 > $KS/fn/ks_test_vuln/retval
> +[[ $(cat $KS/fn/ks_test_vuln/retval) == 7 ]] \
> +	&& ksft_pass "retval rewrite round-trips" \
> +	|| ksft_fail "retval rewrite failed"
> +
> +# --- engage on a kprobe-rejected function fails ---
> +# warn_thunk_thunk is in /sys/kernel/debug/kprobes/blacklist;
> +# register_kprobe() refuses it.
> +KP_REJECT=warn_thunk_thunk
> +if echo "engage $KP_REJECT 0" > $KS/control 2>/dev/null; then
> +	ksft_fail "register_kprobe should have rejected $KP_REJECT"
> +	echo "disengage $KP_REJECT" > $KS/control
> +else
> +	ksft_pass "register_kprobe refuses blacklisted target"
> +fi
> +
> +# --- disengage ---
> +echo "disengage ks_test_vuln" > $KS/control
> +[[ -z "$(cat $KS/engaged)" ]] \
> +	&& ksft_pass "disengage: engaged list empty" \
> +	|| ksft_fail "disengage: engaged list not empty"
> +
> +[[ ! -d $KS/fn/ks_test_vuln ]] \
> +	&& ksft_pass "disengage: per-fn dir removed" \
> +	|| ksft_fail "disengage: per-fn dir still present"
> +
> +[[ $(cat $KS/taint) == 1 ]] \
> +	&& ksft_pass "disengage: taint persists" \
> +	|| ksft_fail "disengage: taint should persist"
> +
> +# --- post-disengage: bad path active again ---
> +if echo 0xC0FFEE > $TRIG 2>/dev/null; then
> +	ksft_fail "post-disengage: write should fail again"
> +else
> +	ksft_pass "post-disengage: bad path active again"
> +fi
> +
> +# ---- CVE-2026-31431 mitigation proof (AF_ALG aead via af_alg_sendmsg) ----
> +# Skip the whole block if AF_ALG / AEAD machinery isn't compiled in.
> +if [[ -x $(dirname "$0")/cve_31431_test ]]; then
> +	CVE=$(dirname "$0")/cve_31431_test
> +	$CVE >/dev/null 2>&1 && PRE=$? || PRE=$?
> +	if [[ $PRE -eq 0 ]]; then
> +		ksft_pass "cve-31431: pre-engage AEAD round-trip OK"
> +
> +		echo "engage af_alg_sendmsg -1" > $KS/control
> +		$CVE >/dev/null 2>&1 && POST=$? || POST=$?
> +		if [[ $POST -eq 1 ]]; then
> +			ksft_pass "cve-31431: post-engage AEAD refused (mitigated)"
> +		else
> +			ksft_fail "cve-31431: post-engage exit=$POST (expected 1)"
> +		fi
> +
> +		HITS=$(cat $KS/fn/af_alg_sendmsg/hits 2>/dev/null || echo 0)
> +		[[ $HITS -ge 1 ]] && ksft_pass "cve-31431: hits=$HITS recorded" \
> +			|| ksft_fail "cve-31431: hits not recorded"
> +
> +		echo "disengage af_alg_sendmsg" > $KS/control
> +		$CVE >/dev/null 2>&1 && POST2=$? || POST2=$?
> +		[[ $POST2 -eq 0 ]] && ksft_pass "cve-31431: post-disengage restored" \
> +			|| ksft_fail "cve-31431: post-disengage exit=$POST2"
> +	elif [[ $PRE -eq 2 ]]; then
> +		echo "# SKIP cve-31431 (AF_ALG/AEAD not available)"
> +	else
> +		ksft_fail "cve-31431: pre-engage exit=$PRE"
> +	fi
> +fi
> +
> +# ---- CVE-2026-43284 mitigation proof (IPsec ESP via esp_input) ----
> +# Engaging esp_input causes inbound ESP packets to be dropped before
> +# decapsulation, neutering any bug downstream of the ESP receive path.
> +# Two netns + veth so traffic actually traverses xfrm (single-netns
> +# 127.0.0.0/8 traffic short-circuits before xfrm policy lookup).
> +NS0=ks-esp-0
> +NS1=ks-esp-1
> +esp_setup_ok=0
> +esp_cleanup() {
> +	[[ $esp_setup_ok -eq 1 ]] || return 0
> +	ip netns del $NS0 2>/dev/null
> +	ip netns del $NS1 2>/dev/null
> +}
> +trap 'cleanup; esp_cleanup' EXIT
> +
> +# UDP probe in python3 (always present on Debian/Fedora minimal installs).
> +esp_round_trip() {
> +	# $1: source netns, $2: dest netns, $3: dest ip, $4: port
> +	local tmp rpid rc
> +	tmp=$(mktemp)
> +	ip netns exec "$2" python3 -c '
> +import socket
> +r = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
> +r.bind(("0.0.0.0", '"$4"'))
> +r.settimeout(2.0)
> +try:
> +    d,_ = r.recvfrom(64)
> +    print(d.decode(errors="replace"))
> +except socket.timeout:
> +    print("timeout")
> +' > "$tmp" 2>&1 &
> +	rpid=$!
> +	sleep 0.3
> +	ip netns exec "$1" python3 -c '
> +import socket
> +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
> +s.sendto(b"ks-esp-probe", ("'"$3"'", '"$4"'))
> +' 2>/dev/null
> +	wait $rpid 2>/dev/null
> +	rc=1
> +	grep -q "ks-esp-probe" "$tmp" && rc=0
> +	rm -f "$tmp"
> +	return $rc
> +}
> +
> +if command -v ip >/dev/null 2>&1 && command -v python3 >/dev/null 2>&1; then
> +	KEY=0x0123456789abcdef0123456789abcdef01234567
> +
> +	if ip netns add $NS0 2>/dev/null && \
> +	   ip netns add $NS1 2>/dev/null && \
> +	   ip link add veth0 type veth peer name veth1 2>/dev/null && \
> +	   ip link set veth0 netns $NS0 2>/dev/null && \
> +	   ip link set veth1 netns $NS1 2>/dev/null && \
> +	   ip -n $NS0 addr add 10.99.0.1/24 dev veth0 2>/dev/null && \
> +	   ip -n $NS1 addr add 10.99.0.2/24 dev veth1 2>/dev/null && \
> +	   ip -n $NS0 link set veth0 up 2>/dev/null && \
> +	   ip -n $NS1 link set veth1 up 2>/dev/null && \
> +	   ip -n $NS0 link set lo up 2>/dev/null && \
> +	   ip -n $NS1 link set lo up 2>/dev/null && \
> +	   ip -n $NS0 xfrm state add src 10.99.0.1 dst 10.99.0.2 proto esp \
> +		spi 0x1000 mode transport reqid 0x100 \
> +		aead 'rfc4106(gcm(aes))' $KEY 128 2>/dev/null && \
> +	   ip -n $NS0 xfrm state add src 10.99.0.2 dst 10.99.0.1 proto esp \
> +		spi 0x1001 mode transport reqid 0x100 \
> +		aead 'rfc4106(gcm(aes))' $KEY 128 2>/dev/null && \
> +	   ip -n $NS1 xfrm state add src 10.99.0.1 dst 10.99.0.2 proto esp \
> +		spi 0x1000 mode transport reqid 0x100 \
> +		aead 'rfc4106(gcm(aes))' $KEY 128 2>/dev/null && \
> +	   ip -n $NS1 xfrm state add src 10.99.0.2 dst 10.99.0.1 proto esp \
> +		spi 0x1001 mode transport reqid 0x100 \
> +		aead 'rfc4106(gcm(aes))' $KEY 128 2>/dev/null && \
> +	   ip -n $NS0 xfrm policy add src 10.99.0.1 dst 10.99.0.2 \
> +		dir out tmpl src 10.99.0.1 dst 10.99.0.2 proto esp \
> +		reqid 0x100 mode transport 2>/dev/null && \
> +	   ip -n $NS1 xfrm policy add src 10.99.0.1 dst 10.99.0.2 \
> +		dir in tmpl src 10.99.0.1 dst 10.99.0.2 proto esp \
> +		reqid 0x100 mode transport 2>/dev/null; then
> +		esp_setup_ok=1
> +	fi
> +
> +	if [[ $esp_setup_ok -eq 1 ]] \
> +	   && esp_round_trip $NS0 $NS1 10.99.0.2 53435; then
> +		ksft_pass "cve-43284: pre-engage ESP round-trip OK"
> +
> +		echo "engage esp_input -22" > $KS/control
> +		if esp_round_trip $NS0 $NS1 10.99.0.2 53435; then
> +			ksft_fail "cve-43284: post-engage ESP should have been dropped"
> +		else
> +			ksft_pass "cve-43284: post-engage ESP refused (mitigated)"
> +		fi
> +
> +		ESP_HITS=$(cat $KS/fn/esp_input/hits 2>/dev/null || echo 0)
> +		[[ $ESP_HITS -ge 1 ]] \
> +			&& ksft_pass "cve-43284: hits=$ESP_HITS recorded" \
> +			|| ksft_fail "cve-43284: hits not recorded"
> +
> +		echo "disengage esp_input" > $KS/control
> +		if esp_round_trip $NS0 $NS1 10.99.0.2 53435; then
> +			ksft_pass "cve-43284: post-disengage restored"
> +		else
> +			ksft_fail "cve-43284: post-disengage ESP still dropped"
> +		fi
> +	else
> +		echo "# SKIP cve-43284 (netns/veth/XFRM/ESP setup failed)"
> +	fi
> +fi
> +
> +echo "1..$N"
> +exit $((FAIL > 0))

^ permalink raw reply

* Re: [PATCH v11 3/6] iio: adc: ad4691: add triggered buffer support
From: David Lechner @ 2026-05-17 19:21 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, Nuno Sá,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
	Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
	linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
	linux-doc
In-Reply-To: <20260517132526.27c71b70@jic23-huawei>

On 5/17/26 7:25 AM, Jonathan Cameron wrote:
> On Sat, 16 May 2026 12:32:51 -0500
> David Lechner <dlechner@baylibre.com> wrote:
> 
>> On 5/15/26 8:31 AM, Radu Sabau via B4 Relay wrote:
>>> From: Radu Sabau <radu.sabau@analog.com>
>>>
>>> Add buffered capture support using the IIO triggered buffer framework.
>>>
>>> CNV Burst Mode: the GP pin identified by interrupt-names in the device
>>> tree is configured as DATA_READY output. The IRQ handler stops
>>> conversions and fires the IIO trigger; the trigger handler executes a
>>> pre-built SPI message that reads all active channels from the AVG_IN
>>> accumulator registers and then resets accumulator state and restarts
>>> conversions for the next cycle.
>>>
>>> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
>>> reads the previous result and starts the next conversion (pipelined
>>> N+1 scheme). At preenable time a pre-built, optimised SPI message of
>>> N+1 transfers is constructed (N channel reads plus one NOOP to drain
>>> the pipeline). The trigger handler executes the message in a single
>>> spi_sync() call and collects the results. An external trigger (e.g.
>>> iio-trig-hrtimer) is required to drive the trigger at the desired
>>> sample rate.
>>>
>>> Both modes share the same trigger handler and push a complete scan —
>>> one big-endian 16-bit (__be16) slot per active channel, densely packed
>>> in scan_index order, followed by a timestamp.
>>>
>>> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
>>> buffer-level attribute via IIO_DEVICE_ATTR.
>>>
>>> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> 
>>> +
>>> +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct ad4691_state *st = iio_priv(indio_dev);
>>> +	unsigned int k, i;
>>> +	int ret;
>>> +
>>> +	memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
>>> +	memset(st->scan_tx, 0, sizeof(st->scan_tx));
>>> +
>>> +	spi_message_init(&st->scan_msg);
>>> +
>>> +	k = 0;
>>> +	iio_for_each_active_channel(indio_dev, i) {
>>> +		if (i >= indio_dev->num_channels - 1)
>>> +			break; /* skip soft timestamp */  
>>
>> I don't think timestamp gets set in the scan mask. It is handled separately.
> 
> FWIW that is a sashiko false postive (I believe anyway!)
> If we do hit this please shout as we have a core bug.
> 
> If anyone has time to look at how hard it would be to tweak
> iio_for_each_active_channel to skip a last element timestamp that
> would be great.
> 
> I think that iterates one too far which is what sashiko is tripping over.
> 
> I'm only keen to fix that if we can make it low cost and hid it entirely
> from drivers.
> 
> Jonathan
> 
This is what I came up with (totally untested).

Since timestamp can never be set in scan_mask/active_scan_mask, it should
be safe to exclude it from masklength without breaking existing code.

I didn't check all callers of masklength/iio_get_masklength() though.

---
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 9d66510a1d49..17f539fc23e2 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -2300,8 +2300,10 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
 	if (channels) {
 		int ml = 0;
 
-		for (i = 0; i < indio_dev->num_channels; i++)
-			ml = max(ml, channels[i].scan_index + 1);
+		for (i = 0; i < indio_dev->num_channels; i++) {
+			if (channels[i].type != IIO_TIMESTAMP)
+				ml = max(ml, channels[i].scan_index + 1);
+		}
 		ACCESS_PRIVATE(indio_dev, masklength) = ml;
 	}
 



^ permalink raw reply related

* Re: [PATCH v4 05/16] vfio: Enforce preserved devices are retrieved via LIVEUPDATE_SESSION_RETRIEVE_FD
From: Zhu Yanjun @ 2026-05-17 19:04 UTC (permalink / raw)
  To: Vipin Sharma, kvm, linux-doc, linux-kernel, linux-kselftest,
	linux-pci, yanjun.zhu@linux.dev
  Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
	dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
	leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
	praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
	skhan, skhawaja, vivek.kasireddy, witu, yi.l.liu
In-Reply-To: <20260511234802.2280368-6-vipinsh@google.com>


在 2026/5/11 16:47, Vipin Sharma 写道:
> From: David Matlack <dmatlack@google.com>
>
> Enforce that files for incoming (preserved by previous kernel) VFIO
> devices are retrieved via LIVEUPDATE_SESSION_RETRIEVE_FD rather than by
> opening the corresponding VFIO character device or via
> VFIO_GROUP_GET_DEVICE_FD.
>
> Both of these methods would result in VFIO initializing the device
> without access to the preserved state of the device passed by the
> previous kernel.
>
> Reviewed-by: Pranjal Shrivastava <praan@google.com>
> Signed-off-by: David Matlack <dmatlack@google.com>
> Co-developed-by: Vipin Sharma <vipinsh@google.com>
> Signed-off-by: Vipin Sharma <vipinsh@google.com>
> ---
>   drivers/vfio/device_cdev.c             |  8 ++++++++
>   drivers/vfio/group.c                   |  9 +++++++++
>   drivers/vfio/pci/vfio_pci_liveupdate.c |  6 ++++++
>   drivers/vfio/vfio.h                    | 18 ++++++++++++++++++
>   4 files changed, 41 insertions(+)
>
> diff --git a/drivers/vfio/device_cdev.c b/drivers/vfio/device_cdev.c
> index 1ab07ccaf3ab..4df0495941c6 100644
> --- a/drivers/vfio/device_cdev.c
> +++ b/drivers/vfio/device_cdev.c
> @@ -49,6 +49,14 @@ static int vfio_device_cdev_open(struct vfio_device *device, struct file **filep
>   		}
>   
>   		*filep = file;
> +	} else if (vfio_liveupdate_incoming_is_preserved(device)) {
> +		/*
> +		 * Since it is live update preserved device, it must be
> +		 * retrieved via LIVEUPDATE_SESSION_RETRIEVE_FD instead of
> +		 * opening /dev/vfio/devices/vfioX.
> +		 */
> +		ret = -EBUSY;
> +		goto err_free_device_file;

When vfio_liveupdate_incoming_is_preserved(device) returns true, 
vfio_device_put_registration(device) is not called in this path.

Is vfio_device_put_registration(device) instead invoked from the 
err_free_device_file error handling path?

Zhu Yanjun

>   	}
>   
>   	file->private_data = df;
> diff --git a/drivers/vfio/group.c b/drivers/vfio/group.c
> index b2299e5bc6df..62b4eaabc829 100644
> --- a/drivers/vfio/group.c
> +++ b/drivers/vfio/group.c
> @@ -316,6 +316,15 @@ static int vfio_group_ioctl_get_device_fd(struct vfio_group *group,
>   	if (IS_ERR(device))
>   		return PTR_ERR(device);
>   
> +	/*
> +	 * This device was preserved across a Live Update. Accessing it via
> +	 * VFIO_GROUP_GET_DEVICE_FD is not allowed.
> +	 */
> +	if (vfio_liveupdate_incoming_is_preserved(device)) {
> +		vfio_device_put_registration(device);
> +		return -EBUSY;
> +	}
> +
>   	fd = FD_ADD(O_CLOEXEC, vfio_device_open_file(device));
>   	if (fd < 0)
>   		vfio_device_put_registration(device);
> diff --git a/drivers/vfio/pci/vfio_pci_liveupdate.c b/drivers/vfio/pci/vfio_pci_liveupdate.c
> index 11c3bc8a8dcd..731a3e34085f 100644
> --- a/drivers/vfio/pci/vfio_pci_liveupdate.c
> +++ b/drivers/vfio/pci/vfio_pci_liveupdate.c
> @@ -47,6 +47,12 @@
>    *   ...
>    *   ioctl(session_fd, LIVEUPDATE_SESSION_FINISH, ...);
>    *
> + * .. note::
> + *    After kexec, if a device was preserved by the previous kernel, attempting
> + *    to open a new file for the device via its character device
> + *    (``/dev/vfio/devices/X``) or via ``VFIO_GROUP_GET_DEVICE_FD`` will fail
> + *    with ``-EBUSY``.
> + *
>    * Restrictions
>    * ============
>    *
> diff --git a/drivers/vfio/vfio.h b/drivers/vfio/vfio.h
> index 0854f3fa1a22..5269fe021ee3 100644
> --- a/drivers/vfio/vfio.h
> +++ b/drivers/vfio/vfio.h
> @@ -11,6 +11,7 @@
>   #include <linux/cdev.h>
>   #include <linux/module.h>
>   #include <linux/vfio.h>
> +#include <linux/pci.h>
>   
>   struct iommufd_ctx;
>   struct iommu_group;
> @@ -461,4 +462,21 @@ static inline void vfio_device_debugfs_init(struct vfio_device *vdev) { }
>   static inline void vfio_device_debugfs_exit(struct vfio_device *vdev) { }
>   #endif /* CONFIG_VFIO_DEBUGFS */
>   
> +#ifdef CONFIG_PCI_LIVEUPDATE
> +static inline bool vfio_liveupdate_incoming_is_preserved(struct vfio_device *device)
> +{
> +	struct device *d = device->dev;
> +
> +	if (dev_is_pci(d))
> +		return to_pci_dev(d)->liveupdate_incoming;
> +
> +	return false;
> +}
> +#else
> +static inline bool vfio_liveupdate_incoming_is_preserved(struct vfio_device *device)
> +{
> +	return false;
> +}
> +#endif /* CONFIG_PCI_LIVEUPDATE */
> +
>   #endif

-- 
Best Regards,
Yanjun.Zhu


^ permalink raw reply

* Re: [PATCH v2 05/10] liveupdate: defer session block allocation and PA setting
From: Pasha Tatashin @ 2026-05-17 18:52 UTC (permalink / raw)
  To: Mike Rapoport
  Cc: Pasha Tatashin, linux-kselftest, shuah, akpm, linux-mm, skhan,
	linux-doc, linux-kernel, corbet, dmatlack, kexec, pratyush,
	skhawaja, graf
In-Reply-To: <agn7XnuO7VU4eK52@kernel.org>

On 05-17 20:31, Mike Rapoport wrote:
> On Thu, May 14, 2026 at 10:26:23PM +0000, Pasha Tatashin wrote:
> > Currently, luo_session_setup_outgoing() allocates the session block and
> > sets its physical address in the header immediately. With upcoming
> > dynamic block-based session management, this makes the first block
> > different from the rest. Move the allocation to where it is first needed.
> > 
> > Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> 
> Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> 
> > ---
> > @@ -77,15 +77,16 @@
> >  
> >  /**
> >   * struct luo_session_header - Header struct for managing LUO sessions.
> > - * @count:      The number of sessions currently tracked in the @list.
> > - * @list:       The head of the linked list of `struct luo_session` instances.
> > - * @rwsem:      A read-write semaphore providing synchronized access to the
> > - *              session list and other fields in this structure.
> > - * @header_ser: The header data of serialization array.
> > - * @ser:        The serialized session data (an array of
> > - *              `struct luo_session_ser`).
> > - * @active:     Set to true when first initialized. If previous kernel did not
> > - *              send session data, active stays false for incoming.
> > + * @count:       The number of sessions currently tracked in the @list.
> > + * @list:        The head of the linked list of `struct luo_session` instances.
> > + * @rwsem:       A read-write semaphore providing synchronized access to the
> > + *               session list and other fields in this structure.
> > + * @header_ser:  The header data of serialization array.
> > + * @ser:         The serialized session data (an array of
> > + *               `struct luo_session_ser`).
> > + * @sessions_pa: Points to the location of sessions_pa within struct luo_ser.
> > + * @active:      Set to true when first initialized. If previous kernel did not
> > + *               send session data, active stays false for incoming.
> 
> Hmm, why addition of a single field changed the entire block? :/

Yes, I had to decide whether to shorten the field name or change the 
entire block. I opted for the latter. :-)

> 
> >   */
> >  struct luo_session_header {
> >  	long count;
> 
> -- 
> Sincerely yours,
> Mike.

^ permalink raw reply

* Re: [RFC PATCH 3/5] mm/damon/core: floor effective quota size at minimum region size
From: SeongJae Park @ 2026-05-17 18:47 UTC (permalink / raw)
  To: Ravi Jonnalagadda
  Cc: SeongJae Park, damon, linux-mm, linux-kernel, linux-doc, akpm,
	corbet, bijan311, ajayjoshi, honggyu.kim, yunjeong.mun
In-Reply-To: <20260516210357.2247-4-ravis.opensrc@gmail.com>

On Sat, 16 May 2026 14:03:55 -0700 Ravi Jonnalagadda <ravis.opensrc@gmail.com> wrote:

> The CONSIST quota goal tuner initializes esz_bp to 0, producing an
> effective quota size (esz) of 1 byte on the first tick.
> damos_quota_is_full() rejects all regions when esz < min_region_sz
> (default PAGE_SIZE = 4096), so no regions can be tried and no
> feedback reaches the tuner — a bootstrapping deadlock.

That depend on whether the goal is already [over]-achieved.  If the goal is
achieved, the tuner will think no change is needed, so keep the
effectively-zero quota.  If the goal is over-achived, the tuner will think the
DAMOS scheme should be less aggressive, but it is already effectively-zero
quota, so keep having effectively-zero quota.

If the ogal is under-achived, the logic will iteratively increase the internal
esz (esz_bp), until it exceeds the min_region_sz, and finally start making some
effects.

So, unless the goal is already [over]-achieved, there is no deadlock.  If the
goal is already [over]-achieved, why we would want to make DAMOS do something?

Am I missing something?

I'd like to discuss this high level thing first, before digging deep into the
details.


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [PATCH v2 04/10] liveupdate: add support for linked-block serialization
From: Pasha Tatashin @ 2026-05-17 18:40 UTC (permalink / raw)
  To: Mike Rapoport
  Cc: Pasha Tatashin, linux-kselftest, shuah, akpm, linux-mm, skhan,
	linux-doc, linux-kernel, corbet, dmatlack, kexec, pratyush,
	skhawaja, graf
In-Reply-To: <agn6QeIamoeMkesv@kernel.org>

On 05-17 20:26, Mike Rapoport wrote:
> On Thu, May 14, 2026 at 10:26:22PM +0000, Pasha Tatashin wrote:
> > Introduce a linked-block serialization mechanism for LUO state.
> > 
> > Previously, LUO used contiguous memory blocks for serializing sessions
> > and files, which imposed limits on the total number of items that could
> > be preserved across a live update.
> > 
> > This commit adds the infrastructure for a more flexible, block-based
> > approach where serialized data is stored in a chain of linked blocks.
> > This is a preparatory step to allow an unlimited number of
> > luo_sessions and luo_files to be preserved.
> 
> Shouldn't it be a part of KHO?

It can be. However, at the moment, it is only used by LUO, so I kept it 
there. Sami is also planning to use it for IOMMU; if so, he will adopt 
the interface for his usage as well, and move it to a separate KHO 
library.

>  
> > Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> > ---
> >  Documentation/core-api/liveupdate.rst |   8 +
> >  include/linux/kho/abi/luo.h           |  22 ++
> >  kernel/liveupdate/Makefile            |   1 +
> >  kernel/liveupdate/luo_block.c         | 388 ++++++++++++++++++++++++++
> >  kernel/liveupdate/luo_internal.h      |  57 ++++
> >  5 files changed, 476 insertions(+)
> >  create mode 100644 kernel/liveupdate/luo_block.c
> 
> -- 
> Sincerely yours,
> Mike.

^ permalink raw reply

* [PATCH v5 13/13] docs: iio: add documentation for ad9910 driver
From: Rodrigo Alencar via B4 Relay @ 2026-05-17 18:37 UTC (permalink / raw)
  To: linux-iio, devicetree, linux-kernel, linux-doc, linux-hardening
  Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan,
	Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
In-Reply-To: <20260517-ad9910-iio-driver-v5-0-31599c88314a@analog.com>

From: Rodrigo Alencar <rodrigo.alencar@analog.com>

Add documentation for the AD9910 DDS IIO driver, which describes channels,
DDS modes, attributes and ABI usage examples.

Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
 Documentation/iio/ad9910.rst | 666 +++++++++++++++++++++++++++++++++++++++++++
 Documentation/iio/index.rst  |   1 +
 MAINTAINERS                  |   1 +
 3 files changed, 668 insertions(+)

diff --git a/Documentation/iio/ad9910.rst b/Documentation/iio/ad9910.rst
new file mode 100644
index 000000000000..dbcf8f8a1dda
--- /dev/null
+++ b/Documentation/iio/ad9910.rst
@@ -0,0 +1,666 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+=============
+AD9910 driver
+=============
+
+Direct Digital Synthesizer (DDS) driver for the Analog Devices Inc. AD9910.
+The module name is ``ad9910``.
+
+* `AD9910 <https://www.analog.com/en/products/ad9910.html>`_
+
+The AD9910 is a 1 GSPS DDS with a 14-bit DAC, controlled over SPI. The driver
+exposes the device through the IIO ``altvoltage`` channel type and supports
+five DDS operating modes: single tone, parallel port modulation, digital ramp
+generation (DRG), RAM playback and output shift keying (OSK). The device has
+8 hardware profiles, each capable of storing independent single tone and RAM
+playback parameters.
+
+
+Channel hierarchy
+=================
+
+The driver exposes the following IIO output channels, each identified by a
+unique channel number and a human-readable label. The ``phy`` channel is the
+root of the hierarchy. Changing its ``sampling_frequency`` reconfigures the
+system clock (SYSCLK) which affects all other channels. Most of the
+mode-specific channels have an ``enable`` attribute that turns the mode on/off.
+
+.. flat-table::
+   :header-rows: 1
+
+   * - Channel
+     - Label
+     - Parent
+     - Description
+
+   * - ``out_altvoltage100``
+     - ``phy``
+     -
+     - Physical output: system clock and profile control.
+       See `Physical channel`_.
+
+   * - ``out_altvoltage110`` ... ``out_altvoltage117``
+     - ``profile0`` ... ``profile7``
+     - ``phy``
+     - Single tone control: frequency, phase, amplitude.
+       See `Single Tone mode`_.
+
+   * - ``out_altvoltage120``
+     - ``parallel_port``
+     - ``phy``
+     - Parallel port modulation channel.
+       See `Parallel Port mode`_.
+
+   * - ``out_altvoltage130``
+     - ``digital_ramp_generator``
+     - ``phy``
+     - Digital ramp generator (DRG) control: enable.
+       See `Digital ramp generator (DRG)`_.
+
+   * - ``out_altvoltage131``
+     - ``digital_ramp_up``
+     - ``digital_ramp_generator``
+     - DRG ramp-up parameters: dwell enable, limits, rate of change, ramp rate.
+
+   * - ``out_altvoltage132``
+     - ``digital_ramp_down``
+     - ``digital_ramp_generator``
+     - DRG ramp-down parameters: dwell enable, limits, rate of change, ramp rate.
+
+   * - ``out_altvoltage140``
+     - ``ram_control``
+     - ``phy``
+     - RAM playback: enable, frequency, phase and sampling frequency for active
+       profile. See `RAM mode`_.
+
+   * - ``out_altvoltage150``
+     - ``output_shift_keying``
+     - ``phy``
+     - Output shift keying (OSK): enable, amplitude scale, ramp rate,
+       rate of change control. See `Output Shift Keying (OSK)`_.
+
+DDS modes
+=========
+
+The AD9910 supports multiple modes of operation that can be configured
+independently or in combination. Such modes and their corresponding IIO channels
+are described in this section. Each DDS core parameter (frequency, phase and
+amplitude) value can come from different sources, but only one is active at a
+time. This activation depends on a priority list, which is based on the enable
+and destination configurations for such modes. The following tables are
+extracted from the AD9910 datasheet and summarizes the control parameters for
+each mode and their priority when multiple sources are enabled simultaneously:
+
+.. flat-table:: DDS Frequency Control
+   :header-rows: 1
+
+   * - Priority
+     - Data Source
+     - Conditions
+
+   * - Highest Priority
+     - RAM
+     - RAM enabled and data destination is frequency
+
+   * -
+     - DRG
+     - DRG enabled and data destination is frequency
+
+   * -
+     - Parallel data and Frequency Tuning Word, FTW (frequency_offset)
+     - Parallel data port enabled and data destination is frequency
+
+   * -
+     - FTW register (frequency)
+     - RAM enabled and data destination is not frequency
+
+   * - Lowest Priority
+     - FTW (frequency) in single tone channel for the active profile
+     - All other cases
+
+.. flat-table:: DDS Phase Control
+   :header-rows: 1
+
+   * - Priority
+     - Data Source
+     - Conditions
+
+   * - Highest Priority
+     - RAM
+     - RAM enabled and data destination is phase or polar
+
+   * -
+     - DRG
+     - DRG enabled and data destination is phase
+
+   * -
+     - Parallel data port
+     - Parallel data port enabled and data destination is phase
+
+   * -
+     - Parallel data port and Phase Offset Word, POW register LSBs (phase_offset)
+     - Parallel data port enabled and data destination is polar
+
+   * -
+     - POW register (phase)
+     - RAM enabled and destination is not phase nor polar
+
+   * - Lowest Priority
+     - POW (phase) in single tone channel for the active profile
+     - All other cases
+
+.. flat-table:: DDS Amplitude Control
+   :header-rows: 1
+
+   * - Priority
+     - Data Source
+     - Conditions
+
+   * - Highest Priority
+     - Amplitude Scale Factor, ASF register and OSK generator
+     - OSK enabled
+
+   * -
+     - RAM
+     - RAM enabled and data destination is amplitude or polar
+
+   * -
+     - DRG
+     - DRG enabled and data destination is amplitude
+
+   * -
+     - Parallel data port
+     - Parallel data port enabled and data destination is amplitude
+
+   * -
+     - Parallel data port and ASF register LSBs (scale_offset)
+     - Parallel data port enabled and data destination is polar
+
+   * - Lowest Priority
+     - ASF (scale) in single tone channel for the active profile
+     - (Amplitude scale is already enabled by default)
+
+While debugging or testing, the debug attributes ``frequency_source``,
+``phase_source`` and ``amplitude_source`` can be used to read the label of
+the channel that is actively controlling the correspondent DDS parameter,
+which reflects the priority list described above.
+
+Single Tone mode
+----------------
+
+Single tone is the baseline operating mode. The ``profileY`` channels
+provide enable, frequency, phase and amplitude control:
+
+.. flat-table::
+   :header-rows: 1
+
+   * - Attribute
+     - Unit
+     - Description
+
+   * - ``en``
+     - boolean (0 or 1)
+     - Enable/disable profile Y. Only one profile can be active at a
+       time. Then enabling a profile disables the current active profile.
+       Disabling an active profile brings the device to a powered down state.
+
+   * - ``frequency``
+     - Hz
+     - Output frequency. Range :math:`[0, f_{SYSCLK}/2)`. Stored in the
+       profile's frequency tuning word (FTW).
+
+   * - ``phase``
+     - rad
+     - Phase offset. Range :math:`[0, 2\pi)`. Stored in the profile's phase
+       offset word (POW).
+
+   * - ``scale``
+     - fractional
+     - Amplitude scale factor. Range :math:`[0, 1]`. Stored in the profile's
+       amplitude scale factor (ASF).
+
+Profile switching is allowed while RAM mode is enabled. In that case single tone
+parameters are stored in a shadow register and are not written to hardware until
+RAM mode is disabled.
+
+Usage examples
+^^^^^^^^^^^^^^
+
+Configure a 100 MHz tone in profile to 2 and set it as the active profile:
+
+.. code-block:: bash
+
+  echo 100000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage112_frequency
+  echo 0.5 > /sys/bus/iio/devices/iio:device0/out_altvoltage112_scale
+  echo 0 > /sys/bus/iio/devices/iio:device0/out_altvoltage112_phase
+
+  # Activate profile 2
+  echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage112_en
+
+Read back the current single tone frequency:
+
+.. code-block:: bash
+
+  cat /sys/bus/iio/devices/iio:device0/out_altvoltage112_frequency
+
+Parallel Port mode
+------------------
+
+The parallel port allows real-time modulation of DDS parameters through a
+16-bit external data bus.
+
+.. flat-table::
+   :header-rows: 1
+
+   * - Attribute
+     - Unit
+     - Description
+
+   * - ``frequency_scale``
+     - power-of-2
+     - Frequency modulation (FM) gain multiplier applied to 16-bit parallel
+       input. Range :math:`[1, 32768]`, must be a power of 2.
+
+   * - ``frequency_offset``
+     - Hz
+     - Base FTW to which scaled parallel data is added. Range :math:`[0, f_{SYSCLK}/2)`.
+
+   * - ``phase_offset``
+     - rad
+     - Base phase for polar modulation. Lower 8 bits of POW register.
+       Range :math:`[0, 2\pi/256)`.
+
+   * - ``scale_offset``
+     - fractional
+     - Base amplitude for polar modulation. Lower 6 bits of ASF register.
+       Range :math:`[0, 1/256)`.
+
+Usage examples
+^^^^^^^^^^^^^^
+
+Set parallel port frequency modulation with a scale of 16 and a 50 MHz
+offset:
+
+.. code-block:: bash
+
+  echo 16 > /sys/bus/iio/devices/iio:device0/out_altvoltage120_frequency_scale
+  echo 50000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage120_frequency_offset
+
+Digital ramp generator (DRG)
+----------------------------
+
+The DRG produces linear frequency, phase or amplitude sweeps using dedicated
+hardware. It is controlled through three channels: a parent control channel
+(``digital_ramp_generator``) and two child ramp channels
+(``digital_ramp_up``, ``digital_ramp_down``).
+
+The DRG can target only one destination at a time (frequency, phase or
+amplitude). Destination selection follows a "last write wins" policy: writing
+any value (including zero) to a destination-specific attribute (e.g.
+``frequency``, ``frequency_roc``, ``phase``, ``phase_roc``, ``scale`` or
+``scale_roc``) switches the DRG destination accordingly. Reading an attribute
+whose destination is not currently active returns ``-EBUSY``.
+
+Control channel attributes
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. flat-table::
+   :header-rows: 1
+
+   * - Attribute
+     - Unit
+     - Description
+
+   * - ``en``
+     - boolean
+     - Enable/disable the DRG.
+
+Ramp channel attributes
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``digital_ramp_up`` and ``digital_ramp_down`` channels share the same
+attribute set but configure ascending and descending ramp parameters
+independently:
+
+.. flat-table::
+   :header-rows: 1
+
+   * - Attribute
+     - Unit
+     - Description
+
+   * - ``dwell_en``
+     - boolean
+     - Enable dwell at the ramp limit. When disabled, the ramp auto-transitions
+       at this limit without waiting for the DRCTL pin. Disabling both creates a
+       bidirectional continuous ramp (Triangular pattern). Other configurations
+       create a single-shot ramp at the transition of the DRCTL pin: ramp-up
+       only, ramp-down only or bidirectional with dwell at the limits.
+
+   * - ``frequency``
+     - Hz
+     - Frequency ramp limit. Range: :math:`[0, f_{SYSCLK}/2)`. Writing a value
+       sets the ramp destination to frequency. Reading back returns the
+       currently active frequency limit or -EBUSY if other destination is
+       active (phase or amplitude).
+
+   * - ``phase``
+     - rad
+     - Phase ramp limit. Range: :math:`[0, 2\pi)`. Writing a value sets the
+       ramp destination to phase. Reading back returns the currently active
+       phase limit or -EBUSY if other destination is active (frequency or
+       amplitude).
+
+   * - ``scale``
+     - fractional
+     - Amplitude scale ramp limit. Range: :math:`[0, 1)`. Writing a value sets
+       the ramp destination to amplitude. Reading back returns the currently
+       active scale limit or -EBUSY if other destination is active (frequency
+       or phase).
+
+   * - ``sampling_frequency``
+     - Hz
+     - Ramp clock rate. It is controlled by an integer divider so the requested
+       value will adjust to nearest supported value.
+
+   * - ``frequency_roc``
+     - Hz/s
+     - Frequency rate of change. Sets the per-tick frequency increment/decrement
+       based on the current ramp clock rate.
+
+   * - ``phase_roc``
+     - rad/s
+     - Phase rate of change. Sets the per-tick phase increment/decrement based
+       on the current ramp clock rate.
+
+   * - ``scale_roc``
+     - 1/s
+     - Amplitude scale rate of change. Sets the per-tick amplitude scale
+       increment/decrement based on the current ramp clock rate.
+
+Usage examples
+^^^^^^^^^^^^^^
+
+Configure a frequency sweep from 40 MHz to 60 MHz with a rate of change of
+25 GHz/s:
+
+.. code-block:: bash
+
+  # Disable dwell on both limits for a bidirectional continuous ramp
+  echo 0 > /sys/bus/iio/devices/iio:device0/out_altvoltage131_dwell_en
+  echo 0 > /sys/bus/iio/devices/iio:device0/out_altvoltage132_dwell_en
+
+  # Set ramp limits
+  echo 60000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage131_frequency
+  echo 40000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage132_frequency
+
+  # Set ramp rate
+  echo 25000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage131_sampling_frequency
+  echo 25000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage132_sampling_frequency
+
+  # Set frequency rate of change (Hz/s)
+  echo 25000000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage131_frequency_roc
+  echo 25000000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage132_frequency_roc
+
+  # Enable the DRG
+  echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage130_en
+
+RAM mode
+--------
+
+The AD9910 contains a 1024 x 32-bit RAM that can be loaded with waveform data
+and played back to modulate frequency, phase, amplitude, or polar (phase +
+amplitude) parameters.
+
+RAM control channel attributes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. flat-table::
+   :header-rows: 1
+
+   * - Attribute
+     - Unit
+     - Description
+
+   * - ``en``
+     - boolean
+     - Enable/disable RAM playback. Toggling swaps profile registers between
+       single tone and RAM configurations across all 8 profiles.
+
+   * - ``frequency``
+     - Hz
+     - Frequency tuning word used as the single tone frequency when
+       RAM destination is not ``frequency``. Range: :math:`[0, f_{SYSCLK}/2)`.
+
+   * - ``phase``
+     - rad
+     - Phase offset word used as the single tone phase when RAM destination
+       is not ``phase``. Range: :math:`[0, 2\pi)`.
+
+   * - ``sampling_frequency``
+     - Hz
+     - RAM playback step rate of the active profile, which controls how fast the
+       address counter advances. It is controlled by an integer divider so the
+       requested value will adjust to nearest supported value.
+
+Loading RAM data
+^^^^^^^^^^^^^^^^
+
+RAM data is loaded through the firmware upload framework. The driver registers
+a firmware upload sysfs entry named ``iio_deviceX:ram``. The firmware data
+follows a binary format (version 1) with an 80-byte header followed by data
+words. All fields are big-endian.
+
+.. flat-table:: RAM firmware header (80 bytes)
+   :header-rows: 1
+
+   * - Offset
+     - Size
+     - Field
+     - Description
+
+   * - 0
+     - 4
+     - ``magic``
+     - Magic number: ``0x00AD9910``
+
+   * - 4
+     - 2
+     - ``version``
+     - Format version: ``0x0001``
+
+   * - 6
+     - 2
+     - ``wcount``
+     - Number of 32-bit RAM data words (0--1024)
+
+   * - 8
+     - 4
+     - ``crc``
+     - CRC32 checksum over ``cfr1``, ``profiles`` and ``words``
+
+   * - 12
+     - 4
+     - ``cfr1``
+     - CFR1 register value. Only RAM-relevant bits are used:
+       bits [30:29] set data destination (00: frequency, 01: phase,
+       10: amplitude, 11: polar); bits [20:17] set internal profile
+       control (see datasheet Table 14)
+
+   * - 16
+     - 64
+     - ``profiles[0..7]``
+     - 8 sets of 8-byte RAM profile configurations (see below)
+
+   * - 80
+     - 4 x wcount
+     - ``words[]``
+     - RAM data words in reverse order
+
+Each 8-byte profile entry contains:
+
+.. flat-table:: RAM profile entry (8 bytes)
+   :header-rows: 1
+
+   * - Bits
+     - Field
+     - Description
+
+   * - [55:40]
+     - Address step rate
+     - Controls playback speed for this profile
+
+   * - [39:30]
+     - End address
+     - Last RAM address for this profile
+
+   * - [23:14]
+     - Start address
+     - First RAM address for this profile
+
+   * - [5]
+     - No-dwell high
+     - No-dwell at high limit (ramp-up mode)
+
+   * - [3]
+     - Zero-crossing
+     - Zero-crossing enable (direct-switch mode)
+
+   * - [2:0]
+     - Operating mode
+     - 000: direct switch, 001: ramp-up, 010: bidirectional,
+       011: bidirectional continuous, 100: ramp-up continuous
+
+Usage examples
+^^^^^^^^^^^^^^
+
+Configure RAM mode with firmware data and enable it:
+
+.. code-block:: bash
+
+  # Load RAM data via firmware upload
+  echo 1 > /sys/class/firmware/iio\:device0\:ram/loading
+  cat ad9910-ram.bin > /sys/class/firmware/iio\:device0\:ram/data
+  echo 0 > /sys/class/firmware/iio\:device0\:ram/loading
+
+  # Enable RAM mode
+  echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage140_en
+
+Output Shift Keying (OSK)
+-------------------------
+
+OSK controls the output amplitude envelope, allowing the output to be ramped
+on/off rather than switched abruptly.
+
+.. flat-table::
+   :header-rows: 1
+
+   * - Attribute
+     - Unit
+     - Description
+
+   * - ``en``
+     - boolean (0 or 1)
+     - Enable/disable OSK.
+
+   * - ``scale``
+     - fractional
+     - Target amplitude for the OSK ramp. 14-bit ASF field. Range: :math:`[0, 1)`.
+
+   * - ``scale_roc``
+     - 1/s
+     - Amplitude scale rate of change. Writing a non-zero value enables
+       automatic OSK and selects the closest hardware step size. Writing ``0``
+       disables automatic ramping (manual control of the ASF register using
+       ``scale``). Writing the maximum available value enables pin-controlled
+       immediate transition with no ramping.
+
+   * - ``scale_roc_available``
+     - 1/s
+     - Lists the available ``scale_roc`` values based on the current
+       ``sampling_frequency``. The first value is always ``0`` (disabled) and
+       the last value corresponds to pin-controlled immediate mode.
+
+   * - ``sampling_frequency``
+     - Hz
+     - OSK ramp rate. It is controlled by an integer divider so the requested
+       value will adjust to nearest supported value.
+
+Usage examples
+^^^^^^^^^^^^^^
+
+Enable OSK with automatic ramping:
+
+.. code-block:: bash
+
+  # Set ramp rate
+  echo 1000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_sampling_frequency
+
+  # Check available rate of change values
+  cat /sys/bus/iio/devices/iio:device0/out_altvoltage150_scale_roc_available
+
+  # Enable automatic OSK with a rate of change
+  echo 61.035000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_scale_roc
+
+  # Enable OSK
+  echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_en
+
+Enable pin-controlled immediate OSK:
+
+.. code-block:: bash
+
+  # Read the last (highest) available value for pin-controlled mode
+  cat /sys/bus/iio/devices/iio:device0/out_altvoltage150_scale_roc_available
+
+  # Enable OSK in manual mode (no rate of change)
+  echo 0 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_scale_roc
+  echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_en
+
+  # Set target amplitude to full scale
+  echo 1.0 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_scale
+
+Physical channel
+================
+
+The ``phy`` channel provides device-level control:
+
+.. flat-table::
+   :header-rows: 1
+
+   * - Attribute
+     - Unit
+     - Description
+
+   * - ``sampling_frequency``
+     - Hz
+     - System clock (SYSCLK) frequency. When the internal PLL is enabled
+       (via the ``adi,pll-enable`` devicetree property), configures the PLL
+       multiplier (range 420--1000 MHz). Without PLL, the reference clock can
+       only be divided by 2.
+
+   * - ``powerdown``
+     - boolean (0 or 1)
+     - Software power-down. Writing 1 powers down the digital core, DAC,
+       reference clock input and auxiliary DAC simultaneously.
+
+Usage examples
+--------------
+
+Set the system clock to 1 GHz:
+
+.. code-block:: bash
+
+  echo 1000000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage100_sampling_frequency
+
+Read current system clock frequency:
+
+.. code-block:: bash
+
+  cat /sys/bus/iio/devices/iio:device0/out_altvoltage100_sampling_frequency
+
+Power down the device:
+
+.. code-block:: bash
+
+  echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage100_powerdown
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index 007e0a1fcc5a..1ada7b460066 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -30,6 +30,7 @@ Industrial I/O Kernel Drivers
    ad7606
    ad7625
    ad7944
+   ad9910
    ade9000
    adis16475
    adis16480
diff --git a/MAINTAINERS b/MAINTAINERS
index c39affe4157a..363b7af827c3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1645,6 +1645,7 @@ S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910
 F:	Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml
+F:	Documentation/iio/ad9910.rst
 F:	drivers/iio/frequency/ad9910.c
 
 ANALOG DEVICES INC MAX22007 DRIVER

-- 
2.43.0



^ permalink raw reply related

* [PATCH v5 11/13] iio: frequency: ad9910: show channel priority in debugfs
From: Rodrigo Alencar via B4 Relay @ 2026-05-17 18:37 UTC (permalink / raw)
  To: linux-iio, devicetree, linux-kernel, linux-doc, linux-hardening
  Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan,
	Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
In-Reply-To: <20260517-ad9910-iio-driver-v5-0-31599c88314a@analog.com>

From: Rodrigo Alencar <rodrigo.alencar@analog.com>

Expose frequency_source, phase_source and amplitude_source attributes in
debugfs. Those indicate from which channel the specific DDS parameter is
being sourced by returning its label. The implementation follows the
priority table found in the datasheet.

Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
 drivers/iio/frequency/ad9910.c | 148 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 148 insertions(+)

diff --git a/drivers/iio/frequency/ad9910.c b/drivers/iio/frequency/ad9910.c
index 91f28c7de68b..8c8e73f340f8 100644
--- a/drivers/iio/frequency/ad9910.c
+++ b/drivers/iio/frequency/ad9910.c
@@ -22,6 +22,7 @@
 #include <linux/property.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
+#include <linux/seq_file.h>
 #include <linux/spi/spi.h>
 #include <linux/sysfs.h>
 #include <linux/types.h>
@@ -2116,6 +2117,146 @@ static int ad9910_setup(struct device *dev, struct ad9910_state *st,
 	return ad9910_io_update(st);
 }
 
+static inline const char *ad9910_frequency_source_get(struct ad9910_state *st)
+{
+	bool ram_en, mode_en;
+
+	guard(mutex)(&st->lock);
+
+	/* RAM enabled and data destination is frequency */
+	ram_en = AD9910_RAM_ENABLED(st);
+	if (ram_en && AD9910_DEST_FREQUENCY ==
+		      FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK,
+				st->reg[AD9910_REG_CFR1].val32))
+		return ad9910_channel_str[AD9910_CHAN_IDX_RAM];
+
+	/* DRG enabled and data destination is frequency */
+	mode_en = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK,
+			    st->reg[AD9910_REG_CFR2].val32);
+	if (mode_en && AD9910_DEST_FREQUENCY ==
+		       FIELD_GET(AD9910_CFR2_DRG_DEST_MSK,
+				 st->reg[AD9910_REG_CFR2].val32))
+		return ad9910_channel_str[AD9910_CHAN_IDX_DRG];
+
+	/* Parallel data port enabled and data destination is frequency */
+	mode_en = FIELD_GET(AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK,
+			    st->reg[AD9910_REG_CFR2].val32);
+	if (mode_en) /* TODO: get destination from backend once it is supported */
+		return ad9910_channel_str[AD9910_CHAN_IDX_PARALLEL_PORT];
+
+	/* FTW: RAM enabled and data destination is phase, amplitude, or polar */
+	if (ram_en)
+		return ad9910_channel_str[AD9910_CHAN_IDX_RAM];
+
+	/* single tone profiles */
+	return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile];
+}
+
+static int ad9910_frequency_source_show(struct seq_file *s, void *ignored)
+{
+	seq_printf(s, "%s\n", ad9910_frequency_source_get(s->private));
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ad9910_frequency_source);
+
+static inline const char *ad9910_phase_source_get(struct ad9910_state *st)
+{
+	bool ram_en, mode_en;
+	u32 destination;
+
+	guard(mutex)(&st->lock);
+
+	/* RAM enabled and data destination is phase or polar  */
+	ram_en = AD9910_RAM_ENABLED(st);
+	if (ram_en) {
+		destination = FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK,
+					st->reg[AD9910_REG_CFR1].val32);
+		if (destination == AD9910_DEST_PHASE ||
+		    destination == AD9910_DEST_POLAR)
+			return ad9910_channel_str[AD9910_CHAN_IDX_RAM];
+	}
+
+	/* DRG enabled and data destination is phase */
+	mode_en = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK,
+			    st->reg[AD9910_REG_CFR2].val32);
+	if (mode_en && AD9910_DEST_PHASE ==
+		       FIELD_GET(AD9910_CFR2_DRG_DEST_MSK,
+				 st->reg[AD9910_REG_CFR2].val32))
+		return ad9910_channel_str[AD9910_CHAN_IDX_DRG];
+
+	/* Parallel data port enabled and data destination is phase */
+	mode_en = FIELD_GET(AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK,
+			    st->reg[AD9910_REG_CFR2].val32);
+	if (mode_en) /* TODO: get destination from backend once it is supported */
+		return ad9910_channel_str[AD9910_CHAN_IDX_PARALLEL_PORT];
+
+	/* POW: RAM enabled and data destination is frequency, amplitude, or polar */
+	if (ram_en)
+		return ad9910_channel_str[AD9910_CHAN_IDX_RAM];
+
+	/* single tone profiles */
+	return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile];
+}
+
+static int ad9910_phase_source_show(struct seq_file *s, void *ignored)
+{
+	seq_printf(s, "%s\n", ad9910_phase_source_get(s->private));
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ad9910_phase_source);
+
+static inline const char *ad9910_amplitude_source_get(struct ad9910_state *st)
+{
+	bool ram_en, mode_en;
+	u32 destination;
+
+	guard(mutex)(&st->lock);
+
+	/* OSK enabled */
+	mode_en = FIELD_GET(AD9910_CFR1_OSK_ENABLE_MSK,
+			    st->reg[AD9910_REG_CFR1].val32);
+	if (mode_en)
+		return ad9910_channel_str[AD9910_CHAN_IDX_OSK];
+
+	/* RAM enabled and data destination is amplitude or polar */
+	ram_en = AD9910_RAM_ENABLED(st);
+	if (ram_en) {
+		destination = FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK,
+					st->reg[AD9910_REG_CFR1].val32);
+		if (destination == AD9910_DEST_AMPLITUDE ||
+		    destination == AD9910_DEST_POLAR)
+			return ad9910_channel_str[AD9910_CHAN_IDX_RAM];
+	}
+
+	/* DRG enabled and data destination is amplitude */
+	mode_en = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK,
+			    st->reg[AD9910_REG_CFR2].val32);
+	if (mode_en && AD9910_DEST_AMPLITUDE ==
+		       FIELD_GET(AD9910_CFR2_DRG_DEST_MSK,
+				 st->reg[AD9910_REG_CFR2].val32))
+		return ad9910_channel_str[AD9910_CHAN_IDX_DRG];
+
+	/* Parallel data port enabled and data destination is amplitude */
+	mode_en = FIELD_GET(AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK,
+			    st->reg[AD9910_REG_CFR2].val32);
+	if (mode_en) /* TODO: get destination from backend once it is supported */
+		return ad9910_channel_str[AD9910_CHAN_IDX_PARALLEL_PORT];
+
+	/* only way to control amplitude at this point is through OSK */
+	if (ram_en)
+		return ad9910_channel_str[AD9910_CHAN_IDX_OSK];
+
+	/* single tone profiles */
+	return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile];
+}
+
+static int ad9910_amplitude_source_show(struct seq_file *s, void *ignored)
+{
+	seq_printf(s, "%s\n", ad9910_amplitude_source_get(s->private));
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ad9910_amplitude_source);
+
 static inline void ad9910_debugfs_init(struct ad9910_state *st,
 				       struct iio_dev *indio_dev)
 {
@@ -2131,6 +2272,13 @@ static inline void ad9910_debugfs_init(struct ad9910_state *st,
 
 	snprintf(buf, sizeof(buf), "/sys/class/firmware/%s/data", st->ram_fwu_name);
 	debugfs_create_symlink("ram_data", d, buf);
+
+	debugfs_create_file("frequency_source", 0400, d, st,
+			    &ad9910_frequency_source_fops);
+	debugfs_create_file("phase_source", 0400, d, st,
+			    &ad9910_phase_source_fops);
+	debugfs_create_file("amplitude_source", 0400, d, st,
+			    &ad9910_amplitude_source_fops);
 }
 
 static int ad9910_probe(struct spi_device *spi)

-- 
2.43.0



^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox