I finished an initial testing and construction of the ts_ser2 module. We are
not planning on using the serial ports on our TS-SER2, but I took the hours
required and worked them out all the same yesterday. I turns out that the
jumpers on the TS-SER2 have many more possibilities than on the TS-SER1. These
have now been setup with an auto-port detection using Matthieu's MARKER_BYTE
which essentially validates that the entries in /etc/modprobe.d/ts_ser2.conf
match what is found and dump some printk()s if not. But this is only my fifth
module, and there is much I am nubile on yet. Please chime in with any hints
(or swats).
SERIAL PORTS COMA & COMB
- Not clear on how the syntax works for IRQs <6.
Matthieu Crapets has this:
#if CONFIG_SERIAL_8250_TS_SER1_IRQ == 5
gpio_direction_input(EP93XX_GPIO_LINE_F(3));
comX->irq = gpio_to_irq(EP93XX_GPIO_LINE_F(3)); // 83
set_irq_type(comX->irq, IRQ_TYPE_EDGE_RISING);
#elif CONFIG_SERIAL_8250_TS_SER1_IRQ == 6
comX->irq = IRQ_EP93XX_EXT1;
#elif CONFIG_SERIAL_8250_TS_SER1_IRQ == 7
comX->irq = IRQ_EP93XX_EXT3;
#else
comX->irq = IRQ_EP93XX_EXT3;
#endif
- I cannot transcribe with those provided by Kevin Cozens (yet):
#define GPIOFIntType1 (gpio_ptr + 0x4C)
#define GPIOFIntType2 (gpio_ptr + 0x50)
writeb (readb (GPIOFIntType1) | FLOW, GPIOFIntType1); /* Int on edge */
writeb (readb (GPIOFIntType2) & ~FLOW, GPIOFIntType2); /* Rising edge */
If you inspect the source below, you will see my stubs. Where do I find a nice
map or INTEL_IRQ <=> EP9302_IRQ BTW?
PARALLEL PORT LPT
The architecture for the parallel port setup turns out to be a bit more
complex. There are two choices:
1. Patch parport_pc with our stuff, keeping the TS-SER2 stuff completely
separate.
2. Setup irq etc., in ts_ser2, and load it as a dependency for parport_pc.
That decision is tricky. Better encapsulation is 1., but as far as I can see,
(and I may have to re-visit) it seems that the TS-SER2 is completely
non-responsive if both JP14 & JP15 are pulled. Therefore the two cannot be
completely be separated. Any ideas? TS?
Thanks & ENJOY!
- Clark
CODE ===========================================
/*
* linux/drivers/serial/ts_ser2.c
* Support for Technologic Systems TS-SER2 board on ARM, TS-7260, etc.
*
* (c) Copyright 2011 Clark Dunson <>
*
* with big thanks to Matthieu Crapets & the TS-7000 Yahoo Group
*
* Data taken from include/asm-i386/serial.h
*
* 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, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Pin Number:
* 1 DCD
* 2 Receive data
* 3 Trasmit data
* 4 DTR
* 5 Signal Ground
* 6 DSR
* 7 RTS
* 8 CTS
* 9 RI
*
* A sampled, fully configured /etc/modprobe.d/ts_ser2.conf file (with default
values):
*
options comA_base=0x2E8 comA_irq=5 comB_base=0x3E8 comB_irq=6 createParportDev=0
*
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/serial_8250.h>
#include <linux/irq.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
const char tsSer2DriverName[] = "ts_ser2";
#define DEBUG_ASSUMPTIONS
//#define BLESSED_SILENCE
#define TS72XX_SER2_IO_PHYS_BASE (TS72XX_PC104_8BIT_IO_PHYS_BASE)
#define TS72XX_SER2_IO_SIZE (TS72XX_PC104_8BIT_IO_SIZE)
#define NUM_TS_SER2_PORTS 2
enum {
comAPort,
comBPort,
numTS_Ser2_Ports
};
#define TS_SER2_PORT_COM_A_SEL1 0x2E8
#define TS_SER2_PORT_COM_A_SEL2 0x2F8
#define TS_SER2_PORT_COM_A_SEL3 0x260
#define TS_SER2_PORT_COM_A_SEL4 0x250
#define TS_SER2_PORT_COM_A_SEL5 0x220
#define TS_SER2_PORT_COM_A_SEL6 0x210
enum {
comAbase1,
comAbase2,
comAbase3,
comAbase4,
comAbase5,
comAbase6,
numComABases
};
#define TS_SER2_PORT_COM_B_SEL1 0x3E8
#define TS_SER2_PORT_COM_B_SEL2 0x3F8
#define TS_SER2_PORT_COM_B_SEL3 0x268
#define TS_SER2_PORT_COM_B_SEL4 0x258
#define TS_SER2_PORT_COM_B_SEL5 0x228
#define TS_SER2_PORT_COM_B_SEL6 0x218
enum {
comBbase1,
comBbase2,
comBbase3,
comBbase4,
comBbase5,
comBbase6,
numComBBases
};
#define TS_SER2_PORT_COM_A_IRQ_SEL1 3
#define TS_SER2_PORT_COM_A_IRQ_SEL2 5
#define TS_SER2_PORT_COM_A_IRQ_SEL3 7
#define TS_SER2_PORT_COM_B_IRQ_SEL1 4
#define TS_SER2_PORT_COM_B_IRQ_SEL2 6
#define TS_SER2_PORT_COM_B_IRQ_SEL3 9
/* Value to write in 16550A scratch register */
#define MARKER_BYTE 0xAA /* or 0x55 */
#define PORT(_base,_irq) \
{ \
.iobase = _base, \
.membase = (void __iomem *)0, \
.irq = _irq, \
.uartclk = 1843200, \
.iotype = UPIO_PORT, \
.flags = UPF_BOOT_AUTOCONF, \
}
// Note: IRQ can be shared (see CONFIG_SERIAL_8250_SHARE_IRQ)
void
setPortStruct( struct plat_serial8250_port *portS, int baseAddr, int irqSel )
{
/* Assumes initialization by above macro */
if( portS ) {
portS->iobase = baseAddr;
portS->irq = irqSel;
}
}
// Follow Matthieu here, and intialize these to defaults. Set later with parsed
args results.
static struct plat_serial8250_port ts72xx_ser2_data_comA[] = {
PORT(TS_SER2_PORT_COM_A_SEL1, 0),
{ },
};
static struct plat_serial8250_port ts72xx_ser2_data_comB[] = {
PORT(TS_SER2_PORT_COM_B_SEL1, 0),
{ },
};
static struct platform_device ts72xx_ser2_COMA_device = {
.name = "ts_ser2_com_A",
.id = 0,
.dev = {
.platform_data = ts72xx_ser2_data_comA,
},
};
static struct platform_device ts72xx_ser2_COMB_device = {
.name = "ts_ser2_com_B",
.id = 0,
.dev = {
.platform_data = ts72xx_ser2_data_comB,
},
};
static void __iomem *iomem;
/* Comment from parport_pc.c:
* "Piles of crap below pretend to be a parser for module and kernel
* parameters. Say "thank you" to whoever had come up with that
* syntax and keep in mind that code below is a cleaned up version."
*/
// Use for both serial & parallel vals, use enum vals to index.
static int __initdata ioval[NUM_TS_SER2_PORTS] = {0, 0};
static int __initdata irqval[NUM_TS_SER2_PORTS] = {0, 0};
static int __initdata createParportDev = 0;
#ifdef MODULE
static int comA_base;
static int comB_base;
static int comA_irq;
static int comB_irq;
static int createParportDevice;
MODULE_PARM_DESC(comA_base, "Base I/O address for COM_A");
module_param(comA_base, int, S_IRUGO);
MODULE_PARM_DESC(comB_base, "Base I/O address for COM_B");
module_param(comB_base, int, S_IRUGO);
MODULE_PARM_DESC(comA_irq, "COM_A IRQ selection");
module_param(comA_irq, int, S_IRUGO);
MODULE_PARM_DESC(comB_irq, "COM_B IRQ selection");
module_param(comB_irq, int, S_IRUGO);
/* No need to parse */
MODULE_PARM_DESC(createParportDevice, "Create parallel port device node");
module_param(createParportDevice, int, S_IRUGO);
static int
__init parse_ts_ser2_params(void)
{
/* The rules. Each port and IRQ must be valid values if supplied,
or a warning is issued, and the configuration is set to defaults. */
int parseSuccess = 0, parseFail = 0;
/* COM A */
switch( comA_base ) {
case TS_SER2_PORT_COM_A_SEL1 :
case TS_SER2_PORT_COM_A_SEL2 :
case TS_SER2_PORT_COM_A_SEL3 :
case TS_SER2_PORT_COM_A_SEL4 :
case TS_SER2_PORT_COM_A_SEL5 :
case TS_SER2_PORT_COM_A_SEL6 :
parseSuccess++;
break;
default :
if( comA_base ) /* Zeros means no file */
parseFail++;
}
switch( comA_irq ) {
case TS_SER2_PORT_COM_A_IRQ_SEL1 :
case TS_SER2_PORT_COM_A_IRQ_SEL2 :
case TS_SER2_PORT_COM_A_IRQ_SEL3 :
parseSuccess++;
break;
default :
if( comA_irq )
parseFail++;
}
if( parseSuccess == 2 ) {
ioval[comAPort] = comA_base;
irqval[comAPort] = comA_irq;
} else { /* defaults */
ioval[comAPort] = TS_SER2_PORT_COM_A_SEL1;
irqval[comAPort] = TS_SER2_PORT_COM_A_IRQ_SEL2;
#ifndef BLESSED_SILENCE
if( comA_base ) /* Zeros means no file */
printk (KERN_WARNING "%s: Your supplied baseAddr %x, and irq %d for
COM_A were not accepted!! Defaulting to: baseAddr %x, and irq %d\n",
tsSer2DriverName, comA_base, comA_irq, ioval[comAPort], irqval[comAPort] );
#endif // BLESSED_SILENCE
}
/* COM B */
parseSuccess = 0;
switch( comB_base ) {
case TS_SER2_PORT_COM_B_SEL1 :
case TS_SER2_PORT_COM_B_SEL2 :
case TS_SER2_PORT_COM_B_SEL3 :
case TS_SER2_PORT_COM_B_SEL4 :
case TS_SER2_PORT_COM_B_SEL5 :
case TS_SER2_PORT_COM_B_SEL6 :
parseSuccess++;
break;
default :
if( comB_base ) /* Zeros means no file */
parseFail++;
}
switch( comB_irq ) {
case TS_SER2_PORT_COM_B_IRQ_SEL1 :
case TS_SER2_PORT_COM_B_IRQ_SEL2 :
case TS_SER2_PORT_COM_B_IRQ_SEL3 :
parseSuccess++;
break;
default :
if( comB_irq )
parseFail++;
}
if( parseSuccess == 2 ) {
ioval[comBPort] = comB_base;
irqval[comBPort] = comB_irq;
} else {
ioval[comBPort] = TS_SER2_PORT_COM_B_SEL1;
irqval[comBPort] = TS_SER2_PORT_COM_B_IRQ_SEL2;
#ifndef BLESSED_SILENCE
if( comB_base ) /* Zeros means no file */
printk (KERN_WARNING "%s: Your supplied baseAddr %x, and irq %d for
COM_B were not accepted!! Defaulting to: baseAddr %x, and irq %d\n",
tsSer2DriverName, comB_base, comB_irq, ioval[comBPort], irqval[comBPort] );
#endif // BLESSED_SILENCE
}
createParportDev = createParportDevice;
#ifndef BLESSED_SILENCE
printk (KERN_INFO "%s: You specified baseAddr %x, and irq %d for COM_A\n",
tsSer2DriverName, ioval[comAPort], irqval[comAPort] );
printk (KERN_INFO "%s: You specified baseAddr %x, and irq %d for COM_B\n",
tsSer2DriverName, ioval[comBPort], irqval[comBPort] );
//printk (KERN_INFO "%s: You supplied baseAddr %x, and irq %d for COM_A\n",
tsSer2DriverName, ioval[comAPort], irqval[comAPort] );
#endif // BLESSED_SILENCE
printk (KERN_INFO "%s: parseFail %d\n", tsSer2DriverName, parseFail );
return( parseFail >= 4 );
}
#else // !MODULE
#error NYET!
#endif // MODULE
/* "Parser" ends here */
static void
init_ts_ser2_params(void)
{
if( ioval[comAPort] != -1 )
setPortStruct( &(ts72xx_ser2_data_comA[0]), ioval[comAPort],
irqval[comAPort] );
if( ioval[comBPort] != -1 )
setPortStruct( &(ts72xx_ser2_data_comB[0]), ioval[comBPort],
irqval[comBPort] );
}
#define NOT_FOUND -1
#define BLABBER_ATTEMPTS
static int
auto_detect_port( long unsigned int *bases, int numBases )
{
if( iomem != NULL )
{
int n = 0;
/* Scan serial port addresses */
for( n = 0; n < numBases; n++ ) {
#ifdef BLABBER_ATTEMPTS
printk(KERN_WARNING "%s: Probing Port %lx, at remapped address
%p.\n", tsSer2DriverName, bases[n], iomem + bases[n] );
#endif // BLABBER_ATTEMPTS
__raw_writeb(MARKER_BYTE, iomem + bases[n] + 7);
if (__raw_readb(iomem + bases[n] + 7) == MARKER_BYTE ) {
#ifndef BLESSED_SILENCE
printk(KERN_WARNING "%s: Found Port at %lx, (index = %d)!\n",
tsSer2DriverName, bases[n], n );
#endif // BLABBER_ATTEMPTS
return n;
}
}
}
return NOT_FOUND;
}
static int
__init ts_ser2_init(void)
{
static struct plat_serial8250_port *comA = &(ts72xx_ser2_data_comA[0]);
static struct plat_serial8250_port *comB = &(ts72xx_ser2_data_comB[0]);
int comAStat = -1, comBStat = -1;
#ifdef DEBUG_ASSUMPTIONS
if( numComABases != numComBBases ) {
printk (KERN_WARNING "%s: The number of possible base addresses should
match for COM_A & COM_B !!\n", tsSer2DriverName );
return -ENOMSG;
}
#endif // DEBUG_ASSUMPTIONS
/* Read in values from /etc/modprobe.d/ts_ser2.conf */
if( parse_ts_ser2_params() ) {
#ifndef BLESSED_SILENCE
printk (KERN_WARNING "%s: None of your supplied addr or irq parameters
were accepted!!\n", tsSer2DriverName );
#endif // BLESSED_SILENCE
return -EINVAL;
}
/* Set port structures trustingly */
init_ts_ser2_params();
/* Remap port memory to access it */
iomem = ioremap(TS72XX_SER2_IO_PHYS_BASE, TS72XX_SER2_IO_SIZE);
if( !iomem ) {
#ifndef BLESSED_SILENCE
printk (KERN_WARNING "%s: The attempt to remap the 8-bit failed!!\n",
tsSer2DriverName );
#endif // BLESSED_SILENCE
return -ENOMSG;
}
/* Now do some mercy hunting before registration */
{
int comNum;
long unsigned int addrs[numComABases]; /* numComABases == numComBBases
*/
/* COM A */
addrs[comAbase1] = TS_SER2_PORT_COM_A_SEL1;
addrs[comAbase2] = TS_SER2_PORT_COM_A_SEL2;
addrs[comAbase3] = TS_SER2_PORT_COM_A_SEL3;
addrs[comAbase4] = TS_SER2_PORT_COM_A_SEL4;
addrs[comAbase5] = TS_SER2_PORT_COM_A_SEL5;
addrs[comAbase6] = TS_SER2_PORT_COM_A_SEL6;
if( (comNum = auto_detect_port( addrs, numComABases )) == -1 ) {
#ifndef BLESSED_SILENCE
printk (KERN_WARNING "%s: The attempt to auto-detect your jumper
settings for COM_A failed!!\n", tsSer2DriverName );
#endif // BLESSED_SILENCE
} else {
#ifndef BLESSED_SILENCE
if( addrs[comNum] != comA->iobase )
printk (KERN_WARNING "%s The auto-detected port (%lx) for COM_A
differs from the one you specified in ts_ser2.conf (%lx)!!\n",
tsSer2DriverName, addrs[comNum], comA->iobase );
#endif // BLESSED_SILENCE
#if 0
/* From Kevin Cozens: "I don't know the 7260 but I have used
interrupts in a 7250 board. Based on
your description it sounds like you have the interrupt on the board
set to
be level sensitive where you really want it set to generate
interrupts on a
signal edge." */
// On a TS-7250 the key two lines for setting up the interrupt was:
#define GPIOFIntType1 (gpio_ptr + 0x4C)
#define GPIOFIntType2 (gpio_ptr + 0x50)
writeb (readb (GPIOFIntType1) | FLOW, GPIOFIntType1); /* Int on
edge */
writeb (readb (GPIOFIntType2) & ~FLOW, GPIOFIntType2); /* Rising
edge */
// gpio_ptr points to the base address of the GPIO registers.
#endif
gpio_direction_input(EP93XX_GPIO_LINE_F(3));
comA->iobase += (unsigned long)iomem; // virtual address
switch( irqval[comAPort] ) {
case 3 :
break;
case 7 :
comA->irq = IRQ_EP93XX_EXT3;
break;
case 5 :
default :
comA->irq = gpio_to_irq(EP93XX_GPIO_LINE_F(3)); // 83
}
set_irq_type(comA->irq, IRQ_TYPE_EDGE_RISING);
ts72xx_ser2_COMA_device.id = comNum;
ts72xx_ser2_COMA_device.dev.platform_data = comA;
comAStat = platform_device_register(&ts72xx_ser2_COMA_device);
#ifndef BLESSED_SILENCE
printk (KERN_INFO "%s: Serial COM_A registered at addr %x/%lx,
irq=%u/%d\n", tsSer2DriverName, ioval[comAPort], comA->iobase,
irqval[comAPort], comA->irq );
#endif // BLESSED_SILENCE
}
/* COM B */
addrs[comBbase1] = TS_SER2_PORT_COM_B_SEL1;
addrs[comBbase2] = TS_SER2_PORT_COM_B_SEL2;
addrs[comBbase3] = TS_SER2_PORT_COM_B_SEL3;
addrs[comBbase4] = TS_SER2_PORT_COM_B_SEL4;
addrs[comBbase5] = TS_SER2_PORT_COM_B_SEL5;
addrs[comBbase6] = TS_SER2_PORT_COM_B_SEL6;
if( (comNum = auto_detect_port( addrs, numComBBases )) == -1 ) {
#ifndef BLESSED_SILENCE
printk (KERN_WARNING "%s: The attempt to auto-detect your jumper
settings for COM_B failed!!\n", tsSer2DriverName );
#endif // BLESSED_SILENCE
} else {
#ifndef BLESSED_SILENCE
if( addrs[comNum] != comB->iobase )
printk (KERN_WARNING "%s The auto-detected port (%lx) for COM_B
differs from the one you specified in ts_ser2.conf (%lx)!!\n",
tsSer2DriverName, addrs[comNum], comA->iobase );
#endif // BLESSED_SILENCE
gpio_direction_input(EP93XX_GPIO_LINE_F(3));
comB->iobase += (unsigned long)iomem; // virtual address
switch( irqval[comBPort] ) {
case 4 :
break;
case 9 :
//comB->irq = IRQ_EP93XX_EXT3;
break;
case 6 :
default :
comB->irq = IRQ_EP93XX_EXT1;
}
set_irq_type(comB->irq, IRQ_TYPE_EDGE_RISING);
ts72xx_ser2_COMB_device.id = comNum;
ts72xx_ser2_COMB_device.dev.platform_data = comB;
comBStat = platform_device_register(&ts72xx_ser2_COMB_device);
#ifndef BLESSED_SILENCE
printk (KERN_INFO "%s: Serial COM_B registered at addr %x/%lx,
irq=%u/%d\n", tsSer2DriverName, ioval[comBPort], comB->iobase,
irqval[comBPort], comB->irq );
#endif // BLESSED_SILENCE
}
}
#if 0
if (comX) {
#if CONFIG_SERIAL_8250_TS_SER1_IRQ == 5
gpio_direction_input(EP93XX_GPIO_LINE_F(3));
comX->irq = gpio_to_irq(EP93XX_GPIO_LINE_F(3)); // 83
set_irq_type(comX->irq, IRQ_TYPE_EDGE_RISING);
#elif CONFIG_SERIAL_8250_TS_SER1_IRQ == 6
comX->irq = IRQ_EP93XX_EXT1;
#elif CONFIG_SERIAL_8250_TS_SER1_IRQ == 7
comX->irq = IRQ_EP93XX_EXT3;
#else
comX->irq = IRQ_EP93XX_EXT3;
#endif
comX->iobase += (unsigned long)iomem; // virtual address
}
}
#endif
return ( (comAStat && comBStat) ? -ENODEV : 0 );
}
static void __exit ts_ser2_exit(void)
{
iounmap(iomem);
platform_device_unregister(&ts72xx_ser2_COMA_device);
platform_device_unregister(&ts72xx_ser2_COMB_device);
}
module_init(ts_ser2_init);
module_exit(ts_ser2_exit);
MODULE_AUTHOR("Clark Dunson <>");
MODULE_DESCRIPTION("Serial probe module for TS-SER2 (TS-72xx)");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
------------------------------------
Yahoo! Groups Links
<*> To visit your group on the web, go to:
http://groups.yahoo.com/group/ts-7000/
<*> Your email settings:
Individual Email | Traditional
<*> To change settings online go to:
http://groups.yahoo.com/group/ts-7000/join
(Yahoo! ID required)
<*> To change settings via email:
<*> To unsubscribe from this group, send an email to:
<*> Your use of Yahoo! Groups is subject to:
http://docs.yahoo.com/info/terms/
|