]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
staging: gpib: Add hp82335x GPIB driver
authorDave Penkler <dpenkler@gmail.com>
Wed, 18 Sep 2024 12:19:02 +0000 (14:19 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 10 Oct 2024 13:28:42 +0000 (15:28 +0200)
Driver for old hp82335x boards.

Signed-off-by: Dave Penkler <dpenkler@gmail.com>
Link: https://lore.kernel.org/r/20240918121908.19366-16-dpenkler@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/gpib/hp_82335/Makefile [new file with mode: 0644]
drivers/staging/gpib/hp_82335/hp82335.c [new file with mode: 0644]
drivers/staging/gpib/hp_82335/hp82335.h [new file with mode: 0644]

diff --git a/drivers/staging/gpib/hp_82335/Makefile b/drivers/staging/gpib/hp_82335/Makefile
new file mode 100644 (file)
index 0000000..8b7a552
--- /dev/null
@@ -0,0 +1,4 @@
+
+obj-m += hp82335.o
+
+
diff --git a/drivers/staging/gpib/hp_82335/hp82335.c b/drivers/staging/gpib/hp_82335/hp82335.c
new file mode 100644 (file)
index 0000000..4e27799
--- /dev/null
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright            : (C) 2002 by Frank Mori Hess                      *
+ ***************************************************************************/
+
+/*should enable ATN interrupts (and update board->status on occurrence),
+ *     implement recovery from bus errors (if necessary)
+ */
+
+#include "hp82335.h"
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+MODULE_LICENSE("GPL");
+
+static int hp82335_attach(gpib_board_t *board, const gpib_board_config_t *config);
+
+static void hp82335_detach(gpib_board_t *board);
+
+// wrappers for interface functions
+int hp82335_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read);
+}
+
+int hp82335_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
+                 size_t *bytes_written)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written);
+}
+
+int hp82335_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written);
+}
+
+int hp82335_take_control(gpib_board_t *board, int synchronous)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_take_control(board, &priv->tms9914_priv, synchronous);
+}
+
+int hp82335_go_to_standby(gpib_board_t *board)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_go_to_standby(board, &priv->tms9914_priv);
+}
+
+void hp82335_request_system_control(gpib_board_t *board, int request_control)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       tms9914_request_system_control(board, &priv->tms9914_priv, request_control);
+}
+
+void hp82335_interface_clear(gpib_board_t *board, int assert)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       tms9914_interface_clear(board, &priv->tms9914_priv, assert);
+}
+
+void hp82335_remote_enable(gpib_board_t *board, int enable)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       tms9914_remote_enable(board, &priv->tms9914_priv, enable);
+}
+
+int hp82335_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits);
+}
+
+void hp82335_disable_eos(gpib_board_t *board)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       tms9914_disable_eos(board, &priv->tms9914_priv);
+}
+
+unsigned int hp82335_update_status(gpib_board_t *board, unsigned int clear_mask)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
+}
+
+int hp82335_primary_address(gpib_board_t *board, unsigned int address)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_primary_address(board, &priv->tms9914_priv, address);
+}
+
+int hp82335_secondary_address(gpib_board_t *board, unsigned int address, int enable)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
+}
+
+int hp82335_parallel_poll(gpib_board_t *board, uint8_t *result)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
+}
+
+void hp82335_parallel_poll_configure(gpib_board_t *board, uint8_t config)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
+}
+
+void hp82335_parallel_poll_response(gpib_board_t *board, int ist)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
+}
+
+void hp82335_serial_poll_response(gpib_board_t *board, uint8_t status)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
+}
+
+static uint8_t hp82335_serial_poll_status(gpib_board_t *board)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_serial_poll_status(board, &priv->tms9914_priv);
+}
+
+static int hp82335_line_status(const gpib_board_t *board)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_line_status(board, &priv->tms9914_priv);
+}
+
+static unsigned int hp82335_t1_delay(gpib_board_t *board, unsigned int nano_sec)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec);
+}
+
+void hp82335_return_to_local(gpib_board_t *board)
+{
+       struct hp82335_priv *priv = board->private_data;
+
+       tms9914_return_to_local(board, &priv->tms9914_priv);
+}
+
+gpib_interface_t hp82335_interface = {
+name: "hp82335",
+attach : hp82335_attach,
+detach : hp82335_detach,
+read : hp82335_read,
+write : hp82335_write,
+command : hp82335_command,
+request_system_control : hp82335_request_system_control,
+take_control : hp82335_take_control,
+go_to_standby : hp82335_go_to_standby,
+interface_clear : hp82335_interface_clear,
+remote_enable : hp82335_remote_enable,
+enable_eos : hp82335_enable_eos,
+disable_eos : hp82335_disable_eos,
+parallel_poll : hp82335_parallel_poll,
+parallel_poll_configure : hp82335_parallel_poll_configure,
+parallel_poll_response : hp82335_parallel_poll_response,
+local_parallel_poll_mode : NULL, // XXX
+line_status : hp82335_line_status,
+update_status : hp82335_update_status,
+primary_address : hp82335_primary_address,
+secondary_address : hp82335_secondary_address,
+serial_poll_response : hp82335_serial_poll_response,
+serial_poll_status : hp82335_serial_poll_status,
+t1_delay : hp82335_t1_delay,
+return_to_local : hp82335_return_to_local,
+};
+
+int hp82335_allocate_private(gpib_board_t *board)
+{
+       board->private_data = kmalloc(sizeof(struct hp82335_priv), GFP_KERNEL);
+       if (!board->private_data)
+               return -1;
+       memset(board->private_data, 0, sizeof(struct hp82335_priv));
+       return 0;
+}
+
+void hp82335_free_private(gpib_board_t *board)
+{
+       kfree(board->private_data);
+       board->private_data = NULL;
+}
+
+static inline unsigned int tms9914_to_hp82335_offset(unsigned int register_num)
+{
+       return 0x1ff8 + register_num;
+}
+
+static uint8_t hp82335_read_byte(struct tms9914_priv *priv, unsigned int register_num)
+{
+       return tms9914_iomem_read_byte(priv, tms9914_to_hp82335_offset(register_num));
+}
+
+static void hp82335_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num)
+{
+       tms9914_iomem_write_byte(priv, data, tms9914_to_hp82335_offset(register_num));
+}
+
+static void hp82335_clear_interrupt(struct hp82335_priv *hp_priv)
+{
+       struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
+
+       writeb(0, tms_priv->iobase + HPREG_INTR_CLEAR);
+}
+
+int hp82335_attach(gpib_board_t *board, const gpib_board_config_t *config)
+{
+       struct hp82335_priv *hp_priv;
+       struct tms9914_priv *tms_priv;
+       int retval;
+       const unsigned long upper_iomem_base = (unsigned long)config->ibbase + hp82335_rom_size;
+
+       board->status = 0;
+
+       if (hp82335_allocate_private(board))
+               return -ENOMEM;
+       hp_priv = board->private_data;
+       tms_priv = &hp_priv->tms9914_priv;
+       tms_priv->read_byte = hp82335_read_byte;
+       tms_priv->write_byte = hp82335_write_byte;
+       tms_priv->offset = 1;
+
+       switch ((unsigned long)(config->ibbase)) {
+       case 0xc4000:
+       case 0xc8000:
+       case 0xcc000:
+       case 0xd0000:
+       case 0xd4000:
+       case 0xd8000:
+       case 0xdc000:
+       case 0xe0000:
+       case 0xe4000:
+       case 0xe8000:
+       case 0xec000:
+       case 0xf0000:
+       case 0xf4000:
+       case 0xf8000:
+       case 0xfc000:
+               break;
+       default:
+               pr_err("hp82335: invalid base io address 0x%p\n", config->ibbase);
+               return -EINVAL;
+       }
+       if (!request_mem_region(upper_iomem_base, hp82335_upper_iomem_size, "hp82335")) {
+               pr_err("hp82335: failed to allocate io memory region 0x%lx-0x%lx\n",
+                      upper_iomem_base, upper_iomem_base + hp82335_upper_iomem_size - 1);
+               return -EBUSY;
+       }
+       hp_priv->raw_iobase = upper_iomem_base;
+       tms_priv->iobase = ioremap(upper_iomem_base, hp82335_upper_iomem_size);
+       pr_info("hp82335: upper half of 82335 iomem region 0x%lx remapped to 0x%p\n",
+               hp_priv->raw_iobase, tms_priv->iobase);
+
+       retval = request_irq(config->ibirq, hp82335_interrupt, 0, "hp82335", board);
+       if (retval) {
+               pr_err("hp82335: can't request IRQ %d\n", config->ibirq);
+               return retval;
+       }
+       hp_priv->irq = config->ibirq;
+       pr_info("hp82335: IRQ %d\n", config->ibirq);
+
+       tms9914_board_reset(tms_priv);
+
+       hp82335_clear_interrupt(hp_priv);
+
+       writeb(INTR_ENABLE, tms_priv->iobase + HPREG_CCR);
+
+       tms9914_online(board, tms_priv);
+
+       return 0;
+}
+
+void hp82335_detach(gpib_board_t *board)
+{
+       struct hp82335_priv *hp_priv = board->private_data;
+       struct tms9914_priv *tms_priv;
+
+       if (hp_priv) {
+               tms_priv = &hp_priv->tms9914_priv;
+               if (hp_priv->irq)
+                       free_irq(hp_priv->irq, board);
+               if (tms_priv->iobase) {
+                       writeb(0, tms_priv->iobase + HPREG_CCR);
+                       tms9914_board_reset(tms_priv);
+                       iounmap((void *)tms_priv->iobase);
+               }
+               if (hp_priv->raw_iobase)
+                       release_mem_region(hp_priv->raw_iobase, hp82335_upper_iomem_size);
+       }
+       hp82335_free_private(board);
+}
+
+static int __init hp82335_init_module(void)
+{
+       gpib_register_driver(&hp82335_interface, THIS_MODULE);
+       return 0;
+}
+
+static void __exit hp82335_exit_module(void)
+{
+       gpib_unregister_driver(&hp82335_interface);
+}
+
+module_init(hp82335_init_module);
+module_exit(hp82335_exit_module);
+
+/*
+ * GPIB interrupt service routines
+ */
+
+irqreturn_t hp82335_interrupt(int irq, void *arg)
+{
+       int status1, status2;
+       gpib_board_t *board = arg;
+       struct hp82335_priv *priv = board->private_data;
+       unsigned long flags;
+       irqreturn_t retval;
+
+       spin_lock_irqsave(&board->spinlock, flags);
+       status1 = read_byte(&priv->tms9914_priv, ISR0);
+       status2 = read_byte(&priv->tms9914_priv, ISR1);
+       hp82335_clear_interrupt(priv);
+       retval = tms9914_interrupt_have_status(board, &priv->tms9914_priv, status1, status2);
+       spin_unlock_irqrestore(&board->spinlock, flags);
+       return retval;
+}
+
diff --git a/drivers/staging/gpib/hp_82335/hp82335.h b/drivers/staging/gpib/hp_82335/hp82335.h
new file mode 100644 (file)
index 0000000..5e5297a
--- /dev/null
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ *    copyright            : (C) 2002 by Frank Mori Hess                   *
+ ***************************************************************************/
+
+#ifndef _HP82335_H
+#define _HP82335_H
+
+#include "tms9914.h"
+#include "gpibP.h"
+
+// struct which defines private_data for board
+struct hp82335_priv  {
+       struct tms9914_priv tms9914_priv;
+       unsigned int irq;
+       unsigned long raw_iobase;
+};
+
+// interfaces
+extern gpib_interface_t hp82335_interface;
+
+// interface functions
+int hp82335_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read);
+int hp82335_write(gpib_board_t *board, uint8_t *buffer, size_t length,
+                 int send_eoi, size_t *bytes_written);
+int hp82335_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
+int hp82335_take_control(gpib_board_t *board, int synchronous);
+int hp82335_go_to_standby(gpib_board_t *board);
+void hp82335_request_system_control(gpib_board_t *board, int request_control);
+void hp82335_interface_clear(gpib_board_t *board, int assert);
+void hp82335_remote_enable(gpib_board_t *board, int enable);
+int hp82335_enable_eos(gpib_board_t *board, uint8_t eos_byte, int
+       compare_8_bits);
+void hp82335_disable_eos(gpib_board_t *board);
+unsigned int hp82335_update_status(gpib_board_t *board, unsigned int clear_mask);
+int hp82335_primary_address(gpib_board_t *board, unsigned int address);
+int hp82335_secondary_address(gpib_board_t *board, unsigned int address, int
+       enable);
+int hp82335_parallel_poll(gpib_board_t *board, uint8_t *result);
+void hp82335_parallel_poll_configure(gpib_board_t *board, uint8_t config);
+void hp82335_parallel_poll_response(gpib_board_t *board, int ist);
+void hp82335_serial_poll_response(gpib_board_t *board, uint8_t status);
+void hp82335_return_to_local(gpib_board_t *board);
+
+// interrupt service routines
+irqreturn_t hp82335_interrupt(int irq, void *arg);
+
+// utility functions
+int hp82335_allocate_private(gpib_board_t *board);
+void hp82335_free_private(gpib_board_t *board);
+
+// size of io memory region used
+static const int hp82335_rom_size = 0x2000;
+static const int hp82335_upper_iomem_size = 0x2000;
+
+// hp82335 register offsets
+enum hp_read_regs {
+       HPREG_CSR = 0x17f8,
+       HPREG_STATUS = 0x1ffc,
+};
+
+enum hp_write_regs {
+       HPREG_INTR_CLEAR = 0x17f7,
+       HPREG_CCR = HPREG_CSR,
+};
+
+enum ccr_bits {
+       DMA_ENABLE = (1 << 0),   /* DMA enable                  */
+       DMA_CHAN_SELECT = (1 << 1),   /* DMA channel select  O=3,1=2 */
+       INTR_ENABLE = (1 << 2),   /* interrupt enable            */
+       SYS_DISABLE = (1 << 3),   /* system controller disable   */
+};
+
+enum csr_bits {
+       SWITCH6 = (1 << 0),   /* switch 6 position           */
+       SWITCH5 = (1 << 1),   /* switch 5 position           */
+       SYS_CONTROLLER = (1 << 2),   /* system controller bit       */
+       DMA_ENABLE_STATUS = (1 << 4),   /* DMA enabled                 */
+       DMA_CHAN_STATUS = (1 << 5),   /* DMA channel   0=3,1=2       */
+       INTR_ENABLE_STATUS = (1 << 6),   /* Interrupt enable            */
+       INTR_PENDING = (1 << 7),   /* Interrupt Pending           */
+};
+
+#endif // _HP82335_H