I am trying to install a driver for UART ports on a custom hardware build using the conga-SA5. I am using the legacy ports and they are controlled by the Board Controller. They provide a Linux driver for them. In the tarball I downloaded from their website are three files:
Readme
=================================================================
congatec BC serial port driver for linux v1.1
=================================================================
------------
INTRODUCTION
------------
Some newer congatec boards contain two onboard module serial ports.
This package provides the congatec BC serial port driver for linux.
Currently kernel versions 3.x are supported. Please contact your
local congatec sales partner if you need support for other kernels.
The driver has been compiled with kernel version 3.2 and 3.13 under
Ubuntu/Xubuntu 12.04 LTS and 14.04 LTS.
------------
INSTALLATION
------------
Please make sure that the *module* serial ports are enabled in the BIOS
setup and configured as CGT0501 and CGT0502.
Extract the driver from the cgbcser.tar.gz archive to a new directory.
tar -xzf cgbcser.tar.gz
The driver builds as a module cgbcser.ko
To compile the driver just run
make
The driver can be installed with
make install
To remove the driver run
make uninstall
Makefile
obj-m += cgbcser.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
install:
install -m 644 -o root -g root cgbcser.ko /lib/modules/`uname -r`/kernel/drivers/tty/serial
depmod -a
mod:
modprobe cgbcser
uninstall:
rmmod cgbcser
rm /lib/modules/`uname -r`/kernel/drivers/tty/serial/cgbcser.ko
depmod -a
mship:
tar -czf cgbcser.tar.gz cgbcser.c Makefile readme
cgbser.c
/*
* Serial port driver for congatec board controller
* Copyright (C) 2014 <info@congatec.com>, congatec
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
* V1.1
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/pnp.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/serial_8250.h>
#define dbpf(pars)
/* #define dbpf(pars) printk pars */
static const struct pnp_device_id pnp_dev_table[] = {
/* congatec BC serial Plug & Play */
{ "CGT0501", 0 },
{ "CGT0502", 1 },
{ "", 0 }
};
MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
/*
* Board Controller Serial Port 0
*/
static unsigned int io_serial_in(struct uart_port *p, int offset)
{
unsigned int value;
value = inb(p->iobase + offset);
if (offset == UART_IIR) {
unsigned int ier = inb(p->iobase + UART_IER);
if ((ier & 2) && (value & 6) == 2)
outb(ier & ~2, p->iobase + UART_IER);
value = (value & 6) ? value & ~1 : value | 1;
}
dbpf((KERN_DEBUG "cgbcser: IN(%d)->0x%02x\n", offset, value));
return value;
}
static void io_serial_out(struct uart_port *p, int offset, int value)
{
dbpf((KERN_DEBUG "cgbcser: OUT(%d,0x%02x)\n", offset, value));
switch (offset) {
case UART_LCR:
if ((value & UART_LCR_DLAB)) {
outb(value, p->iobase + offset);
udelay(10);
return;
} else udelay(2);
break;
case UART_DLM:
if ((inb(p->iobase + UART_LCR) & UART_LCR_DLAB))
return;
break;
case UART_TX: {
unsigned int ier = inb(p->iobase + UART_IER);
if ((ier & 0x0f)) {
outb(value, p->iobase + offset);
outb(ier | 2, p->iobase + UART_IER);
return;
}
break;
}
}
outb(value, p->iobase + offset);
}
/*
* Board Controller Serial Port 1
*/
#define BC_COM1_CMD 0
#define BC_COM1_DAT 1
#define BC_COM1_STR 3
static unsigned int io_serial_in1(struct uart_port *p, int offset)
{
unsigned int value;
while (inb(p->iobase + BC_COM1_STR));
outb(offset, p->iobase + BC_COM1_CMD);
outb(1, p->iobase + BC_COM1_STR);
while (inb(p->iobase + BC_COM1_STR));
value = inb(p->iobase + BC_COM1_DAT);
if (offset == UART_IIR) value |= 0xC0; // kernel tests to see if FIFOs are enabled to see if 16550A is supported
if (offset == UART_IER) value &= ~(UART_IER_UUE); // turn off UUE to ensure not detected as Xscale instead of 16550A
dbpf((KERN_DEBUG "cgbcser: IN(%d)->0x%02x)\n", offset, value));
return value;
}
static void io_serial_out1(struct uart_port *p, int offset, int value)
{
dbpf((KERN_DEBUG "cgbcser: OUT(%d,0x%02x)\n", offset, value));
while (inb(p->iobase + BC_COM1_STR));
outb(8 | offset, p->iobase + BC_COM1_CMD);
outb(value, p->iobase + BC_COM1_DAT);
outb(1, p->iobase + BC_COM1_STR);
}
/*
* Driver interface
*/
static int
cgbcser_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
struct uart_port uart;
struct uart_port *port=&uart;
#define serial8250_register_8250_port(p) serial8250_register_port(p)
#else
struct uart_8250_port uart;
struct uart_port *port=&uart.port;
#endif
int line, flags = dev_id->driver_data;
memset(&uart, 0, sizeof(uart));
if (pnp_irq_valid(dev, 0))
port->irq = pnp_irq(dev, 0);
if (pnp_port_valid(dev, 0)) {
port->iobase = pnp_port_start(dev, 0);
port->iotype = UPIO_PORT;
} else
return -ENODEV;
dbpf((KERN_DEBUG
"Setup cgbcser port: port 0x%lx, irq %d, type %d\n",
port->iobase, port->irq, port->iotype));
port->flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_BUGGY_UART;
if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
port->flags |= UPF_SHARE_IRQ;
port->uartclk = 1843200;
/*port->fifosize = 16*/;
port->dev = &dev->dev;
port->serial_in = (flags) ? io_serial_in1 : io_serial_in;
port->serial_out = (flags) ? io_serial_out1 : io_serial_out;
line = serial8250_register_8250_port(&uart);
if (line < 0)
return -ENODEV;
pnp_set_drvdata(dev, (void *)((long)line + 1));
return 0;
}
static void cgbcser_remove(struct pnp_dev *dev)
{
long line = (long)pnp_get_drvdata(dev);
if (line)
serial8250_unregister_port(line - 1);
}
#ifdef CONFIG_PM
static int cgbcser_suspend(struct pnp_dev *dev, pm_message_t state)
{
long line = (long)pnp_get_drvdata(dev);
if (!line)
return -ENODEV;
serial8250_suspend_port(line - 1);
return 0;
}
static int cgbcser_resume(struct pnp_dev *dev)
{
long line = (long)pnp_get_drvdata(dev);
if (!line)
return -ENODEV;
serial8250_resume_port(line - 1);
return 0;
}
#else
#define cgbcser_suspend NULL
#define cgbcser_resume NULL
#endif /* CONFIG_PM */
static struct pnp_driver cgbcser_driver = {
.name = "cgbcser",
.probe = cgbcser_probe,
.remove = cgbcser_remove,
.suspend = cgbcser_suspend,
.resume = cgbcser_resume,
.id_table = pnp_dev_table,
};
static int __init cgbcser_init(void)
{
dbpf((KERN_DEBUG "cgbcser: init\n"));
return pnp_register_driver(&cgbcser_driver);
}
static void __exit cgbcser_exit(void)
{
dbpf((KERN_DEBUG "cgbcser: exit\n"));
pnp_unregister_driver(&cgbcser_driver);
}
module_init(cgbcser_init);
module_exit(cgbcser_exit);
MODULE_AUTHOR("congatec <info@congatec.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("congatec BC serial driver");
I tried following the Readme, but I'm getting a make
error. I'm guessing it's because I don't have the kernel headers. Ideally, I would compile this on the router, but I don't mind cross compiling on Ubuntu first then copying the modules onto the router, so long as I can find the correct version of the linux-headers
package on Ubuntu for 21.02.0.