kernelnewbies.kernelnewbies.org archive mirror
 help / color / mirror / Atom feed
* Simple write to an UART mode register fails
@ 2011-03-02 13:08 Frey ext-FA, Maurus
  2011-03-03 13:12 ` Thomas Petazzoni
  0 siblings, 1 reply; 3+ messages in thread
From: Frey ext-FA, Maurus @ 2011-03-02 13:08 UTC (permalink / raw)
  To: kernelnewbies

Hi,

I wrote a simple module, which should initialise an USART in RS485-mode
(RTS stays low after initialisation) and register it as platform_device.
Its on an Atmel at91sam9260 board, so I can re-use most of the mach
arm-sources (atmel_serial.c etc.)

When the module gets loaded I got the following output

$ modprobe axm_rs485_uart
Original ATMEL_US_MR=0
Write 1 to ATMEL_US_MR
After Write ATMEL_US_MR=0
axm_rs485_uart: registering uart port #3 in RS485 mode as atmel_usart3

- read the actual state from the USART Mode Register
- Put the mode into RS485 mode ("or" a bit)
- write the new mode into the register
- read the register again...and (what a surprise) see, that the write
operation didn't succeed

A simple write to a register seems to fail. In consequence the RTS line
gets high after the Pin has been "muxed" to its RTS-Rolle with
at91_set_B_periph().

I think, that I'm doing something wrong with request_mem() and ioremap()
No clue what? Can you help me with this? 

Thanks for any advice. 

Regards

maurus


#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/atmel_serial.h>
#include <mach/board.h>

#define UART_PORT_WITH_RTS_MAX 4
#define UART_PORT_DEFAULT 3
#define UART_PORT_DEFAULT_DEVICE_NAME "atmel_usart3"

// USART ids / interrupts
static u32 at91sam9260_id_us[UART_PORT_WITH_RTS_MAX] =
{ AT91SAM9260_ID_US0, AT91SAM9260_ID_US1, AT91SAM9260_ID_US2,
    AT91SAM9260_ID_US3 };

#define A_PERIPHERAL 0
#define B_PERIPHERAL 1

struct io_mux_pin
{
  u32 io_line_addr; // I/O Line address
  uint peripheral; // A or B Peripheral
} io_mux_pin;

struct io_mux_pin at91_rts_io_pin[UART_PORT_WITH_RTS_MAX] =
{
{ AT91_PIN_PB26, A_PERIPHERAL }, //RTS0
    { AT91_PIN_PB28, A_PERIPHERAL }, //RTS1
    { AT91_PIN_PA4, A_PERIPHERAL }, //RTS2
    { AT91_PIN_PC8, B_PERIPHERAL } //RTS3
};

//Define some module information.
MODULE_AUTHOR("Maus");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Initialize UART port in RS485 mode. RTS-Line stays
low.") ;

// Stores our pointer to the platform device, needed for release during
exit.
static struct platform_device *pdev;
// Remapped virtual base address
static void *vbaseaddr;

// get port_number as parameter, available in sysfs
// valid value from 0 to UART_PORT_MAX
static uint uart_port = UART_PORT_DEFAULT;
module_param( uart_port, uint, 0400 );
MODULE_PARM_DESC(uart_port, "UART port number to initialize in RS485
mode [0-" __MODULE_STRING(UART_PORT_WITH_RTS_MAX)"]");

// get device_name as parameter, available in sysfs
static char device_name[30] = UART_PORT_DEFAULT_DEVICE_NAME;
module_param_string( device_name, device_name, sizeof(device_name), 0400
);

/*
 * Register UARTX as platform device.
 */
static int __init axm_rs485_uart_init(void)
{
  int retValue = 0;
  unsigned mode;

  //check parameter input
  if (uart_port >= UART_PORT_WITH_RTS_MAX)
  {
    printk(KERN_ERR "%s: uart_port=%u must be below %u\n",
THIS_MODULE->name,
        uart_port, UART_PORT_WITH_RTS_MAX);
    return -EINVAL;
  }

  /*
   * Get platform_device struct from exported at91_register_uart
   *
   * - Don't configure RTS pin.
   * - portnr = uart_port+1 (not necessary due call after boot-up)
   */
  pdev = at91_register_uart(at91sam9260_id_us[uart_port], uart_port + 1,
      ATMEL_UART_CTS);
  if (pdev == NULL)
  {
    printk(KERN_ERR "%s: unable to register uart%u\n",
THIS_MODULE->name,
        uart_port);
    return -EINVAL;
  }
  //set platform_device_name according parameter or default value
  if (!strcmp(pdev->name, device_name))
  {
    pdev->name = device_name;
    //correct the device id
    pdev->id = uart_port;
  }

  //if (!request_mem_region(at91sam9260_base_us[uart_port], SZ_16K ,
THIS_MODULE->name))
    if
(!request_mem_region(pdev->resource[0].start,pdev->resource[0].end-
pdev->resource[0].start , THIS_MODULE->name))
  {
    printk(KERN_ERR "%s: unable to request memory region for uart%u \n",
        THIS_MODULE->name, uart_port);
    goto out_direct;
  }

  vbaseaddr = ioremap(pdev->resource[0].start,pdev->resource[0].end-
pdev->resource[0].start);

  if (!vbaseaddr)
  {
    goto out_reqmem_io_pin;
  }

  mode =readl(vbaseaddr + ATMEL_US_MR);
  printk("Original ATMEL_US_MR=%lX\n", mode);

  mode |= ATMEL_US_USMODE_RS485;
  
  printk("Write %lX to ATMEL_US_MR\n", mode);

  /* put USART to RS485 mode */
  writel(mode, vbaseaddr + ATMEL_US_MR);

  /*check if mode has been written to register*/
  mode = readl(vbaseaddr + ATMEL_US_MR);
  printk("After Write ATMEL_US_MR=%lX\n", mode);

  /* release all resources to be used by other drivers */
  iounmap(vbaseaddr);
  release_mem_region( pdev->resource[0].start, pdev->resource[0].end-
pdev->resource[0].start);



  /* Decide whether pin is A or B peripheral and configure RTS pin to
stay low */
  if (at91_rts_io_pin[uart_port].peripheral == A_PERIPHERAL)
  {
    at91_set_A_periph(at91_rts_io_pin[uart_port].io_line_addr, 0);
  }
  else
  {
    at91_set_B_periph(at91_rts_io_pin[uart_port].io_line_addr, 0);
  }


  retValue = platform_device_register(pdev);
  if (retValue)
  {
    printk(KERN_ERR "%s: unable to register platform device\n",
        THIS_MODULE->name);
    return -EINVAL;
  }

  printk(KERN_NOTICE "%s: registering uart port #%u in RS485 mode as
%s\n",
      THIS_MODULE->name, uart_port, device_name);

  return retValue;

  out_reqmem_io_pin: 
	release_mem_region( pdev->resource[0].start,
pdev->resource[0].end- pdev->resource[0].start);


  out_direct: return -EINVAL;
}

/*
 * Release (unregister device) UART
 */
static void __exit axm_rs485_uart_exit(void)
{
  if (pdev != NULL)
  {
    platform_device_unregister(pdev);
  }

  printk(KERN_NOTICE "%s: releasing USART \n", THIS_MODULE->name);
}
module_init(axm_rs485_uart_init);
module_exit(axm_rs485_uart_exit);

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Simple write to an UART mode register fails
  2011-03-02 13:08 Simple write to an UART mode register fails Frey ext-FA, Maurus
@ 2011-03-03 13:12 ` Thomas Petazzoni
  2011-03-03 17:01   ` AW: " Frey ext-FA, Maurus
  0 siblings, 1 reply; 3+ messages in thread
From: Thomas Petazzoni @ 2011-03-03 13:12 UTC (permalink / raw)
  To: kernelnewbies

Hello Maurus,

Glad to see you're experimenting what you've seen in the kernel
training :-)

On Wed, 2 Mar 2011 14:08:34 +0100
"Frey ext-FA, Maurus" <maurus.frey.ext@siemens.com> wrote:

> I wrote a simple module, which should initialise an USART in RS485-mode
> (RTS stays low after initialisation) and register it as platform_device.
> Its on an Atmel at91sam9260 board, so I can re-use most of the mach
> arm-sources (atmel_serial.c etc.)
> 
> When the module gets loaded I got the following output
> 
> $ modprobe axm_rs485_uart
> Original ATMEL_US_MR=0
> Write 1 to ATMEL_US_MR
> After Write ATMEL_US_MR=0
> axm_rs485_uart: registering uart port #3 in RS485 mode as atmel_usart3
> 
> - read the actual state from the USART Mode Register
> - Put the mode into RS485 mode ("or" a bit)
> - write the new mode into the register
> - read the register again...and (what a surprise) see, that the write
> operation didn't succeed
> 
> A simple write to a register seems to fail. In consequence the RTS line
> gets high after the Pin has been "muxed" to its RTS-Rolle with
> at91_set_B_periph().
> 
> I think, that I'm doing something wrong with request_mem() and ioremap()
> No clue what? Can you help me with this? 

I haven't look into many details, but I don't see why you need a
specific driver to do this. The Atmel serial driver in
drivers/serial/atmel_serial.c already supports RS485.

So from what I can see, all you need to do is to add some details in
the platform_data structure relative to the serial port. So probably
you need to change the uart3_data structure in
arch/arm/mach-at91/at91sam9260_devices.c, by adding a .rs485 field :

static struct atmel_uart_data uart3_data = {
        .use_dma_tx     = 1,
        .use_dma_rx     = 1,
	.rs485		= {
			/* Fill the fields of the serial_rs485 structure
			   defined in include/linux/serial.h */
	},
};

To configure the pins, you have to call at91_register_uart() in the
ek_map_io() function of your board file  (presumably
arch/arm/mach-at91/board-at91sam9261ek.c). The third parameter allows
to tell which pins should be configured. For example, if you pass
ATMEL_UART_RTS, then the code in
arch/arm/mach-at91/at91sam9260_devices.c will do:

        if (pins & ATMEL_UART_RTS)
                at91_set_B_periph(AT91_PIN_PC8, 0);     /* RTS3 */

see the function configure_usart3_pins().

> #include <linux/errno.h>
> #include <linux/init.h>
> #include <linux/module.h>
> #include <linux/platform_device.h>
> #include <linux/gpio.h>
> #include <linux/atmel_serial.h>
> #include <mach/board.h>
> 
> #define UART_PORT_WITH_RTS_MAX 4
> #define UART_PORT_DEFAULT 3
> #define UART_PORT_DEFAULT_DEVICE_NAME "atmel_usart3"
> 
> // USART ids / interrupts
> static u32 at91sam9260_id_us[UART_PORT_WITH_RTS_MAX] =
> { AT91SAM9260_ID_US0, AT91SAM9260_ID_US1, AT91SAM9260_ID_US2,
>     AT91SAM9260_ID_US3 };
> 
> #define A_PERIPHERAL 0
> #define B_PERIPHERAL 1
> 
> struct io_mux_pin
> {
>   u32 io_line_addr; // I/O Line address
>   uint peripheral; // A or B Peripheral
> } io_mux_pin;
> 
> struct io_mux_pin at91_rts_io_pin[UART_PORT_WITH_RTS_MAX] =
> {
> { AT91_PIN_PB26, A_PERIPHERAL }, //RTS0
>     { AT91_PIN_PB28, A_PERIPHERAL }, //RTS1
>     { AT91_PIN_PA4, A_PERIPHERAL }, //RTS2
>     { AT91_PIN_PC8, B_PERIPHERAL } //RTS3
> };
> 
> //Define some module information.
> MODULE_AUTHOR("Maus");
> MODULE_LICENSE("GPL");
> MODULE_DESCRIPTION("Initialize UART port in RS485 mode. RTS-Line stays
> low.") ;
> 
> // Stores our pointer to the platform device, needed for release during
> exit.
> static struct platform_device *pdev;
> // Remapped virtual base address
> static void *vbaseaddr;
> 
> // get port_number as parameter, available in sysfs
> // valid value from 0 to UART_PORT_MAX
> static uint uart_port = UART_PORT_DEFAULT;
> module_param( uart_port, uint, 0400 );
> MODULE_PARM_DESC(uart_port, "UART port number to initialize in RS485
> mode [0-" __MODULE_STRING(UART_PORT_WITH_RTS_MAX)"]");
> 
> // get device_name as parameter, available in sysfs
> static char device_name[30] = UART_PORT_DEFAULT_DEVICE_NAME;
> module_param_string( device_name, device_name, sizeof(device_name), 0400
> );
> 
> /*
>  * Register UARTX as platform device.
>  */
> static int __init axm_rs485_uart_init(void)
> {
>   int retValue = 0;
>   unsigned mode;
> 
>   //check parameter input
>   if (uart_port >= UART_PORT_WITH_RTS_MAX)
>   {
>     printk(KERN_ERR "%s: uart_port=%u must be below %u\n",
> THIS_MODULE->name,
>         uart_port, UART_PORT_WITH_RTS_MAX);
>     return -EINVAL;
>   }
> 
>   /*
>    * Get platform_device struct from exported at91_register_uart
>    *
>    * - Don't configure RTS pin.
>    * - portnr = uart_port+1 (not necessary due call after boot-up)
>    */
>   pdev = at91_register_uart(at91sam9260_id_us[uart_port], uart_port + 1,
>       ATMEL_UART_CTS);

I don't know how this can even compile: at91_register_uart() returns an
int, and pdev is a platform_device * is your code.

I'm sorry but this code really isn't implemented with the driver model
in mind. That's not how things are supposed to work.

And also, your code is completely wrapped, which makes it impossible to
read.

It's the board/SoC code (in your case
arch/arm/mach-at91/board-at91sam9261ek.c and
arch/arm/mach-at91/at91sam9260_devices.c) that instantiates the
platform_device structure and does the pin muxing. Each platform_device
structure can receive driver-specific information in the platform_data
field, such as the ones needed here for RS485. You really don't need a
separate kernel module to do this, just modify your board/SoC file (in
this case you'll have to modify the SoC file, as the
at91_add_device_serial() function doesn't allow to pass the
UART-specific platform_data, unlike what is done with the other
at91_add_device_*() functions).

Regards,

Thomas
-- 
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply	[flat|nested] 3+ messages in thread

* AW: Simple write to an UART mode register fails
  2011-03-03 13:12 ` Thomas Petazzoni
@ 2011-03-03 17:01   ` Frey ext-FA, Maurus
  0 siblings, 0 replies; 3+ messages in thread
From: Frey ext-FA, Maurus @ 2011-03-03 17:01 UTC (permalink / raw)
  To: kernelnewbies

Salut Thomas,

Thanks! Your answer is appreciated. 

I know that what I'm trying to do seems to weird and does not follow the
best practice I've learned. But currently it's not the appropriate
moment to do such changes in board kernel. So I have to provide a quick
solution with a LKM. 

> 
> I haven't look into many details, but I don't see why you need a
> specific driver to do this. The Atmel serial driver in
> drivers/serial/atmel_serial.c already supports RS485.
> 
> So from what I can see, all you need to do is to add some details in
> the platform_data structure relative to the serial port. So probably
> you need to change the uart3_data structure in
> arch/arm/mach-at91/at91sam9260_devices.c, by adding a .rs485 field :
> 
> static struct atmel_uart_data uart3_data = {
>         .use_dma_tx     = 1,
>         .use_dma_rx     = 1,
> 	.rs485		= {
> 			/* Fill the fields of the serial_rs485 structure
> 			   defined in include/linux/serial.h */
> 	},
> };
Sorry when I wasn't precise enough in my explanation: Finally I want
choose if this UART should be associated with the serial-driver
(according to the device model) or with another not-tty driver. That's
why I allow to change platform_device.name in the LKM. 

Anyway the 2.6.32 atmel_serial driver doesn't support the RS485 mode. As
mentioned before I'm currently not "able" to back-port the most recent
driver. 
http://lxr.free-electrons.com/source/drivers/serial/atmel_serial.c?v=2.6
.32;a=arm

> 
> To configure the pins, you have to call at91_register_uart() in the
> ek_map_io() function of your board file  (presumably
> arch/arm/mach-at91/board-at91sam9261ek.c). The third parameter allows
> to tell which pins should be configured. For example, if you pass
> ATMEL_UART_RTS, then the code in
> arch/arm/mach-at91/at91sam9260_devices.c will do:
> 
>         if (pins & ATMEL_UART_RTS)
>                 at91_set_B_periph(AT91_PIN_PC8, 0);     /* RTS3 */
> 
> see the function configure_usart3_pins().

Yes, that's where I got at91_set_X_periph() from.

> 
> I don't know how this can even compile: at91_register_uart() 
> returns an
> int, and pdev is a platform_device * is your code.

Sorry: I forgot to mention that at91_register_uart() has been patched to
return platform_device*. Without it wouldn't be possible to compile. I
didn't post the full context. 

> 
> I'm sorry but this code really isn't implemented with the driver model
> in mind. That's not how things are supposed to work.

It's "bricolage", I know. I'm urged to wrest this device out of the
driver model, to use it with a specific driver. 

> And also, your code is completely wrapped, which makes it 
> impossible to
> read.

I thought that to post the entire lkm code would be better than
partially post it... Wrong choice related to the line wrapping. 

Thanks for your help.

Regards

maurus

> 
> It's the board/SoC code (in your case
> arch/arm/mach-at91/board-at91sam9261ek.c and
> arch/arm/mach-at91/at91sam9260_devices.c) that instantiates the
> platform_device structure and does the pin muxing. Each 
> platform_device
> structure can receive driver-specific information in the platform_data
> field, such as the ones needed here for RS485. You really don't need a
> separate kernel module to do this, just modify your board/SoC file (in
> this case you'll have to modify the SoC file, as the
> at91_add_device_serial() function doesn't allow to pass the
> UART-specific platform_data, unlike what is done with the other
> at91_add_device_*() functions).
> 
> Regards,
> 
> Thomas
> -- 
> Thomas Petazzoni, Free Electrons
> Kernel, drivers, real-time and embedded Linux
> development, consulting, training and support.
> http://free-electrons.com

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2011-03-03 17:01 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-02 13:08 Simple write to an UART mode register fails Frey ext-FA, Maurus
2011-03-03 13:12 ` Thomas Petazzoni
2011-03-03 17:01   ` AW: " Frey ext-FA, Maurus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).