From 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 Feb 2023 18:24:12 -0800 Subject: Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ... --- drivers/ipack/devices/Kconfig | 7 + drivers/ipack/devices/Makefile | 2 + drivers/ipack/devices/ipoctal.c | 775 ++++++++++++++++++++++++++++++++++++++++ drivers/ipack/devices/ipoctal.h | 36 ++ drivers/ipack/devices/scc2698.h | 225 ++++++++++++ 5 files changed, 1045 insertions(+) create mode 100644 drivers/ipack/devices/Kconfig create mode 100644 drivers/ipack/devices/Makefile create mode 100644 drivers/ipack/devices/ipoctal.c create mode 100644 drivers/ipack/devices/ipoctal.h create mode 100644 drivers/ipack/devices/scc2698.h (limited to 'drivers/ipack/devices') diff --git a/drivers/ipack/devices/Kconfig b/drivers/ipack/devices/Kconfig new file mode 100644 index 000000000..75a62d170 --- /dev/null +++ b/drivers/ipack/devices/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SERIAL_IPOCTAL + tristate "IndustryPack IP-OCTAL uart support" + depends on IPACK_BUS && TTY + help + This driver supports the IPOCTAL serial port device for the IndustryPack bus. + default n diff --git a/drivers/ipack/devices/Makefile b/drivers/ipack/devices/Makefile new file mode 100644 index 000000000..67a7ed684 --- /dev/null +++ b/drivers/ipack/devices/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_SERIAL_IPOCTAL) += ipoctal.o diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c new file mode 100644 index 000000000..fc0027407 --- /dev/null +++ b/drivers/ipack/devices/ipoctal.c @@ -0,0 +1,775 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * driver for the GE IP-OCTAL boards + * + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipoctal.h" +#include "scc2698.h" + +#define IP_OCTAL_ID_SPACE_VECTOR 0x41 +#define IP_OCTAL_NB_BLOCKS 4 + +static const struct tty_operations ipoctal_fops; + +struct ipoctal_channel { + struct ipoctal_stats stats; + unsigned int nb_bytes; + wait_queue_head_t queue; + spinlock_t lock; + unsigned int pointer_read; + unsigned int pointer_write; + struct tty_port tty_port; + bool tty_registered; + union scc2698_channel __iomem *regs; + union scc2698_block __iomem *block_regs; + unsigned int board_id; + u8 isr_rx_rdy_mask; + u8 isr_tx_rdy_mask; + unsigned int rx_enable; +}; + +struct ipoctal { + struct ipack_device *dev; + unsigned int board_id; + struct ipoctal_channel channel[NR_CHANNELS]; + struct tty_driver *tty_drv; + u8 __iomem *mem8_space; + u8 __iomem *int_space; +}; + +static inline struct ipoctal *chan_to_ipoctal(struct ipoctal_channel *chan, + unsigned int index) +{ + return container_of(chan, struct ipoctal, channel[index]); +} + +static void ipoctal_reset_channel(struct ipoctal_channel *channel) +{ + iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); + channel->rx_enable = 0; + iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); +} + +static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct ipoctal_channel *channel; + + channel = dev_get_drvdata(tty->dev); + + /* + * Enable RX. TX will be enabled when + * there is something to send + */ + iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); + channel->rx_enable = 1; + return 0; +} + +static int ipoctal_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct ipoctal_channel *channel = dev_get_drvdata(tty->dev); + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); + int res; + + if (!ipack_get_carrier(ipoctal->dev)) + return -EBUSY; + + res = tty_standard_install(driver, tty); + if (res) + goto err_put_carrier; + + tty->driver_data = channel; + + return 0; + +err_put_carrier: + ipack_put_carrier(ipoctal->dev); + + return res; +} + +static int ipoctal_open(struct tty_struct *tty, struct file *file) +{ + struct ipoctal_channel *channel = tty->driver_data; + + return tty_port_open(&channel->tty_port, tty, file); +} + +static void ipoctal_reset_stats(struct ipoctal_stats *stats) +{ + stats->tx = 0; + stats->rx = 0; + stats->rcv_break = 0; + stats->framing_err = 0; + stats->overrun_err = 0; + stats->parity_err = 0; +} + +static void ipoctal_free_channel(struct ipoctal_channel *channel) +{ + ipoctal_reset_stats(&channel->stats); + channel->pointer_read = 0; + channel->pointer_write = 0; + channel->nb_bytes = 0; +} + +static void ipoctal_close(struct tty_struct *tty, struct file *filp) +{ + struct ipoctal_channel *channel = tty->driver_data; + + tty_port_close(&channel->tty_port, tty, filp); + ipoctal_free_channel(channel); +} + +static int ipoctal_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct ipoctal_channel *channel = tty->driver_data; + + icount->cts = 0; + icount->dsr = 0; + icount->rng = 0; + icount->dcd = 0; + icount->rx = channel->stats.rx; + icount->tx = channel->stats.tx; + icount->frame = channel->stats.framing_err; + icount->parity = channel->stats.parity_err; + icount->brk = channel->stats.rcv_break; + return 0; +} + +static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr) +{ + struct tty_port *port = &channel->tty_port; + unsigned char value; + unsigned char flag; + u8 isr; + + do { + value = ioread8(&channel->regs->r.rhr); + flag = TTY_NORMAL; + /* Error: count statistics */ + if (sr & SR_ERROR) { + iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); + + if (sr & SR_OVERRUN_ERROR) { + channel->stats.overrun_err++; + /* Overrun doesn't affect the current character*/ + tty_insert_flip_char(port, 0, TTY_OVERRUN); + } + if (sr & SR_PARITY_ERROR) { + channel->stats.parity_err++; + flag = TTY_PARITY; + } + if (sr & SR_FRAMING_ERROR) { + channel->stats.framing_err++; + flag = TTY_FRAME; + } + if (sr & SR_RECEIVED_BREAK) { + channel->stats.rcv_break++; + flag = TTY_BREAK; + } + } + tty_insert_flip_char(port, value, flag); + + /* Check if there are more characters in RX FIFO + * If there are more, the isr register for this channel + * has enabled the RxRDY|FFULL bit. + */ + isr = ioread8(&channel->block_regs->r.isr); + sr = ioread8(&channel->regs->r.sr); + } while (isr & channel->isr_rx_rdy_mask); + + tty_flip_buffer_push(port); +} + +static void ipoctal_irq_tx(struct ipoctal_channel *channel) +{ + unsigned char value; + unsigned int *pointer_write = &channel->pointer_write; + + if (channel->nb_bytes == 0) + return; + + spin_lock(&channel->lock); + value = channel->tty_port.xmit_buf[*pointer_write]; + iowrite8(value, &channel->regs->w.thr); + channel->stats.tx++; + (*pointer_write)++; + *pointer_write = *pointer_write % PAGE_SIZE; + channel->nb_bytes--; + spin_unlock(&channel->lock); +} + +static void ipoctal_irq_channel(struct ipoctal_channel *channel) +{ + u8 isr, sr; + + /* The HW is organized in pair of channels. See which register we need + * to read from */ + isr = ioread8(&channel->block_regs->r.isr); + sr = ioread8(&channel->regs->r.sr); + + if (isr & (IMR_DELTA_BREAK_A | IMR_DELTA_BREAK_B)) + iowrite8(CR_CMD_RESET_BREAK_CHANGE, &channel->regs->w.cr); + + if ((sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) { + iowrite8(CR_DISABLE_TX, &channel->regs->w.cr); + /* In case of RS-485, change from TX to RX when finishing TX. + * Half-duplex. */ + if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) { + iowrite8(CR_CMD_NEGATE_RTSN, &channel->regs->w.cr); + iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); + channel->rx_enable = 1; + } + } + + /* RX data */ + if ((isr & channel->isr_rx_rdy_mask) && (sr & SR_RX_READY)) + ipoctal_irq_rx(channel, sr); + + /* TX of each character */ + if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY)) + ipoctal_irq_tx(channel); +} + +static irqreturn_t ipoctal_irq_handler(void *arg) +{ + unsigned int i; + struct ipoctal *ipoctal = (struct ipoctal *) arg; + + /* Clear the IPack device interrupt */ + readw(ipoctal->int_space + ACK_INT_REQ0); + readw(ipoctal->int_space + ACK_INT_REQ1); + + /* Check all channels */ + for (i = 0; i < NR_CHANNELS; i++) + ipoctal_irq_channel(&ipoctal->channel[i]); + + return IRQ_HANDLED; +} + +static const struct tty_port_operations ipoctal_tty_port_ops = { + .dtr_rts = NULL, + .activate = ipoctal_port_activate, +}; + +static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, + unsigned int slot) +{ + int res; + int i; + struct tty_driver *drv; + struct ipoctal_channel *channel; + struct ipack_region *region; + void __iomem *addr; + union scc2698_channel __iomem *chan_regs; + union scc2698_block __iomem *block_regs; + + ipoctal->board_id = ipoctal->dev->id_device; + + region = &ipoctal->dev->region[IPACK_IO_SPACE]; + addr = devm_ioremap(&ipoctal->dev->dev, + region->start, region->size); + if (!addr) { + dev_err(&ipoctal->dev->dev, + "Unable to map slot [%d:%d] IO space!\n", + bus_nr, slot); + return -EADDRNOTAVAIL; + } + /* Save the virtual address to access the registers easily */ + chan_regs = + (union scc2698_channel __iomem *) addr; + block_regs = + (union scc2698_block __iomem *) addr; + + region = &ipoctal->dev->region[IPACK_INT_SPACE]; + ipoctal->int_space = + devm_ioremap(&ipoctal->dev->dev, + region->start, region->size); + if (!ipoctal->int_space) { + dev_err(&ipoctal->dev->dev, + "Unable to map slot [%d:%d] INT space!\n", + bus_nr, slot); + return -EADDRNOTAVAIL; + } + + region = &ipoctal->dev->region[IPACK_MEM8_SPACE]; + ipoctal->mem8_space = + devm_ioremap(&ipoctal->dev->dev, + region->start, 0x8000); + if (!ipoctal->mem8_space) { + dev_err(&ipoctal->dev->dev, + "Unable to map slot [%d:%d] MEM8 space!\n", + bus_nr, slot); + return -EADDRNOTAVAIL; + } + + + /* Disable RX and TX before touching anything */ + for (i = 0; i < NR_CHANNELS ; i++) { + struct ipoctal_channel *channel = &ipoctal->channel[i]; + channel->regs = chan_regs + i; + channel->block_regs = block_regs + (i >> 1); + channel->board_id = ipoctal->board_id; + if (i & 1) { + channel->isr_tx_rdy_mask = ISR_TxRDY_B; + channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_B; + } else { + channel->isr_tx_rdy_mask = ISR_TxRDY_A; + channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_A; + } + + ipoctal_reset_channel(channel); + iowrite8(MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY, + &channel->regs->w.mr); /* mr1 */ + iowrite8(0, &channel->regs->w.mr); /* mr2 */ + iowrite8(TX_CLK_9600 | RX_CLK_9600, &channel->regs->w.csr); + } + + for (i = 0; i < IP_OCTAL_NB_BLOCKS; i++) { + iowrite8(ACR_BRG_SET2, &block_regs[i].w.acr); + iowrite8(OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN | OPCR_MPOb_RTSN, + &block_regs[i].w.opcr); + iowrite8(IMR_TxRDY_A | IMR_RxRDY_FFULL_A | IMR_DELTA_BREAK_A | + IMR_TxRDY_B | IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B, + &block_regs[i].w.imr); + } + + /* Dummy write */ + iowrite8(1, ipoctal->mem8_space + 1); + + /* Register the TTY device */ + + /* Each IP-OCTAL channel is a TTY port */ + drv = tty_alloc_driver(NR_CHANNELS, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(drv)) + return PTR_ERR(drv); + + /* Fill struct tty_driver with ipoctal data */ + drv->owner = THIS_MODULE; + drv->driver_name = KBUILD_MODNAME; + drv->name = kasprintf(GFP_KERNEL, KBUILD_MODNAME ".%d.%d.", bus_nr, slot); + if (!drv->name) { + res = -ENOMEM; + goto err_put_driver; + } + drv->major = 0; + + drv->minor_start = 0; + drv->type = TTY_DRIVER_TYPE_SERIAL; + drv->subtype = SERIAL_TYPE_NORMAL; + drv->init_termios = tty_std_termios; + drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + drv->init_termios.c_ispeed = 9600; + drv->init_termios.c_ospeed = 9600; + + tty_set_operations(drv, &ipoctal_fops); + res = tty_register_driver(drv); + if (res) { + dev_err(&ipoctal->dev->dev, "Can't register tty driver.\n"); + goto err_free_name; + } + + /* Save struct tty_driver for use it when uninstalling the device */ + ipoctal->tty_drv = drv; + + for (i = 0; i < NR_CHANNELS; i++) { + struct device *tty_dev; + + channel = &ipoctal->channel[i]; + tty_port_init(&channel->tty_port); + res = tty_port_alloc_xmit_buf(&channel->tty_port); + if (res) + continue; + channel->tty_port.ops = &ipoctal_tty_port_ops; + + ipoctal_reset_stats(&channel->stats); + channel->nb_bytes = 0; + spin_lock_init(&channel->lock); + channel->pointer_read = 0; + channel->pointer_write = 0; + tty_dev = tty_port_register_device_attr(&channel->tty_port, drv, + i, NULL, channel, NULL); + if (IS_ERR(tty_dev)) { + dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n"); + tty_port_free_xmit_buf(&channel->tty_port); + tty_port_destroy(&channel->tty_port); + continue; + } + channel->tty_registered = true; + } + + /* + * IP-OCTAL has different addresses to copy its IRQ vector. + * Depending of the carrier these addresses are accesible or not. + * More info in the datasheet. + */ + ipoctal->dev->bus->ops->request_irq(ipoctal->dev, + ipoctal_irq_handler, ipoctal); + + return 0; + +err_free_name: + kfree(drv->name); +err_put_driver: + tty_driver_kref_put(drv); + + return res; +} + +static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel, + const unsigned char *buf, + int count) +{ + unsigned long flags; + int i; + unsigned int *pointer_read = &channel->pointer_read; + + /* Copy the bytes from the user buffer to the internal one */ + for (i = 0; i < count; i++) { + if (i <= (PAGE_SIZE - channel->nb_bytes)) { + spin_lock_irqsave(&channel->lock, flags); + channel->tty_port.xmit_buf[*pointer_read] = buf[i]; + *pointer_read = (*pointer_read + 1) % PAGE_SIZE; + channel->nb_bytes++; + spin_unlock_irqrestore(&channel->lock, flags); + } else { + break; + } + } + return i; +} + +static int ipoctal_write_tty(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct ipoctal_channel *channel = tty->driver_data; + unsigned int char_copied; + + char_copied = ipoctal_copy_write_buffer(channel, buf, count); + + /* As the IP-OCTAL 485 only supports half duplex, do it manually */ + if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) { + iowrite8(CR_DISABLE_RX, &channel->regs->w.cr); + channel->rx_enable = 0; + iowrite8(CR_CMD_ASSERT_RTSN, &channel->regs->w.cr); + } + + /* + * Send a packet and then disable TX to avoid failure after several send + * operations + */ + iowrite8(CR_ENABLE_TX, &channel->regs->w.cr); + return char_copied; +} + +static unsigned int ipoctal_write_room(struct tty_struct *tty) +{ + struct ipoctal_channel *channel = tty->driver_data; + + return PAGE_SIZE - channel->nb_bytes; +} + +static unsigned int ipoctal_chars_in_buffer(struct tty_struct *tty) +{ + struct ipoctal_channel *channel = tty->driver_data; + + return channel->nb_bytes; +} + +static void ipoctal_set_termios(struct tty_struct *tty, + const struct ktermios *old_termios) +{ + unsigned int cflag; + unsigned char mr1 = 0; + unsigned char mr2 = 0; + unsigned char csr = 0; + struct ipoctal_channel *channel = tty->driver_data; + speed_t baud; + + cflag = tty->termios.c_cflag; + + /* Disable and reset everything before change the setup */ + ipoctal_reset_channel(channel); + + /* Set Bits per chars */ + switch (cflag & CSIZE) { + case CS6: + mr1 |= MR1_CHRL_6_BITS; + break; + case CS7: + mr1 |= MR1_CHRL_7_BITS; + break; + case CS8: + default: + mr1 |= MR1_CHRL_8_BITS; + /* By default, select CS8 */ + tty->termios.c_cflag = (cflag & ~CSIZE) | CS8; + break; + } + + /* Set Parity */ + if (cflag & PARENB) + if (cflag & PARODD) + mr1 |= MR1_PARITY_ON | MR1_PARITY_ODD; + else + mr1 |= MR1_PARITY_ON | MR1_PARITY_EVEN; + else + mr1 |= MR1_PARITY_OFF; + + /* Mark or space parity is not supported */ + tty->termios.c_cflag &= ~CMSPAR; + + /* Set stop bits */ + if (cflag & CSTOPB) + mr2 |= MR2_STOP_BITS_LENGTH_2; + else + mr2 |= MR2_STOP_BITS_LENGTH_1; + + /* Set the flow control */ + switch (channel->board_id) { + case IPACK1_DEVICE_ID_SBS_OCTAL_232: + if (cflag & CRTSCTS) { + mr1 |= MR1_RxRTS_CONTROL_ON; + mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_ON; + } else { + mr1 |= MR1_RxRTS_CONTROL_OFF; + mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; + } + break; + case IPACK1_DEVICE_ID_SBS_OCTAL_422: + mr1 |= MR1_RxRTS_CONTROL_OFF; + mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; + break; + case IPACK1_DEVICE_ID_SBS_OCTAL_485: + mr1 |= MR1_RxRTS_CONTROL_OFF; + mr2 |= MR2_TxRTS_CONTROL_ON | MR2_CTS_ENABLE_TX_OFF; + break; + default: + return; + } + + baud = tty_get_baud_rate(tty); + tty_termios_encode_baud_rate(&tty->termios, baud, baud); + + /* Set baud rate */ + switch (baud) { + case 75: + csr |= TX_CLK_75 | RX_CLK_75; + break; + case 110: + csr |= TX_CLK_110 | RX_CLK_110; + break; + case 150: + csr |= TX_CLK_150 | RX_CLK_150; + break; + case 300: + csr |= TX_CLK_300 | RX_CLK_300; + break; + case 600: + csr |= TX_CLK_600 | RX_CLK_600; + break; + case 1200: + csr |= TX_CLK_1200 | RX_CLK_1200; + break; + case 1800: + csr |= TX_CLK_1800 | RX_CLK_1800; + break; + case 2000: + csr |= TX_CLK_2000 | RX_CLK_2000; + break; + case 2400: + csr |= TX_CLK_2400 | RX_CLK_2400; + break; + case 4800: + csr |= TX_CLK_4800 | RX_CLK_4800; + break; + case 9600: + csr |= TX_CLK_9600 | RX_CLK_9600; + break; + case 19200: + csr |= TX_CLK_19200 | RX_CLK_19200; + break; + case 38400: + default: + csr |= TX_CLK_38400 | RX_CLK_38400; + /* In case of default, we establish 38400 bps */ + tty_termios_encode_baud_rate(&tty->termios, 38400, 38400); + break; + } + + mr1 |= MR1_ERROR_CHAR; + mr1 |= MR1_RxINT_RxRDY; + + /* Write the control registers */ + iowrite8(mr1, &channel->regs->w.mr); + iowrite8(mr2, &channel->regs->w.mr); + iowrite8(csr, &channel->regs->w.csr); + + /* Enable again the RX, if it was before */ + if (channel->rx_enable) + iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); +} + +static void ipoctal_hangup(struct tty_struct *tty) +{ + unsigned long flags; + struct ipoctal_channel *channel = tty->driver_data; + + if (channel == NULL) + return; + + spin_lock_irqsave(&channel->lock, flags); + channel->nb_bytes = 0; + channel->pointer_read = 0; + channel->pointer_write = 0; + spin_unlock_irqrestore(&channel->lock, flags); + + tty_port_hangup(&channel->tty_port); + + ipoctal_reset_channel(channel); + tty_port_set_initialized(&channel->tty_port, 0); + wake_up_interruptible(&channel->tty_port.open_wait); +} + +static void ipoctal_shutdown(struct tty_struct *tty) +{ + struct ipoctal_channel *channel = tty->driver_data; + + if (channel == NULL) + return; + + ipoctal_reset_channel(channel); + tty_port_set_initialized(&channel->tty_port, 0); +} + +static void ipoctal_cleanup(struct tty_struct *tty) +{ + struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); + + /* release the carrier driver */ + ipack_put_carrier(ipoctal->dev); +} + +static const struct tty_operations ipoctal_fops = { + .ioctl = NULL, + .install = ipoctal_install, + .open = ipoctal_open, + .close = ipoctal_close, + .write = ipoctal_write_tty, + .set_termios = ipoctal_set_termios, + .write_room = ipoctal_write_room, + .chars_in_buffer = ipoctal_chars_in_buffer, + .get_icount = ipoctal_get_icount, + .hangup = ipoctal_hangup, + .shutdown = ipoctal_shutdown, + .cleanup = ipoctal_cleanup, +}; + +static int ipoctal_probe(struct ipack_device *dev) +{ + int res; + struct ipoctal *ipoctal; + + ipoctal = kzalloc(sizeof(struct ipoctal), GFP_KERNEL); + if (ipoctal == NULL) + return -ENOMEM; + + ipoctal->dev = dev; + res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot); + if (res) + goto out_uninst; + + dev_set_drvdata(&dev->dev, ipoctal); + return 0; + +out_uninst: + kfree(ipoctal); + return res; +} + +static void __ipoctal_remove(struct ipoctal *ipoctal) +{ + int i; + + ipoctal->dev->bus->ops->free_irq(ipoctal->dev); + + for (i = 0; i < NR_CHANNELS; i++) { + struct ipoctal_channel *channel = &ipoctal->channel[i]; + + if (!channel->tty_registered) + continue; + + tty_unregister_device(ipoctal->tty_drv, i); + tty_port_free_xmit_buf(&channel->tty_port); + tty_port_destroy(&channel->tty_port); + } + + tty_unregister_driver(ipoctal->tty_drv); + kfree(ipoctal->tty_drv->name); + tty_driver_kref_put(ipoctal->tty_drv); + kfree(ipoctal); +} + +static void ipoctal_remove(struct ipack_device *idev) +{ + __ipoctal_remove(dev_get_drvdata(&idev->dev)); +} + +static DEFINE_IPACK_DEVICE_TABLE(ipoctal_ids) = { + { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, + IPACK1_DEVICE_ID_SBS_OCTAL_232) }, + { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, + IPACK1_DEVICE_ID_SBS_OCTAL_422) }, + { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, + IPACK1_DEVICE_ID_SBS_OCTAL_485) }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(ipack, ipoctal_ids); + +static const struct ipack_driver_ops ipoctal_drv_ops = { + .probe = ipoctal_probe, + .remove = ipoctal_remove, +}; + +static struct ipack_driver driver = { + .ops = &ipoctal_drv_ops, + .id_table = ipoctal_ids, +}; + +static int __init ipoctal_init(void) +{ + return ipack_driver_register(&driver, THIS_MODULE, KBUILD_MODNAME); +} + +static void __exit ipoctal_exit(void) +{ + ipack_driver_unregister(&driver); +} + +MODULE_DESCRIPTION("IP-Octal 232, 422 and 485 device driver"); +MODULE_LICENSE("GPL"); + +module_init(ipoctal_init); +module_exit(ipoctal_exit); diff --git a/drivers/ipack/devices/ipoctal.h b/drivers/ipack/devices/ipoctal.h new file mode 100644 index 000000000..773dc41bd --- /dev/null +++ b/drivers/ipack/devices/ipoctal.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * driver for the IPOCTAL boards + * + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez + */ + +#ifndef _IPOCTAL_H_ +#define _IPOCTAL_H_ + +#define NR_CHANNELS 8 +#define IPOCTAL_MAX_BOARDS 16 +#define MAX_DEVICES (NR_CHANNELS * IPOCTAL_MAX_BOARDS) + +/** + * struct ipoctal_stats -- Stats since last reset + * + * @tx: Number of transmitted bytes + * @rx: Number of received bytes + * @overrun: Number of overrun errors + * @parity_err: Number of parity errors + * @framing_err: Number of framing errors + * @rcv_break: Number of break received + */ +struct ipoctal_stats { + unsigned long tx; + unsigned long rx; + unsigned long overrun_err; + unsigned long parity_err; + unsigned long framing_err; + unsigned long rcv_break; +}; + +#endif /* _IPOCTAL_H_ */ diff --git a/drivers/ipack/devices/scc2698.h b/drivers/ipack/devices/scc2698.h new file mode 100644 index 000000000..9649576ce --- /dev/null +++ b/drivers/ipack/devices/scc2698.h @@ -0,0 +1,225 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * scc2698.h + * + * driver for the IPOCTAL boards + * + * Copyright (C) 2009-2012 CERN (www.cern.ch) + * Author: Nicolas Serafini, EIC2 SA + * Author: Samuel Iglesias Gonsalvez + */ + +#ifndef SCC2698_H_ +#define SCC2698_H_ + +/* + * union scc2698_channel - Channel access to scc2698 IO + * + * dn value are only spacer. + * + */ +union scc2698_channel { + struct { + u8 d0, mr; /* Mode register 1/2*/ + u8 d1, sr; /* Status register */ + u8 d2, r1; /* reserved */ + u8 d3, rhr; /* Receive holding register (R) */ + u8 junk[8]; /* other crap for block control */ + } __packed r; /* Read access */ + struct { + u8 d0, mr; /* Mode register 1/2 */ + u8 d1, csr; /* Clock select register */ + u8 d2, cr; /* Command register */ + u8 d3, thr; /* Transmit holding register */ + u8 junk[8]; /* other crap for block control */ + } __packed w; /* Write access */ +}; + +/* + * union scc2698_block - Block access to scc2698 IO + * + * The scc2698 contain 4 block. + * Each block containt two channel a and b. + * dn value are only spacer. + * + */ +union scc2698_block { + struct { + u8 d0, mra; /* Mode register 1/2 (a) */ + u8 d1, sra; /* Status register (a) */ + u8 d2, r1; /* reserved */ + u8 d3, rhra; /* Receive holding register (a) */ + u8 d4, ipcr; /* Input port change register of block */ + u8 d5, isr; /* Interrupt status register of block */ + u8 d6, ctur; /* Counter timer upper register of block */ + u8 d7, ctlr; /* Counter timer lower register of block */ + u8 d8, mrb; /* Mode register 1/2 (b) */ + u8 d9, srb; /* Status register (b) */ + u8 da, r2; /* reserved */ + u8 db, rhrb; /* Receive holding register (b) */ + u8 dc, r3; /* reserved */ + u8 dd, ip; /* Input port register of block */ + u8 de, ctg; /* Start counter timer of block */ + u8 df, cts; /* Stop counter timer of block */ + } __packed r; /* Read access */ + struct { + u8 d0, mra; /* Mode register 1/2 (a) */ + u8 d1, csra; /* Clock select register (a) */ + u8 d2, cra; /* Command register (a) */ + u8 d3, thra; /* Transmit holding register (a) */ + u8 d4, acr; /* Auxiliary control register of block */ + u8 d5, imr; /* Interrupt mask register of block */ + u8 d6, ctu; /* Counter timer upper register of block */ + u8 d7, ctl; /* Counter timer lower register of block */ + u8 d8, mrb; /* Mode register 1/2 (b) */ + u8 d9, csrb; /* Clock select register (a) */ + u8 da, crb; /* Command register (b) */ + u8 db, thrb; /* Transmit holding register (b) */ + u8 dc, r1; /* reserved */ + u8 dd, opcr; /* Output port configuration register of block */ + u8 de, r2; /* reserved */ + u8 df, r3; /* reserved */ + } __packed w; /* Write access */ +}; + +#define MR1_CHRL_5_BITS (0x0 << 0) +#define MR1_CHRL_6_BITS (0x1 << 0) +#define MR1_CHRL_7_BITS (0x2 << 0) +#define MR1_CHRL_8_BITS (0x3 << 0) +#define MR1_PARITY_EVEN (0x1 << 2) +#define MR1_PARITY_ODD (0x0 << 2) +#define MR1_PARITY_ON (0x0 << 3) +#define MR1_PARITY_FORCE (0x1 << 3) +#define MR1_PARITY_OFF (0x2 << 3) +#define MR1_PARITY_SPECIAL (0x3 << 3) +#define MR1_ERROR_CHAR (0x0 << 5) +#define MR1_ERROR_BLOCK (0x1 << 5) +#define MR1_RxINT_RxRDY (0x0 << 6) +#define MR1_RxINT_FFULL (0x1 << 6) +#define MR1_RxRTS_CONTROL_ON (0x1 << 7) +#define MR1_RxRTS_CONTROL_OFF (0x0 << 7) + +#define MR2_STOP_BITS_LENGTH_1 (0x7 << 0) +#define MR2_STOP_BITS_LENGTH_2 (0xF << 0) +#define MR2_CTS_ENABLE_TX_ON (0x1 << 4) +#define MR2_CTS_ENABLE_TX_OFF (0x0 << 4) +#define MR2_TxRTS_CONTROL_ON (0x1 << 5) +#define MR2_TxRTS_CONTROL_OFF (0x0 << 5) +#define MR2_CH_MODE_NORMAL (0x0 << 6) +#define MR2_CH_MODE_ECHO (0x1 << 6) +#define MR2_CH_MODE_LOCAL (0x2 << 6) +#define MR2_CH_MODE_REMOTE (0x3 << 6) + +#define CR_ENABLE_RX (0x1 << 0) +#define CR_DISABLE_RX (0x1 << 1) +#define CR_ENABLE_TX (0x1 << 2) +#define CR_DISABLE_TX (0x1 << 3) +#define CR_CMD_RESET_MR (0x1 << 4) +#define CR_CMD_RESET_RX (0x2 << 4) +#define CR_CMD_RESET_TX (0x3 << 4) +#define CR_CMD_RESET_ERR_STATUS (0x4 << 4) +#define CR_CMD_RESET_BREAK_CHANGE (0x5 << 4) +#define CR_CMD_START_BREAK (0x6 << 4) +#define CR_CMD_STOP_BREAK (0x7 << 4) +#define CR_CMD_ASSERT_RTSN (0x8 << 4) +#define CR_CMD_NEGATE_RTSN (0x9 << 4) +#define CR_CMD_SET_TIMEOUT_MODE (0xA << 4) +#define CR_CMD_DISABLE_TIMEOUT_MODE (0xC << 4) + +#define SR_RX_READY (0x1 << 0) +#define SR_FIFO_FULL (0x1 << 1) +#define SR_TX_READY (0x1 << 2) +#define SR_TX_EMPTY (0x1 << 3) +#define SR_OVERRUN_ERROR (0x1 << 4) +#define SR_PARITY_ERROR (0x1 << 5) +#define SR_FRAMING_ERROR (0x1 << 6) +#define SR_RECEIVED_BREAK (0x1 << 7) + +#define SR_ERROR (0xF0) + +#define ACR_DELTA_IP0_IRQ_EN (0x1 << 0) +#define ACR_DELTA_IP1_IRQ_EN (0x1 << 1) +#define ACR_DELTA_IP2_IRQ_EN (0x1 << 2) +#define ACR_DELTA_IP3_IRQ_EN (0x1 << 3) +#define ACR_CT_Mask (0x7 << 4) +#define ACR_CExt (0x0 << 4) +#define ACR_CTxCA (0x1 << 4) +#define ACR_CTxCB (0x2 << 4) +#define ACR_CClk16 (0x3 << 4) +#define ACR_TExt (0x4 << 4) +#define ACR_TExt16 (0x5 << 4) +#define ACR_TClk (0x6 << 4) +#define ACR_TClk16 (0x7 << 4) +#define ACR_BRG_SET1 (0x0 << 7) +#define ACR_BRG_SET2 (0x1 << 7) + +#define TX_CLK_75 (0x0 << 0) +#define TX_CLK_110 (0x1 << 0) +#define TX_CLK_38400 (0x2 << 0) +#define TX_CLK_150 (0x3 << 0) +#define TX_CLK_300 (0x4 << 0) +#define TX_CLK_600 (0x5 << 0) +#define TX_CLK_1200 (0x6 << 0) +#define TX_CLK_2000 (0x7 << 0) +#define TX_CLK_2400 (0x8 << 0) +#define TX_CLK_4800 (0x9 << 0) +#define TX_CLK_1800 (0xA << 0) +#define TX_CLK_9600 (0xB << 0) +#define TX_CLK_19200 (0xC << 0) +#define RX_CLK_75 (0x0 << 4) +#define RX_CLK_110 (0x1 << 4) +#define RX_CLK_38400 (0x2 << 4) +#define RX_CLK_150 (0x3 << 4) +#define RX_CLK_300 (0x4 << 4) +#define RX_CLK_600 (0x5 << 4) +#define RX_CLK_1200 (0x6 << 4) +#define RX_CLK_2000 (0x7 << 4) +#define RX_CLK_2400 (0x8 << 4) +#define RX_CLK_4800 (0x9 << 4) +#define RX_CLK_1800 (0xA << 4) +#define RX_CLK_9600 (0xB << 4) +#define RX_CLK_19200 (0xC << 4) + +#define OPCR_MPOa_RTSN (0x0 << 0) +#define OPCR_MPOa_C_TO (0x1 << 0) +#define OPCR_MPOa_TxC1X (0x2 << 0) +#define OPCR_MPOa_TxC16X (0x3 << 0) +#define OPCR_MPOa_RxC1X (0x4 << 0) +#define OPCR_MPOa_RxC16X (0x5 << 0) +#define OPCR_MPOa_TxRDY (0x6 << 0) +#define OPCR_MPOa_RxRDY_FF (0x7 << 0) + +#define OPCR_MPOb_RTSN (0x0 << 4) +#define OPCR_MPOb_C_TO (0x1 << 4) +#define OPCR_MPOb_TxC1X (0x2 << 4) +#define OPCR_MPOb_TxC16X (0x3 << 4) +#define OPCR_MPOb_RxC1X (0x4 << 4) +#define OPCR_MPOb_RxC16X (0x5 << 4) +#define OPCR_MPOb_TxRDY (0x6 << 4) +#define OPCR_MPOb_RxRDY_FF (0x7 << 4) + +#define OPCR_MPP_INPUT (0x0 << 7) +#define OPCR_MPP_OUTPUT (0x1 << 7) + +#define IMR_TxRDY_A (0x1 << 0) +#define IMR_RxRDY_FFULL_A (0x1 << 1) +#define IMR_DELTA_BREAK_A (0x1 << 2) +#define IMR_COUNTER_READY (0x1 << 3) +#define IMR_TxRDY_B (0x1 << 4) +#define IMR_RxRDY_FFULL_B (0x1 << 5) +#define IMR_DELTA_BREAK_B (0x1 << 6) +#define IMR_INPUT_PORT_CHANGE (0x1 << 7) + +#define ISR_TxRDY_A (0x1 << 0) +#define ISR_RxRDY_FFULL_A (0x1 << 1) +#define ISR_DELTA_BREAK_A (0x1 << 2) +#define ISR_COUNTER_READY (0x1 << 3) +#define ISR_TxRDY_B (0x1 << 4) +#define ISR_RxRDY_FFULL_B (0x1 << 5) +#define ISR_DELTA_BREAK_B (0x1 << 6) +#define ISR_INPUT_PORT_CHANGE (0x1 << 7) + +#define ACK_INT_REQ0 0 +#define ACK_INT_REQ1 2 + +#endif /* SCC2698_H_ */ -- cgit v1.2.3