Skip to contentSkip to conversation

pruss mfd drivers

PATCH Superseded Subhasish Ghosh 16 participants 79 messages Feb 11, 2011 – Mar 22, 2011 2 messages not in the archive

Subsystems (8)

v2 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

v2 cover pruss mfd drivers

PRUSS Functional Block Diagram
/----------------------------------------------------\
| |
| |------| |
32GPO<------->PRU CORE-0 <------>| |<------> DRAM 0 |
30GPI<------->(4KB IRAM) | S | (512 Bytes) |
| | | |
32GPO<------->PRU CORE-1 <------>| C |<------> DRAM 1 |
30GPI<------->(4KB IRAM) | | (512 Bytes) |
| | R | |
| | | |

 Ints to ARM/  |    Interrupt <------>|      |-------------------------> Master I/F (to SCR2)
 DSP INTC <------->Controller         |      |<------------------------- Slave I/F (from SCR2)
 Events from   |     (INTC)           |------|                      |
 Periph + PRUs |                                                    |
               \----------------------------------------------------/

Programmable Realtime Unit (PRU) is basically a 32-bit RISC processor available within TI's DA8XX SOCs. It consists of local instruction and data RAM and also has access to SOC resources via a Switched Central Resource (SCR).

There are two PRU's available within DA8XX SOC's PRUSS, hence providing two execution cores. Devices/Protocols can be emulated on these utilizing either both or only one of the PRUs independently.

The rational behind the MFD driver being the fact that multiple devices can be implemented on the cores independently.
It's also possible, as in our case, to implement a single device on both the PRU's resulting in improved load sharing.

A detailed description is also available here:
http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem

version 2:
==========
* added pruss TTY Soft-UART driver.
* added pruss Soft-UART board and platform changes.
* fixed previous review comments.
* reordered patch sequence.

version 1:
==========
* added pruss mfd driver.
* added pruss mfd board and platform changes.
* added pruss SocketCAN driver.
* added pruss SocketCAN board and platform changes.
* added pruss SocketCAN GPIOs.
* added previous review comments.

Subhasish Ghosh (13):
mfd: pruss mfd driver.
da850: pruss platform specific additions.
da850: pruss board specific additions.
mfd: pruss CAN private data.
da850: pruss CAN platform specific additions.
da850: pruss CAN board specific additions.
da850: pruss CAN platform specific changes for gpios.
da850: pruss CAN board specific changes for gpios.
can: pruss CAN driver.
mfd: pruss SUART private data.
da850: pruss SUART board specific additions.
da850: pruss SUART platform specific additions.
tty: pruss SUART driver

 arch/arm/mach-davinci/board-da850-evm.c            |  105 +
 arch/arm/mach-davinci/da850.c                      |   12 +
 arch/arm/mach-davinci/devices-da8xx.c              |   76 +
 arch/arm/mach-davinci/include/mach/da8xx.h         |    2 +
 arch/arm/mach-davinci/include/mach/mux.h           |    5 +
 drivers/mfd/Kconfig                                |   10 +
 drivers/mfd/Makefile                               |    1 +
 drivers/mfd/da8xx_pru.c                            |  446 ++++
 drivers/net/can/Kconfig                            |    1 +
 drivers/net/can/Makefile                           |    1 +
 drivers/net/can/da8xx_pruss/Kconfig                |   73 +
 drivers/net/can/da8xx_pruss/Makefile               |    7 +
 drivers/net/can/da8xx_pruss/pruss_can.c            |  758 +++++++
 drivers/net/can/da8xx_pruss/pruss_can_api.c        | 1227 ++++++++++
 drivers/net/can/da8xx_pruss/pruss_can_api.h        |  290 +++
 drivers/tty/serial/Kconfig                         |    2 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/da8xx_pruss/Kconfig             |   19 +
 drivers/tty/serial/da8xx_pruss/Makefile            |    9 +
 drivers/tty/serial/da8xx_pruss/pruss_suart.c       | 1014 +++++++++
 drivers/tty/serial/da8xx_pruss/pruss_suart_api.c   | 2350 ++++++++++++++++++++
 drivers/tty/serial/da8xx_pruss/pruss_suart_api.h   |  345 +++
 drivers/tty/serial/da8xx_pruss/pruss_suart_board.h |   58 +
 drivers/tty/serial/da8xx_pruss/pruss_suart_err.h   |   48 +
 drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h |  526 +++++
 drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h  |  153 ++
 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c |  384 ++++
 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h |   63 +
 include/linux/mfd/pruss/da8xx_pru.h                |  131 ++
 include/linux/mfd/pruss/da8xx_prucore.h            |   74 +
 include/linux/serial_core.h                        |    2 +
 31 files changed, 8193 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/da8xx_pru.c
 create mode 100644 drivers/net/can/da8xx_pruss/Kconfig
 create mode 100644 drivers/net/can/da8xx_pruss/Makefile
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can.c
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.c
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/Kconfig
 create mode 100644 drivers/tty/serial/da8xx_pruss/Makefile
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart.c
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_api.c
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_api.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_board.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_err.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h
 create mode 100644 include/linux/mfd/pruss/da8xx_pru.h
 create mode 100644 include/linux/mfd/pruss/da8xx_prucore.h
Signature · 1 line
1.7.2.3
#1 lore raw

PATCH v2 1/13 mfd: pruss mfd driver.

This patch adds the pruss MFD driver and associated include files.

---
 drivers/mfd/Kconfig                     |   10 +
 drivers/mfd/Makefile                    |    1 +
 drivers/mfd/da8xx_pru.c                 |  446 +++++++++++++++++++++++++++++++
 include/linux/mfd/pruss/da8xx_pru.h     |  122 +++++++++
 include/linux/mfd/pruss/da8xx_prucore.h |   74 +++++
 5 files changed, 653 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/da8xx_pru.c
 create mode 100644 include/linux/mfd/pruss/da8xx_pru.h
 create mode 100644 include/linux/mfd/pruss/da8xx_prucore.h
drivers/mfd/Kconfig +10 -0
index fd01836..6c437df 100644
@@ -81,6 +81,16 @@ config MFD_DM355EVM_MSP
 	  boards.  MSP430 firmware manages resets and power sequencing,
 	  inputs from buttons and the IR remote, LEDs, an RTC, and more.
 
+config MFD_DA8XX_PRUSS
+	tristate "Texas Instruments DA8XX PRUSS support"
+	depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850
+	select MFD_CORE
+	help
+	    This driver provides support api's for the programmable
+		realtime unit (PRU) present on TI's da8xx processors. It
+		provides basic read, write, config, enable, disable
+		routines to facilitate devices emulated on it.
+
 config HTC_EGPIO
 	bool "HTC EGPIO support"
 	depends on GENERIC_HARDIRQS && GPIOLIB && ARM
drivers/mfd/Makefile +1 -0
index a54e2c7..670d6b0 100644
@@ -13,6 +13,7 @@ obj-$(CONFIG_HTC_PASIC3)	+= htc-pasic3.o
 obj-$(CONFIG_HTC_I2CPLD)	+= htc-i2cpld.o
 
 obj-$(CONFIG_MFD_DAVINCI_VOICECODEC)	+= davinci_voicecodec.o
+obj-$(CONFIG_MFD_DA8XX_PRUSS)	+= da8xx_pru.o
 obj-$(CONFIG_MFD_DM355EVM_MSP)	+= dm355evm_msp.o
 
 obj-$(CONFIG_MFD_STMPE)		+= stmpe.o
drivers/mfd/da8xx_pru.c new file +446 -0

446 lines not on this page · raw message

include/linux/mfd/pruss/da8xx_pru.h new file +122 -0

122 lines not on this page · raw message

include/linux/mfd/pruss/da8xx_prucore.h new file +74 -0

74 lines not on this page · raw message

Signature · 1 line
1.7.2.3
#2 lore raw

Re: [PATCH v2 01/13] mfd: pruss mfd driver.

Hi Subhasish,

Quoted 1 line · Subhasish Ghosh

This patch adds the pruss MFD driver and associated include files.

A more detailed changelog would be better. What is this device, why is it an MFD and what are its potential subdevices ?

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 86

Quoted 11 lines
drivers/mfd/Kconfig +3 -0
index fd01836..6c437df 100644
@@ -81,6 +81,16 @@ config MFD_DM355EVM_MSP
 	  boards.  MSP430 firmware manages resets and power sequencing,
 	  inputs from buttons and the IR remote, LEDs, an RTC, and more.
 
+config MFD_DA8XX_PRUSS
+	tristate "Texas Instruments DA8XX PRUSS support"
+	depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850

Why are we depending on those ?

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 92

Quoted 6 lines
drivers/mfd/Kconfig +6 -0
+	select MFD_CORE
+	help
+	    This driver provides support api's for the programmable
+		realtime unit (PRU) present on TI's da8xx processors. It
+		provides basic read, write, config, enable, disable
+		routines to facilitate devices emulated on it.

Please fix your identation here.

drivers/mfd/da8xx_pru.c line 2

Quoted 5 lines
drivers/mfd/da8xx_pru.c new file +2 -0
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated

s/2010/2011/ ?

Quoted 14 lines
drivers/mfd/da8xx_pru.c new file +14 -0
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>

drivers/mfd/da8xx_pru.c line 27

Quoted 10 lines
drivers/mfd/da8xx_pru.c new file +10 -0
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mfd/pruss/da8xx_prucore.h>
+#include <linux/mfd/pruss/da8xx_pru.h>
+#include <linux/mfd/core.h>
+#include <linux/io.h>
+#include <mach/da8xx.h>

Is that include needed ?

drivers/mfd/da8xx_pru.c line 29

Quoted 1 line
drivers/mfd/da8xx_pru.c new file +1 -0
+struct da8xx_pruss {

What is the "ss" suffix for ?

drivers/mfd/da8xx_pru.c line 54

Quoted 10 lines
drivers/mfd/da8xx_pru.c new file +10 -0
+u32 pruss_disable(struct device *dev, u8 pruss_num)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	da8xx_prusscore_regs h_pruss;
+	u32 temp_reg;
+
+	if (pruss_num == DA8XX_PRUCORE_0) {
+		/* Disable PRU0  */
+		h_pruss = (da8xx_prusscore_regs)
+			((u32) pruss->ioaddr + 0x7000);

So it seems you're doing this in several places, and I have a few comments:

- You don't need the da8xx_prusscore_regs at all.
- Define the register map through a set of #define in your header file.
- Use a static routine that takes the core number and returns the register map offset.

Then routines like this one will look a lot more readable.

Quoted 2 lines
drivers/mfd/da8xx_pru.c new file +2 -0
+s16 pruss_writeb(struct device *dev, u32 u32offset,
+		u8 *pu8datatowrite, u16 u16bytestowrite)
#3 lore raw

Re: [PATCH v2 01/13] mfd: pruss mfd driver.

Thank you for your comments.

--------------------------------------------------
From: "Samuel Ortiz" <[email protected]>
Sent: Monday, February 21, 2011 10:00 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 01/13] mfd: pruss mfd driver.

Quoted 7 lines

Hi Subhasish,

Quoted 1 line · Subhasish Ghosh

This patch adds the pruss MFD driver and associated include files.

A more detailed changelog would be better. What is this device, why is it an MFD and what are its potential subdevices ?

SG -- Will add a more detailed change log.

Quoted 8 lines
Quoted 8 lines
drivers/mfd/Kconfig +0 -0
index fd01836..6c437df 100644
@@ -81,6 +81,16 @@ config MFD_DM355EVM_MSP
   boards.  MSP430 firmware manages resets and power sequencing,
   inputs from buttons and the IR remote, LEDs, an RTC, and more.
 
Quoted 4 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 86

Quoted 3 lines
drivers/mfd/Kconfig +3 -0
+config MFD_DA8XX_PRUSS
+ tristate "Texas Instruments DA8XX PRUSS support"
+ depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850

Why are we depending on those ?

SG -- The PRUSS core in only available within DA850 and DA830,
DA830 support is not yet implemented.

Quoted 9 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 92

Quoted 8 lines
drivers/mfd/Kconfig +6 -0
+ select MFD_CORE
+ help
+     This driver provides support api's for the programmable
+ realtime unit (PRU) present on TI's da8xx processors. It
+ provides basic read, write, config, enable, disable
+ routines to facilitate devices emulated on it.

Please fix your identation here.

SG -- Will do.

Quoted 27 lines

drivers/mfd/da8xx_pru.c line 2

Quoted 6 lines
drivers/mfd/da8xx_pru.c new file +2 -0
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated

s/2010/2011/ ?

Quoted 19 lines
drivers/mfd/da8xx_pru.c new file +1 -0
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */

drivers/mfd/da8xx_pru.c new file +8 -0
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
Quoted 8 lines

drivers/mfd/da8xx_pru.c line 27

Quoted 7 lines
drivers/mfd/da8xx_pru.c new file +7 -0
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mfd/pruss/da8xx_prucore.h>
+#include <linux/mfd/pruss/da8xx_pru.h>
+#include <linux/mfd/core.h>
+#include <linux/io.h>
+#include <mach/da8xx.h>

Is that include needed ?

SG - Will remove if not needed.

Quoted 4 lines
Quoted 3 lines

+struct da8xx_pruss {

What is the "ss" suffix for ?

SG -- We call it the PRU Sub-System.

Quoted 14 lines

drivers/mfd/da8xx_pru.c line 54

Quoted 12 lines
drivers/mfd/da8xx_pru.c new file +10 -0
+u32 pruss_disable(struct device *dev, u8 pruss_num)
+{
+ struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+ da8xx_prusscore_regs h_pruss;
+ u32 temp_reg;
+
+ if (pruss_num == DA8XX_PRUCORE_0) {
+ /* Disable PRU0  */
+ h_pruss = (da8xx_prusscore_regs)
+ ((u32) pruss->ioaddr + 0x7000);

So it seems you're doing this in several places, and I have a few comments:

Quoted 7 lines

- You don't need the da8xx_prusscore_regs at all.
- Define the register map through a set of #define in your header file.
- Use a static routine that takes the core number and returns the register map
offset.

Then routines like this one will look a lot more readable.

SG -- There are a huge number of PRUSS registers. A lot of them are reserved and are expected to change as development on the
controller is still ongoing. If we use #defines to plot all the
registers, then first, there are too many array type registers which will need to be duplicated.
And if the offset of one of the macro changes in between, we
will require to adjust all the rest of the macros. So I felt like a structure was a better option here.
I also named the structure elements as per the register names.
so why should it be less readable ? But, we can definitely use macros to define the PRUSS0 & 1
offsets instead of the magic numbers as above.

Quoted 5 lines
Quoted 3 lines

+s16 pruss_writeb(struct device *dev, u32 u32offset,
+ u8 *pu8datatowrite, u16 u16bytestowrite)

From CodingStyle: "Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged"

SG -- Will change.

Quoted 3 lines

Also, all your exported routines severely lack any sort of locking. An IO mutex or spinlock is mandatory here.

SG - As per our current implementation, we do not have two devices running simultaneously on the PRU,
so we do not have any way to test it. We have kept this as an
enhancement if request comes in for
multiple devices.

Quoted 19 lines
Quoted 19 lines
drivers/mfd/da8xx_pru.c new file +18 -0
+static int pruss_mfd_add_devices(struct platform_device *pdev)
+{
+ struct da8xx_pruss_devices *dev_data = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct mfd_cell cell;
+ u32 err, count;
+
+ for (count = 0; (dev_data + count)->dev_name != NULL; count++) {
+ memset(&cell, 0, sizeof(struct mfd_cell));
+ cell.id = count;
+ cell.name = (dev_data + count)->dev_name;
+ cell.platform_data = (dev_data + count)->pdata;
+ cell.data_size = (dev_data + count)->pdata_size;
+
+ err = mfd_add_devices(dev, 0, &cell, 1, NULL, 0);
+ if (err) {
+ dev_err(dev, "cannot add mfd cells\n");
+ return err;
Quoted 8 lines
Quoted 4 lines

+ }
+ }
+ return err;
+}

So, what are the potential subdevices for this driver ? If it's a really dynamic setup, I'm fine with passing those as platform data but then do it so that you pass a NULL terminated da8xx_pruss_devices array. That will avoid most of the ugly casts you're doing here.

SG -- I did not follow your recommendations here, could you please elaborate.
I am already checking the dev_name for a NULL.
This device is basically a microcontroller within DA850, so
basically any device or protocol can be
emulated on it. Currently, we have emulated 8 UARTS using the
two PRUs and also a CAN device.

Quoted 25 lines
Quoted 25 lines
include/linux/mfd/pruss/da8xx_pru.h new file +5 -0
new file mode 100644
index 0000000..68d8421
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Jitendra Kumar <[email protected]>
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */

include/linux/mfd/pruss/da8xx_pru.h new file +3 -0
+
+#ifndef _PRUSS_H_
+#define _PRUSS_H_
Quoted 8 lines

include/linux/mfd/pruss/da8xx_pru.h line 23

Quoted 7 lines
include/linux/mfd/pruss/da8xx_pru.h new file +7 -0
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include "da8xx_prucore.h"
+
+#define PRUSS_NUM0 DA8XX_PRUCORE_0
+#define PRUSS_NUM1 DA8XX_PRUCORE_1

Those are unused.

SG - These are getting used by the CAN & UART drivers.

Quoted 8 lines

include/linux/mfd/pruss/da8xx_prucore.h

Quoted 6 lines
include/linux/mfd/pruss/da8xx_prucore.h new file +0 -0
new file mode 100644
index 0000000..81f2ff9

Please rename your mfd include directory to include/linux/mfd/da8xx/, so that one can match it with the drivers/mfd/da8xx driver code.

SG - Will do.

Quoted 18 lines
Quoted 7 lines
include/linux/mfd/pruss/da8xx_prucore.h new file +5 -0
+typedef struct {
+ u32 CONTROL;
+ u32 STATUS;
+ u32 WAKEUP;
+ u32 CYCLECNT;

include/linux/mfd/pruss/da8xx_prucore.h line 72

Quoted 10 lines
include/linux/mfd/pruss/da8xx_prucore.h new file +10 -0
+ u32 STALLCNT;
+ u8  RSVD0[12];
+ u32 CONTABBLKIDX0;
+ u32 CONTABBLKIDX1;
+ u32 CONTABPROPTR0;
+ u32 CONTABPROPTR1;
+ u8  RSVD1[976];
+ u32 INTGPR[32];
+ u32 INTCTER[32];
+} *da8xx_prusscore_regs;

Again, we don't need that structure.

SG - As mentioned above.

Quoted 8 lines

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/ 
#4 lore raw

Re: [PATCH v2 01/13] mfd: pruss mfd driver.

Hi Subhasish,

Quoted 1 line · Subhasish Ghosh

Thank you for your comments.

No problem.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mfd/Kconfig +0 -0
index fd01836..6c437df 100644
@@ -81,6 +81,16 @@ config MFD_DM355EVM_MSP
  boards.  MSP430 firmware manages resets and power sequencing,
  inputs from buttons and the IR remote, LEDs, an RTC, and more.
 
Quoted 7 lines
Quoted 4 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 86

Quoted 3 lines
drivers/mfd/Kconfig +3 -0
+config MFD_DA8XX_PRUSS
+ tristate "Texas Instruments DA8XX PRUSS support"
+ depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850

Why are we depending on those ?

SG -- The PRUSS core in only available within DA850 and DA830,
DA830 support is not yet implemented.

Sure, but if there are no actual code dependencies, I'd like to get rid of those depends.

Quoted 19 lines
Quoted 19 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 96

Quoted 10 lines
drivers/mfd/Kconfig +10 -0
+u32 pruss_disable(struct device *dev, u8 pruss_num)
+{
+ struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+ da8xx_prusscore_regs h_pruss;
+ u32 temp_reg;
+
+ if (pruss_num == DA8XX_PRUCORE_0) {
+ /* Disable PRU0  */
+ h_pruss = (da8xx_prusscore_regs)
+ ((u32) pruss->ioaddr + 0x7000);

So it seems you're doing this in several places, and I have a few comments:

- You don't need the da8xx_prusscore_regs at all.
- Define the register map through a set of #define in your header file.
- Use a static routine that takes the core number and returns the register map
offset.

Quoted 5 lines
Quoted 1 line

Then routines like this one will look a lot more readable.

SG -- There are a huge number of PRUSS registers. A lot of them are reserved and are expected to change as development on the
controller is still ongoing.

First of all, from what I read in your patch you're only using the CONTROL offset.

Quoted 3 lines

If we use #defines to plot
all the registers, then first, there are too many array type registers which will need to be duplicated.

What I'm expecting is a small set of defines for the register offsets. You have 13 fields in your da8xx_prusscore_regs, you only need to define 13 register offsets.

So, if you have a:

static u32 reg_offset(struct device *dev, u8 pru_num)

{
	struct da8xx_pruss *pru = dev_get_drvdata(dev->parent);

	switch (pru_num) {
	case DA8XX_PRUCORE_0:
		return (u32) pru->ioaddr + 0x7000;
	case DA8XX_PRUCORE_1:
		return (u32) pru->ioaddr + 0x7800;
	default:
		return 0;
}

then routines like pruss_enable (which should return an int, btw) would look like:

int pruss_enable(struct device *dev, u8 pruss_num)

{
	u32 offset = reg_offset(dev, pruss_num);

	if (offset == 0)
		return -EINVAL;

	__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
			offset + PRU_CORE_CONTROL);

	return 0;
}
Quoted 8 lines
Quoted 2 lines

Also, all your exported routines severely lack any sort of locking. An IO mutex or spinlock is mandatory here.

SG - As per our current implementation, we do not have two devices running simultaneously on the PRU,
so we do not have any way to test it. We have kept this as an
enhancement if request comes in for
multiple devices.

It's not about having multiple devices at the same time, it's about having multiple callers writing and reading to the same registers. Since you're exporting all your I/O routines you have no way to prevent 2 drivers from writing to the same register at the "same" time. You need locking here, regardless of the number of devices that you can have on a system.

Quoted 28 lines
Quoted 19 lines
Quoted 19 lines
drivers/mfd/Kconfig +19 -0
+static int pruss_mfd_add_devices(struct platform_device *pdev)
+{
+ struct da8xx_pruss_devices *dev_data = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct mfd_cell cell;
+ u32 err, count;
+
+ for (count = 0; (dev_data + count)->dev_name != NULL; count++) {
+ memset(&cell, 0, sizeof(struct mfd_cell));
+ cell.id = count;
+ cell.name = (dev_data + count)->dev_name;
+ cell.platform_data = (dev_data + count)->pdata;
+ cell.data_size = (dev_data + count)->pdata_size;
+
+ err = mfd_add_devices(dev, 0, &cell, 1, NULL, 0);
+ if (err) {
+ dev_err(dev, "cannot add mfd cells\n");
+ return err;
+ }
Quoted 8 lines
Quoted 3 lines

+ }
+ return err;
+}

So, what are the potential subdevices for this driver ? If it's a really dynamic setup, I'm fine with passing those as platform data but then do it so
that you pass a NULL terminated da8xx_pruss_devices array. That will avoid most of the ugly casts you're doing here.

Quoted 7 lines

SG -- I did not follow your recommendations here, could you please elaborate.
I am already checking the dev_name for a NULL. This device is basically a microcontroller within DA850,
so basically any device or protocol can be
emulated on it. Currently, we have emulated 8 UARTS using
the two PRUs and also a CAN device.

Ok, I wasnt sure you can emulate anything on that thing. So I'm fine with you passing all your devices through platform_data. But I'd prefer this routine to look like:

[...]

	for (count = 0; dev_data[count] != NULL; count++) {
		memset(&cell, 0, sizeof(struct mfd_cell));
		cell.id			= count;
		cell.name		= dev_data[count]->dev_name;
		cell.platform_data	= dev_data[count]->pdata;
		cell.data_size		= dev_data[count]->pdata_size;

Looks nicer to me.

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/
#5 lore raw

Re: [PATCH v2 01/13] mfd: pruss mfd driver.

Quoted 78 lines · Samuel Ortiz

Hi Subhasish,

Quoted 1 line · Subhasish Ghosh

Thank you for your comments.

No problem.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mfd/Kconfig +0 -0
index fd01836..6c437df 100644
@@ -81,6 +81,16 @@ config MFD_DM355EVM_MSP
  boards.  MSP430 firmware manages resets and power sequencing,
  inputs from buttons and the IR remote, LEDs, an RTC, and more.
 
Quoted 7 lines
Quoted 4 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 86

Quoted 3 lines
drivers/mfd/Kconfig +3 -0
+config MFD_DA8XX_PRUSS
+ tristate "Texas Instruments DA8XX PRUSS support"
+ depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850

Why are we depending on those ?

SG -- The PRUSS core in only available within DA850 and DA830,
DA830 support is not yet implemented.

Sure, but if there are no actual code dependencies, I'd like to get rid of those depends.

Quoted 19 lines
Quoted 19 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 96

Quoted 10 lines
drivers/mfd/Kconfig +10 -0
+u32 pruss_disable(struct device *dev, u8 pruss_num)
+{
+ struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+ da8xx_prusscore_regs h_pruss;
+ u32 temp_reg;
+
+ if (pruss_num == DA8XX_PRUCORE_0) {
+ /* Disable PRU0  */
+ h_pruss = (da8xx_prusscore_regs)
+ ((u32) pruss->ioaddr + 0x7000);

So it seems you're doing this in several places, and I have a few comments:

- You don't need the da8xx_prusscore_regs at all.
- Define the register map through a set of #define in your header file.
- Use a static routine that takes the core number and returns the register map
offset.

Quoted 5 lines
Quoted 1 line

Then routines like this one will look a lot more readable.

SG -- There are a huge number of PRUSS registers. A lot of them are reserved and are expected to change as development on the
controller is still ongoing.

First of all, from what I read in your patch you're only using the CONTROL offset.

Quoted 3 lines

If we use #defines to plot
all the registers, then first, there are too many array type registers which will need to be duplicated.

What I'm expecting is a small set of defines for the register offsets. You have 13 fields in your da8xx_prusscore_regs, you only need to define 13 register offsets.

So, if you have a:

static u32 reg_offset(struct device *dev, u8 pru_num)

{
	struct da8xx_pruss *pru = dev_get_drvdata(dev->parent);

	switch (pru_num) {
	case DA8XX_PRUCORE_0:
		return (u32) pru->ioaddr + 0x7000;
	case DA8XX_PRUCORE_1:
		return (u32) pru->ioaddr + 0x7800;
	default:
		return 0;
}

then routines like pruss_enable (which should return an int, btw) would look like:

int pruss_enable(struct device *dev, u8 pruss_num)

Quoted 11 lines
{
	u32 offset = reg_offset(dev, pruss_num);

	if (offset == 0)
		return -EINVAL;

	__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
			offset + PRU_CORE_CONTROL);

	return 0;
}

All registers are memory mapped and could nicely be described by structures (and sub-structures). Therefore we asked to considerer structs, at least for the Pruss SocketCAN drivers. That would result in much much clearer and better readable code. The code above would shrink to:

	__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
		     &prucore[pruss_num].control);

And proper type checking would be ensured as well.

Wolfgang.

#6 lore raw

Re: [PATCH v2 01/13] mfd: pruss mfd driver.

Quoted 95 lines · Wolfgang Grandegger
Quoted 78 lines · Samuel Ortiz

Hi Subhasish,

Quoted 1 line · Subhasish Ghosh

Thank you for your comments.

No problem.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mfd/Kconfig +0 -0
index fd01836..6c437df 100644
@@ -81,6 +81,16 @@ config MFD_DM355EVM_MSP
  boards.  MSP430 firmware manages resets and power sequencing,
  inputs from buttons and the IR remote, LEDs, an RTC, and more.
 
Quoted 7 lines
Quoted 4 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 86

Quoted 3 lines
drivers/mfd/Kconfig +3 -0
+config MFD_DA8XX_PRUSS
+ tristate "Texas Instruments DA8XX PRUSS support"
+ depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850

Why are we depending on those ?

SG -- The PRUSS core in only available within DA850 and DA830,
DA830 support is not yet implemented.

Sure, but if there are no actual code dependencies, I'd like to get rid of those depends.

Quoted 19 lines
Quoted 19 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 96

Quoted 10 lines
drivers/mfd/Kconfig +10 -0
+u32 pruss_disable(struct device *dev, u8 pruss_num)
+{
+ struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+ da8xx_prusscore_regs h_pruss;
+ u32 temp_reg;
+
+ if (pruss_num == DA8XX_PRUCORE_0) {
+ /* Disable PRU0  */
+ h_pruss = (da8xx_prusscore_regs)
+ ((u32) pruss->ioaddr + 0x7000);

So it seems you're doing this in several places, and I have a few comments:

- You don't need the da8xx_prusscore_regs at all.
- Define the register map through a set of #define in your header file.
- Use a static routine that takes the core number and returns the register map
offset.

Quoted 5 lines
Quoted 1 line

Then routines like this one will look a lot more readable.

SG -- There are a huge number of PRUSS registers. A lot of them are reserved and are expected to change as development on the
controller is still ongoing.

First of all, from what I read in your patch you're only using the CONTROL offset.

Quoted 3 lines

If we use #defines to plot
all the registers, then first, there are too many array type registers which will need to be duplicated.

What I'm expecting is a small set of defines for the register offsets. You have 13 fields in your da8xx_prusscore_regs, you only need to define 13 register offsets.

So, if you have a:

static u32 reg_offset(struct device *dev, u8 pru_num)

{
	struct da8xx_pruss *pru = dev_get_drvdata(dev->parent);

	switch (pru_num) {
	case DA8XX_PRUCORE_0:
		return (u32) pru->ioaddr + 0x7000;
	case DA8XX_PRUCORE_1:
		return (u32) pru->ioaddr + 0x7800;
	default:
		return 0;
}

then routines like pruss_enable (which should return an int, btw) would look like:

int pruss_enable(struct device *dev, u8 pruss_num)

Quoted 11 lines
{
	u32 offset = reg_offset(dev, pruss_num);

	if (offset == 0)
		return -EINVAL;

	__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
			offset + PRU_CORE_CONTROL);

	return 0;
}

All registers are memory mapped and could nicely be described by structures (and sub-structures). Therefore we asked to considerer structs, at least for the Pruss SocketCAN drivers.

Quoted 5 lines

That would result in
much much clearer and better readable code. The code above would shrink to:

	__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
		     &prucore[pruss_num].control);

This driver seems to exclusively use the control offset, which is why I don't see an absolute need for doing this mapping.
But if both maps are contiguous then doing the mapping would prevent us from calling reg_offset() and would bring some advantage. I'd then be fine with it. For now, da8xx_prusscore_regs seems to be larger than the 0x800 interval between the 2 maps, so I have no idea if both maps are indeed contiguous.

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/
#7 lore raw

Re: [PATCH v2 01/13] mfd: pruss mfd driver.

I am not sure if I understood you correctly, but the current sizeof the structure da8xx_prusscore_regs is 0x500.

Here is a link to the PRUSS memory map:
http://processors.wiki.ti.com/index.php/PRUSS_Memory_Map

The offset 0x00007000 is the PRU0 reg offset and 0x00007800 is the PRU1 reg offset.
We cannot have a register file larger than this, but lot of space is left out, possibly for future development.

--------------------------------------------------
From: "Samuel Ortiz" <[email protected]>
Sent: Tuesday, February 22, 2011 5:03 PM
To: "Wolfgang Grandegger" <[email protected]>
Cc: "Subhasish Ghosh" <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "open list" <[email protected]>; <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 01/13] mfd: pruss mfd driver.

Quoted 115 lines · Wolfgang Grandegger
Quoted 96 lines · Samuel Ortiz
Quoted 82 lines

Hi Subhasish,

Quoted 1 line · Subhasish Ghosh

Thank you for your comments.

No problem.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mfd/Kconfig +0 -0
index fd01836..6c437df 100644
@@ -81,6 +81,16 @@ config MFD_DM355EVM_MSP
  boards.  MSP430 firmware manages resets and power sequencing,
  inputs from buttons and the IR remote, LEDs, an RTC, and more.
 
Quoted 7 lines
Quoted 4 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 86

Quoted 3 lines
drivers/mfd/Kconfig +3 -0
+config MFD_DA8XX_PRUSS
+ tristate "Texas Instruments DA8XX PRUSS support"
+ depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850

Why are we depending on those ?

SG -- The PRUSS core in only available within DA850 and DA830,
DA830 support is not yet implemented.

Sure, but if there are no actual code dependencies, I'd like to get rid of those depends.

Quoted 20 lines
Quoted 20 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 96

Quoted 10 lines
drivers/mfd/Kconfig +10 -0
+u32 pruss_disable(struct device *dev, u8 pruss_num)
+{
+ struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+ da8xx_prusscore_regs h_pruss;
+ u32 temp_reg;
+
+ if (pruss_num == DA8XX_PRUCORE_0) {
+ /* Disable PRU0  */
+ h_pruss = (da8xx_prusscore_regs)
+ ((u32) pruss->ioaddr + 0x7000);

So it seems you're doing this in several places, and I have a few comments:

- You don't need the da8xx_prusscore_regs at all.
- Define the register map through a set of #define in your header file.
- Use a static routine that takes the core number and returns the register map
offset.

Quoted 5 lines
Quoted 1 line

Then routines like this one will look a lot more readable.

SG -- There are a huge number of PRUSS registers. A lot of them are reserved and are expected to change as development on the
controller is still ongoing.

First of all, from what I read in your patch you're only using the CONTROL offset.

Quoted 3 lines

If we use #defines to plot
all the registers, then first, there are too many array type registers which will need to be duplicated.

What I'm expecting is a small set of defines for the register offsets. You have 13 fields in your da8xx_prusscore_regs, you only need to define 13 register offsets.

So, if you have a:

static u32 reg_offset(struct device *dev, u8 pru_num) {
struct da8xx_pruss *pru = dev_get_drvdata(dev->parent);

switch (pru_num) {
case DA8XX_PRUCORE_0:
return (u32) pru->ioaddr + 0x7000;
case DA8XX_PRUCORE_1:
return (u32) pru->ioaddr + 0x7800;
default:
return 0;
}

then routines like pruss_enable (which should return an int, btw) would look like:

int pruss_enable(struct device *dev, u8 pruss_num) {
u32 offset = reg_offset(dev, pruss_num);

Quoted 8 lines

if (offset == 0)
return -EINVAL;

__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
offset + PRU_CORE_CONTROL);

return 0;
}

All registers are memory mapped and could nicely be described by structures (and sub-structures). Therefore we asked to considerer structs, at least for the Pruss SocketCAN drivers.

Quoted 5 lines

That would result in
much much clearer and better readable code. The code above would shrink to:

__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,

     &prucore[pruss_num].control);

This driver seems to exclusively use the control offset, which is why I don't see an absolute need for doing this mapping.
But if both maps are contiguous then doing the mapping would prevent us from calling reg_offset() and would bring some advantage. I'd then be fine with it. For now, da8xx_prusscore_regs seems to be larger than the 0x800 interval between the 2 maps, so I have no idea if both maps are indeed contiguous.

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/ 
#8 lore raw

Re: [PATCH v2 01/13] mfd: pruss mfd driver.

Quoted 10 lines · Subhasish Ghosh

I am not sure if I understood you correctly, but the current sizeof the structure da8xx_prusscore_regs is 0x500.

Here is a link to the PRUSS memory map:
http://processors.wiki.ti.com/index.php/PRUSS_Memory_Map

The offset 0x00007000 is the PRU0 reg offset and 0x00007800 is the PRU1 reg offset.
We cannot have a register file larger than this, but lot of space is left out, possibly for future development.

What do you mean with "larger than this"? You ioremap the whole space and unsued space could be filled with padding bytes:

struct pruss_regs {
	u8 ram0[0x0200];
	u8 res0[0x1e00];
	u8 ram1[0x0200];
	u8 res1[0x1e00];
	struct pruss_intc_regs intc;
	struct pruss_core_regs core[2];
};

Then:

pruss_dev->regs = ioremap(pruss_dev->res->start,

	   	    resource_size(pruss_dev->res));
  __raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
               &pruss_dev->regs.core[pruss_num].control);

That's simple, transparent and save without magic offset handling.

Wolfgang.

#9 lore raw

Re: [PATCH v2 01/13] mfd: pruss mfd driver.

In reply to Samuel Ortiz

--------------------------------------------------
From: "Samuel Ortiz" <[email protected]>
Sent: Tuesday, February 22, 2011 4:01 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 01/13] mfd: pruss mfd driver.

Quoted 19 lines

Hi Subhasish,

Quoted 1 line · Subhasish Ghosh

Thank you for your comments.

No problem.

Quoted 13 lines
Quoted 12 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 86

Quoted 11 lines
drivers/mfd/Kconfig +3 -0
index fd01836..6c437df 100644
@@ -81,6 +81,16 @@ config MFD_DM355EVM_MSP
  boards.  MSP430 firmware manages resets and power sequencing,
  inputs from buttons and the IR remote, LEDs, an RTC, and more.
 
+config MFD_DA8XX_PRUSS
+ tristate "Texas Instruments DA8XX PRUSS support"
+ depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850

Why are we depending on those ?

Quoted 4 lines
Quoted 2 lines

SG -- The PRUSS core in only available within DA850 and DA830,
DA830 support is not yet implemented.

Sure, but if there are no actual code dependencies, I'd like to get rid of those depends.

SG -- The PRU Clock and Power is the dependency here.
This is available in arch/arm/mach-davinci/da850.c The source is specific to the SOC clock tree.

Quoted 75 lines
Quoted 20 lines
Quoted 20 lines

drivers/mfd/Kconfig config MFD_DM355EVM_MSP · line 96

Quoted 11 lines
drivers/mfd/Kconfig +10 -0
+u32 pruss_disable(struct device *dev, u8 pruss_num)
+{
+ struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+ da8xx_prusscore_regs h_pruss;
+ u32 temp_reg;
+
+ if (pruss_num == DA8XX_PRUCORE_0) {
+ /* Disable PRU0  */
+ h_pruss = (da8xx_prusscore_regs)
+ ((u32) pruss->ioaddr + 0x7000);

So it seems you're doing this in several places, and I have a few comments:

- You don't need the da8xx_prusscore_regs at all.
- Define the register map through a set of #define in your header file.
- Use a static routine that takes the core number and returns the register map
offset.

Quoted 5 lines
Quoted 1 line

Then routines like this one will look a lot more readable.

SG -- There are a huge number of PRUSS registers. A lot of them are reserved and are expected to change as development on the
controller is still ongoing.

First of all, from what I read in your patch you're only using the CONTROL offset.

Quoted 3 lines

If we use #defines to plot
all the registers, then first, there are too many array type registers which will need to be duplicated.

What I'm expecting is a small set of defines for the register offsets. You have 13 fields in your da8xx_prusscore_regs, you only need to define 13 register offsets.

So, if you have a:

static u32 reg_offset(struct device *dev, u8 pru_num) {
struct da8xx_pruss *pru = dev_get_drvdata(dev->parent);

switch (pru_num) {
case DA8XX_PRUCORE_0:
return (u32) pru->ioaddr + 0x7000;
case DA8XX_PRUCORE_1:
return (u32) pru->ioaddr + 0x7800;
default:
return 0;
}

then routines like pruss_enable (which should return an int, btw) would look like:

int pruss_enable(struct device *dev, u8 pruss_num) {
u32 offset = reg_offset(dev, pruss_num);

if (offset == 0)
return -EINVAL;

__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
offset + PRU_CORE_CONTROL);

return 0;
}

Quoted 8 lines
Quoted 2 lines

Also, all your exported routines severely lack any sort of locking. An IO mutex or spinlock is mandatory here.

SG - As per our current implementation, we do not have two devices running simultaneously on the PRU,
so we do not have any way to test it. We have kept this as an
enhancement if request comes in for
multiple devices.

Quoted 6 lines

It's not about having multiple devices at the same time, it's about having multiple callers writing and reading to the same registers. Since you're exporting all your I/O routines you have no way to prevent 2 drivers from writing to the same register at the "same" time. You need locking here, regardless of the number of devices that you can have on a system.

SG - Ok, will do

Quoted 48 lines
Quoted 29 lines
Quoted 20 lines
Quoted 20 lines
drivers/mfd/Kconfig +19 -0
+static int pruss_mfd_add_devices(struct platform_device *pdev)
+{
+ struct da8xx_pruss_devices *dev_data = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct mfd_cell cell;
+ u32 err, count;
+
+ for (count = 0; (dev_data + count)->dev_name != NULL; count++) {
+ memset(&cell, 0, sizeof(struct mfd_cell));
+ cell.id = count;
+ cell.name = (dev_data + count)->dev_name;
+ cell.platform_data = (dev_data + count)->pdata;
+ cell.data_size = (dev_data + count)->pdata_size;
+
+ err = mfd_add_devices(dev, 0, &cell, 1, NULL, 0);
+ if (err) {
+ dev_err(dev, "cannot add mfd cells\n");
+ return err;
+ }
Quoted 8 lines
Quoted 3 lines

+ }
+ return err;
+}

So, what are the potential subdevices for this driver ? If it's a really dynamic setup, I'm fine with passing those as platform data but then do it so
that you pass a NULL terminated da8xx_pruss_devices array. That will avoid most of the ugly casts you're doing here.

Quoted 7 lines

SG -- I did not follow your recommendations here, could you please elaborate.
I am already checking the dev_name for a NULL. This device is basically a microcontroller within DA850,
so basically any device or protocol can be
emulated on it. Currently, we have emulated 8 UARTS using
the two PRUs and also a CAN device.

Ok, I wasnt sure you can emulate anything on that thing. So I'm fine with you passing all your devices through platform_data. But I'd prefer this routine to look like:

[...]
for (count = 0; dev_data[count] != NULL; count++) { memset(&cell, 0, sizeof(struct mfd_cell)); cell.id = count;
cell.name = dev_data[count]->dev_name; cell.platform_data = dev_data[count]->pdata; cell.data_size = dev_data[count]->pdata_size;

Quoted 1 line

Looks nicer to me.

SG - I have a problem here, dev_data was initialized as a structure array.

static struct da8xx_pruss_devices pruss_devices[] = {
{
.dev_name = "da8xx_pruss_can",
.pdata = &can_data,
.pdata_size = sizeof(can_data),
.setup = da850_evm_setup_pruss_can,
.num_resources = 0,
.resources = NULL,
},
{
.dev_name = "da8xx_pruss_uart",
.pdata = &suart_data,
.pdata_size = sizeof(suart_data),
.setup = da850_evm_setup_pruss_suart,
.num_resources = ARRAY_SIZE(da850_evm_suart_resource),
.resources = da850_evm_suart_resource,
},
{
.dev_name = NULL,
},
};

How can I initialize the last array element to NULL! I think, I must have some type of delimiter.

Quoted 7 lines

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/ 
#10 lore raw

Re: [PATCH v2 01/13] mfd: pruss mfd driver.

In reply to Samuel Ortiz
Quoted 14 lines · Samuel Ortiz

So, if you have a:

static u32 reg_offset(struct device *dev, u8 pru_num)

{
	struct da8xx_pruss *pru = dev_get_drvdata(dev->parent);

	switch (pru_num) {
	case DA8XX_PRUCORE_0:
		return (u32) pru->ioaddr + 0x7000;
	case DA8XX_PRUCORE_1:
		return (u32) pru->ioaddr + 0x7800;
	default:
		return 0;
}

No. Please don't encourage people to have 'u32' as valid cookies for readl,writel et.al. Always make the mmio cookies __iomem pointer like.

#11 lore raw

PATCH v2 2/13 da850: pruss platform specific additions.

In reply to Subhasish Ghosh

This patch adds the platform device and assignes the platform resources for the PRUSS mfd driver.

---
 arch/arm/mach-davinci/da850.c              |    7 +++
 arch/arm/mach-davinci/devices-da8xx.c      |   65 ++++++++++++++++++++++++++++
 arch/arm/mach-davinci/include/mach/da8xx.h |    2 +
 3 files changed, 74 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/da850.c +7 -0
index 8a4de97..a098263 100644
@@ -238,6 +238,12 @@ static struct clk tptc2_clk = {
 	.flags		= ALWAYS_ENABLED,
 };
 
+static struct clk pruss_clk = {
+	.name		= "pruss",
+	.parent		= &pll0_sysclk2,
+	.lpsc		= DA8XX_LPSC0_PRUSS,
+};
+
 static struct clk uart0_clk = {
 	.name		= "uart0",
 	.parent		= &pll0_sysclk2,
@@ -387,6 +393,7 @@ static struct clk_lookup da850_clks[] = {
 	CLK(NULL,		"tpcc1",	&tpcc1_clk),
 	CLK(NULL,		"tptc2",	&tptc2_clk),
 	CLK(NULL,		"uart0",	&uart0_clk),
+	CLK(NULL,		"pruss",	&pruss_clk),
 	CLK(NULL,		"uart1",	&uart1_clk),
 	CLK(NULL,		"uart2",	&uart2_clk),
 	CLK(NULL,		"aintc",	&aintc_clk),
arch/arm/mach-davinci/devices-da8xx.c +65 -0

77 lines not on this page · raw message

arch/arm/mach-davinci/include/mach/da8xx.h +2 -0
index c3c3339..2c400d4 100644
@@ -15,6 +15,7 @@
 
 #include <linux/platform_device.h>
 #include <linux/davinci_emac.h>
+#include <linux/mfd/pruss/da8xx_pru.h>
 
 #include <mach/serial.h>
 #include <mach/edma.h>
@@ -82,6 +83,7 @@ int da8xx_register_watchdog(void);
 int da8xx_register_usb20(unsigned mA, unsigned potpgt);
 int da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata);
 int da8xx_register_emac(void);
+int da8xx_register_pruss(struct da8xx_pruss_devices *);
 int da8xx_register_lcdc(struct da8xx_lcdc_platform_data *pdata);
 int da8xx_register_mmcsd0(struct davinci_mmc_config *config);
 int da850_register_mmcsd1(struct davinci_mmc_config *config);
Signature · 1 line
1.7.2.3
#12 lore raw

Re: [PATCH v2 02/13] da850: pruss platform specific additions.

Hello.

Quoted 2 lines · Subhasish Ghosh

This patch adds the platform device and assignes the platform resources for the PRUSS mfd driver.

You also add the PRUSS clock (and I would have done that as a separate patch).

Quoted 1 line

You should have noted the patch dependency here -- this patch depends on the
one nenaming DA8XX_LPSC0_DMAX.

WBR, Sergei

#13 lore raw

Re: [PATCH v2 02/13] da850: pruss platform specific additions.

--------------------------------------------------
From: "Sergei Shtylyov" <[email protected]>
Sent: Saturday, February 12, 2011 12:11 AM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "Kevin Hilman (supporter:TI DAVINCI MACHIN...,commit_signer:16/18=89%)" <[email protected]>; "Russell King (maintainer:ARM PORT)" <[email protected]>; "Michael Williamson (commit_signer:5/18=28%)" <[email protected]>; "Cyril Chemparathy (commit_signer:3/18=17%)" <[email protected]>; "Sergei Shtylyov (commit_signer:3/18=17%)" <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 02/13] da850: pruss platform specific additions.

Quoted 7 lines

Hello.

Quoted 2 lines · Subhasish Ghosh

This patch adds the platform device and assignes the platform resources for the PRUSS mfd driver.

Quoted 7 lines

You also add the PRUSS clock (and I would have done that as a separate
patch).

Quoted 1 line

You should have noted the patch dependency here -- this patch depends
on the one nenaming DA8XX_LPSC0_DMAX.

SG - Will do

Quoted 2 lines

WBR, Sergei

#14 lore raw

RE: [PATCH v2 02/13] da850: pruss platform specific additions.

In reply to Subhasish Ghosh

Hi,

Quoted 10 lines

-----Original Message-----
From: davinci-linux-open-source-bounces at linux.davincidsp.com [mailto:davinci-linux-open-source-bounces at linux.davincidsp.com] On Behalf Of Subhasish Ghosh
Sent: Friday, February 11, 2011 8:21 PM
To: davinci-linux-open-source at linux.davincidsp.com
Cc: sachi at mistralsolutions.com; Russell King; Kevin Hilman; Subhasish Ghosh; open list; Watkins, Melissa; linux-arm-kernel at lists.infradead.org
Subject: [PATCH v2 02/13] da850: pruss platform specific additions.

Quoted 6 lines

This patch adds the platform device and assignes the platform resources for the PRUSS mfd driver.

---
+struct platform_device da8xx_pruss_dev = {

Can you please rename this to da8xx_pruss_mfddev?

Review

Quoted 1 line
(quoted diff) +1 -0
+	.name		= "da8xx_pruss",

Can you please rename name string as pruss_mfd? This will help to base my UIO patches on top of yours.

arch/arm/mach-davinci/devices-da8xx.c void __init da8xx_register_mcasp(int id, struct snd_platform_data *pdata) · line 561

Quoted 6 lines
arch/arm/mach-davinci/devices-da8xx.c +6 -0
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(da8xx_pruss_resources),
+	.resource	= da8xx_pruss_resources,
+};
+
+int __init da8xx_register_pruss(struct da8xx_pruss_devices *pruss_device)

Please rename this to da8xx_register_pruss_mfd

Thanks,
Pratheesh

#15 lore raw

Re: [PATCH v2 02/13] da850: pruss platform specific additions.

Hello,

Can we not have the PRU UIO also as a part of the MFD framework.
I know that the UIO is not really a device, but I feel its incorrect to register the same device twice. Instead we can have a single entry as the MFD framework and the UIO, as a child device, based upon the MFD driver APIs.

All that we need to do is add an entry into the da8xx_pruss_devices structure and the MFD driver will do the rest to get the UIO probe called.

--------------------------------------------------
From: "TK, Pratheesh Gangadhar" <[email protected]>
Sent: Monday, February 28, 2011 6:34 PM
To: "Subhasish Ghosh" <[email protected]>; <[email protected]>
Cc: <[email protected]>; "Russell King" <[email protected]>; "Kevin Hilman" <[email protected]>; "open list" <[email protected]>; "Watkins, Melissa" <[email protected]>; <[email protected]>
Subject: RE: [PATCH v2 02/13] da850: pruss platform specific additions.

Quoted 33 lines

Hi,

Quoted 10 lines

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Subhasish Ghosh
Sent: Friday, February 11, 2011 8:21 PM
To: [email protected]
Cc: [email protected]; Russell King; Kevin Hilman; Subhasish Ghosh; open list; Watkins, Melissa; [email protected]
Subject: [PATCH v2 02/13] da850: pruss platform specific additions.

Quoted 6 lines

This patch adds the platform device and assignes the platform resources for the PRUSS mfd driver.

---
+struct platform_device da8xx_pruss_dev = {

Can you please rename this to da8xx_pruss_mfddev?

Quoted 1 line

+ .name = "da8xx_pruss",

Can you please rename name string as pruss_mfd? This will help to base my UIO patches on top of yours.

Quoted 7 lines

+ .id = -1,
+ .num_resources = ARRAY_SIZE(da8xx_pruss_resources),
+ .resource = da8xx_pruss_resources,

arch/arm/mach-davinci/da850.c +3 -0
+};
+
+int __init da8xx_register_pruss(struct da8xx_pruss_devices 

*pruss_device)

Please rename this to da8xx_register_pruss_mfd

Thanks,
Pratheesh

#16 lore raw

RE: [PATCH v2 02/13] da850: pruss platform specific additions.

Hi,

Quoted 11 lines

-----Original Message-----
From: Subhasish Ghosh [mailto:subhasish at mistralsolutions.com]
Sent: Tuesday, March 01, 2011 12:29 PM
Hello,

Can we not have the PRU UIO also as a part of the MFD framework. I know that the UIO is not really a device, but I feel its incorrect to register the same device twice.
Instead we can have a single entry as the MFD framework and the UIO, as a child device, based upon the MFD driver APIs.

Quoted 4 lines

All that we need to do is add an entry into the da8xx_pruss_devices structure
and the MFD driver will do the rest to get the UIO probe called.

Well I see the need for MFD for in-kernel PRUSS based drivers like UART/CAN,
but UIO use cases are typically mutually exclusive to this. UIO driver
exports whole PRUSS I/O region and Interrupts to user space application and
all the tasks including PRUSS INTC setup, loading firmware and
enable/disable PRUs are handled by PRUSS user driver.
I feel it will be a nightmare to make them co-exist unless say we have
2 instances of PRUSS in a SOC. Think about kernel driver setting up PRUSS, PINTC and loading firmware and user land overriding and vice versa. Things are much simpler if we make the usage model mutually exclusive. Also I don't see any overlap in functionality and reuse UIO can gain from
MFD driver.
Since we can't register device twice - I would prefer KConfig based check
for the same.

Also can you point me where you add UART and CAN driver as children to PRUSS MFD driver - I can't seem to find this in patches.

Thanks,
Pratheesh

#17 lore raw

PATCH v2 3/13 da850: pruss board specific additions.

In reply to Subhasish Ghosh

This patch adds board specific initializations and setup routines.

---
 arch/arm/mach-davinci/board-da850-evm.c |   25 +++++++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/board-da850-evm.c +25 -0
index 11f986b..242d1ed 100644
@@ -1053,6 +1053,26 @@ static __init int da850_evm_init_cpufreq(void)
 static __init int da850_evm_init_cpufreq(void) { return 0; }
 #endif
 
+static struct da8xx_pruss_devices pruss_devices[] = {
+	{.dev_name = NULL,},
+};
+
+static int __init da8xx_evm_setup_pruss(void)
+{
+	u32 ret = 0;
+	u32 count;
+
+	for (count = 0; count < ARRAY_SIZE(pruss_devices); count++) {
+		if (pruss_devices[count].setup != NULL) {
+			ret = pruss_devices[count].setup();
+			if (ret)
+				return ret;
+		}
+	}
+
+	return da8xx_register_pruss(pruss_devices);
+}
+
 static __init void da850_evm_init(void)
 {
 	int ret;
@@ -1127,6 +1147,11 @@ static __init void da850_evm_init(void)
 
 	da8xx_register_mcasp(0, &da850_evm_snd_data);
 
+	ret = da8xx_evm_setup_pruss();
+	if (ret)
+		pr_warning("%s: pruss initialization failed: %d\n",
+				__func__, ret);
+
 	ret = davinci_cfg_reg_list(da850_lcdcntl_pins);
 	if (ret)
 		pr_warning("da850_evm_init: lcdcntl mux setup failed: %d\n",
Signature · 1 line
1.7.2.3
#18 lore raw

Re: [PATCH v2 03/13] da850: pruss board specific additions.

Quoted 3 lines · Subhasish Ghosh

This patch adds board specific initializations and setup routines.

[...]

arch/arm/mach-davinci/board-da850-evm.c static __init int da850_evm_init_cpufreq(void) · line 1057

Quoted 10 lines
arch/arm/mach-davinci/board-da850-evm.c +2 -0
index 11f986b..242d1ed 100644
@@ -1053,6 +1053,26 @@ static __init int da850_evm_init_cpufreq(void)
 static __init int da850_evm_init_cpufreq(void) { return 0; }
 #endif
 
+static struct da8xx_pruss_devices pruss_devices[] = {
+	{.dev_name = NULL,},

No need to explicitly initialize to NULL.

WBR, Sergei

#19 lore raw

Re: [PATCH v2 03/13] da850: pruss board specific additions.

--------------------------------------------------
From: "Sergei Shtylyov" <[email protected]>
Sent: Saturday, February 12, 2011 12:13 AM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; "Russell King" <[email protected]>; "Kevin Hilman" <[email protected]>; "open list" <[email protected]>; <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 03/13] da850: pruss board specific additions.

Quoted 7 lines
Quoted 3 lines · Subhasish Ghosh

This patch adds board specific initializations and setup routines.

[...]

Quoted 12 lines

arch/arm/mach-davinci/board-da850-evm.c static __init int da850_evm_init_cpufreq(void) · line 1056

Quoted 9 lines
arch/arm/mach-davinci/board-da850-evm.c +1 -0
index 11f986b..242d1ed 100644
@@ -1053,6 +1053,26 @@ static __init int da850_evm_init_cpufreq(void)
 static __init int da850_evm_init_cpufreq(void) { return 0; }
 #endif
 +static struct da8xx_pruss_devices pruss_devices[] = {
+ {.dev_name = NULL,},

No need to explicitly initialize to NULL.

SG - Ok, Will remove

Quoted 2 lines

WBR, Sergei

#20 lore raw

PATCH v2 4/13 mfd: pruss CAN private data.

In reply to Subhasish Ghosh

This patch adds the PRUSS CAN data.

---
 include/linux/mfd/pruss/da8xx_pru.h |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
include/linux/mfd/pruss/da8xx_pru.h +4 -0
index 68d8421..50f2ede 100644
@@ -89,6 +89,10 @@ struct da8xx_pruss_devices {
 	int (*setup)(void);
 };
 
+struct da8xx_pruss_can_data {
+	u32 version;
+};
+
 u32 pruss_get_clk_freq(struct device *dev);
 
 u32 pruss_enable(struct device *dev, u8 pruss_num);
Signature · 1 line
1.7.2.3
#21 lore raw

PATCH v2 5/13 da850: pruss CAN platform specific additions.

In reply to Subhasish Ghosh

This patch adds the necessary pins for the pruss CAN.

---
 arch/arm/mach-davinci/da850.c            |    4 ++++
 arch/arm/mach-davinci/include/mach/mux.h |    4 ++++
 2 files changed, 8 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/da850.c +4 -0
index a098263..4862c28 100644
@@ -565,6 +565,10 @@ static const struct mux_config da850_pins[] = {
 	MUX_CFG(DA850, EMA_CLK,		6,	0,	15,	1,	false)
 	MUX_CFG(DA850, EMA_WAIT_1,	6,	24,	15,	1,	false)
 	MUX_CFG(DA850, NEMA_CS_2,	7,	0,	15,	1,	false)
+	/* PRU functions for soft CAN */
+	MUX_CFG(DA850, PRUSS_PRU0_R31_0,	7,	28,	15,	0,	false)
+	MUX_CFG(DA850, PRUSS_PRU1_R30_15,	12,	0,	15,	4,	false)
+	MUX_CFG(DA850, PRUSS_PRU1_R31_18,	11,	20,	15,	0,	false)
 	/* GPIO function */
 	MUX_CFG(DA850, GPIO2_4,		6,	12,	15,	8,	false)
 	MUX_CFG(DA850, GPIO2_6,		6,	4,	15,	8,	false)
arch/arm/mach-davinci/include/mach/mux.h +4 -0
index 5d4e0fe..10f49f2 100644
@@ -906,6 +906,10 @@ enum davinci_da850_index {
 	DA850_EMA_CLK,
 	DA850_EMA_WAIT_1,
 	DA850_NEMA_CS_2,
+	/* PRU I/O */
+	DA850_PRUSS_PRU0_R31_0,
+	DA850_PRUSS_PRU1_R30_15,
+	DA850_PRUSS_PRU1_R31_18,
 
 	/* GPIO function */
 	DA850_GPIO2_4,
Signature · 1 line
1.7.2.3
#22 lore raw

PATCH v2 6/13 da850: pruss CAN board specific additions.

In reply to Subhasish Ghosh

This patch adds the pruss CAN pin mux and registers the device with the pruss mfd driver.

---
 arch/arm/mach-davinci/board-da850-evm.c |   37 ++++++++++++++++++++++++++++++-
 1 files changed, 36 insertions(+), 1 deletions(-)
arch/arm/mach-davinci/board-da850-evm.c +36 -1
index 242d1ed..2ce5536 100644
@@ -1053,8 +1053,43 @@ static __init int da850_evm_init_cpufreq(void)
 static __init int da850_evm_init_cpufreq(void) { return 0; }
 #endif
 
+const short da850_evm_pruss_can_pins[] = {
+	DA850_PRUSS_PRU0_R31_0, DA850_PRUSS_PRU1_R30_15,
+	DA850_PRUSS_PRU1_R31_18,
+	-1
+};
+
+static int __init da850_evm_setup_pruss_can(void)
+{
+	int ret, val = 0;
+	void __iomem *cfg_chip3_reg;
+
+	ret = davinci_cfg_reg_list(da850_evm_pruss_can_pins);
+	if (ret)
+		pr_warning("%s: da850_evm_pruss_can_pins mux setup "
+					"failed:%d\n", __func__, ret);
+	cfg_chip3_reg = DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG);
+	val = __raw_readl(cfg_chip3_reg);
+	val |= BIT(3);
+	__raw_writel(val, cfg_chip3_reg);
+
+	return ret;
+}
+
+static struct da8xx_pruss_can_data can_data = {
+	.version	= 1,
+};
+
 static struct da8xx_pruss_devices pruss_devices[] = {
-	{.dev_name = NULL,},
+	{
+		.dev_name	= "da8xx_pruss_can",
+		.pdata		= &can_data,
+		.pdata_size	= sizeof(can_data),
+		.setup		= da850_evm_setup_pruss_can,
+	},
+	{
+		.dev_name	= NULL,
+	},
 };
 
 static int __init da8xx_evm_setup_pruss(void)
Signature · 1 line
1.7.2.3
#23 lore raw

Re: [PATCH v2 06/13] da850: pruss CAN board specific additions.

Quoted 4 lines · Subhasish Ghosh

This patch adds the pruss CAN pin mux and registers the device with the pruss mfd driver.

[...]

Quoted 5 lines
arch/arm/mach-davinci/board-da850-evm.c +0 -0
index 242d1ed..2ce5536 100644
@@ -1053,8 +1053,43 @@ static __init int da850_evm_init_cpufreq(void)

[...]

arch/arm/mach-davinci/board-da850-evm.c static __init int da850_evm_init_cpufreq(void) · line 1092

Quoted 11 lines

static struct da8xx_pruss_devices pruss_devices[] = {

arch/arm/mach-davinci/board-da850-evm.c +9 -1
-	{.dev_name = NULL,},
+	{
+		.dev_name	= "da8xx_pruss_can",
+		.pdata		= &can_data,
+		.pdata_size	= sizeof(can_data),
+		.setup		= da850_evm_setup_pruss_can,
+	},
+	{
+		.dev_name	= NULL,
+	},

No need to explicitly initialize to NULL.

WBR, Sergei

#24 lore raw

Re: [PATCH v2 06/13] da850: pruss CAN board specific additions.

--------------------------------------------------
From: "Sergei Shtylyov" <[email protected]>
Sent: Saturday, February 12, 2011 12:15 AM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; "Russell King" <[email protected]>; "Kevin Hilman" <[email protected]>; <[email protected]>; "open list" <[email protected]>; <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 06/13] da850: pruss CAN board specific additions.

Quoted 27 lines
Quoted 4 lines · Subhasish Ghosh

This patch adds the pruss CAN pin mux and registers the device with the pruss mfd driver.

[...]

Quoted 6 lines
arch/arm/mach-davinci/board-da850-evm.c +0 -0
index 242d1ed..2ce5536 100644
@@ -1053,8 +1053,43 @@ static __init int da850_evm_init_cpufreq(void)

[...]

Quoted 11 lines

static struct da8xx_pruss_devices pruss_devices[] = {
- {.dev_name = NULL,},
+ {
+ .dev_name = "da8xx_pruss_can",
+ .pdata = &can_data,
+ .pdata_size = sizeof(can_data),
+ .setup = da850_evm_setup_pruss_can,
+ },
+ {
+ .dev_name = NULL,
+ },

Quoted 1 line

No need to explicitly initialize to NULL.

SG -- ok, Will remove

Quoted 2 lines

WBR, Sergei

#25 lore raw

Re: [PATCH v2 06/13] da850: pruss CAN board specific additions.

In reply to Sergei Shtylyov

--------------------------------------------------
From: "Sergei Shtylyov" <[email protected]>
Sent: Saturday, February 12, 2011 12:15 AM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; "Russell King" <[email protected]>; "Kevin Hilman" <[email protected]>; <[email protected]>; "open list" <[email protected]>; <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 06/13] da850: pruss CAN board specific additions.

Quoted 27 lines
Quoted 4 lines · Subhasish Ghosh

This patch adds the pruss CAN pin mux and registers the device with the pruss mfd driver.

[...]

Quoted 6 lines
arch/arm/mach-davinci/board-da850-evm.c +0 -0
index 242d1ed..2ce5536 100644
@@ -1053,8 +1053,43 @@ static __init int da850_evm_init_cpufreq(void)

[...]

Quoted 11 lines

static struct da8xx_pruss_devices pruss_devices[] = {
- {.dev_name = NULL,},
+ {
+ .dev_name = "da8xx_pruss_can",
+ .pdata = &can_data,
+ .pdata_size = sizeof(can_data),
+ .setup = da850_evm_setup_pruss_can,
+ },
+ {
+ .dev_name = NULL,
+ },

Quoted 1 line

No need to explicitly initialize to NULL.

SG -- ok, Will remove

Quoted 2 lines

WBR, Sergei

#26 lore raw

PATCH v2 7/13 da850: pruss CAN platform specific changes for gpios.

In reply to Subhasish Ghosh

This patch adds the GPIOs for the pruss CAN device.

---
 arch/arm/mach-davinci/da850.c            |    1 +
 arch/arm/mach-davinci/include/mach/mux.h |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/da850.c +1 -0
index 4862c28..a15571d 100644
@@ -570,6 +570,7 @@ static const struct mux_config da850_pins[] = {
 	MUX_CFG(DA850, PRUSS_PRU1_R30_15,	12,	0,	15,	4,	false)
 	MUX_CFG(DA850, PRUSS_PRU1_R31_18,	11,	20,	15,	0,	false)
 	/* GPIO function */
+	MUX_CFG(DA850, GPIO2_0,		6,	28,	15,	8,	false)
 	MUX_CFG(DA850, GPIO2_4,		6,	12,	15,	8,	false)
 	MUX_CFG(DA850, GPIO2_6,		6,	4,	15,	8,	false)
 	MUX_CFG(DA850, GPIO2_8,		5,	28,	15,	8,	false)
arch/arm/mach-davinci/include/mach/mux.h +1 -0
index 10f49f2..85b3a0d 100644
@@ -912,6 +912,7 @@ enum davinci_da850_index {
 	DA850_PRUSS_PRU1_R31_18,
 
 	/* GPIO function */
+	DA850_GPIO2_0,
 	DA850_GPIO2_4,
 	DA850_GPIO2_6,
 	DA850_GPIO2_8,
Signature · 1 line
1.7.2.3
#27 lore raw

Re: [PATCH v2 07/13] da850: pruss CAN platform specific changes for gpios.

Hello.

Quoted 3 lines · Subhasish Ghosh

This patch adds the GPIOs for the pruss CAN device.

I still think this patch should be merged with patch 5, and patch 8 with
patch 6...

WBR, Sergei

#28 lore raw

Re: [PATCH v2 07/13] da850: pruss CAN platform specific changes for gpios.

--------------------------------------------------
From: "Sergei Shtylyov" <[email protected]>
Sent: Saturday, February 12, 2011 12:17 AM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; "Russell King" <[email protected]>; "Kevin Hilman" <[email protected]>; "Thomas Koeller" <[email protected]>; <[email protected]>; "open list" <[email protected]>; "Victor Rodriguez" <[email protected]>; "Cyril Chemparathy" <[email protected]>; <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 07/13] da850: pruss CAN platform specific changes for gpios.

Quoted 11 lines

Hello.

Quoted 3 lines · Subhasish Ghosh

This patch adds the GPIOs for the pruss CAN device.

I still think this patch should be merged with patch 5, and patch 8
with patch 6...

SG -- Ok, Will do.

Quoted 1 line

WBR, Sergei

#29 lore raw

PATCH v2 8/13 da850: pruss CAN board specific changes for gpios.

In reply to Subhasish Ghosh

This patch adds the GPIO modifications required for the pruss CAN.

---
 arch/arm/mach-davinci/board-da850-evm.c |   11 ++++++++++-
 1 files changed, 10 insertions(+), 1 deletions(-)
arch/arm/mach-davinci/board-da850-evm.c +10 -1
index 2ce5536..f9c38f8 100644
@@ -45,6 +45,7 @@
 
 #define DA850_MMCSD_CD_PIN		GPIO_TO_PIN(4, 0)
 #define DA850_MMCSD_WP_PIN		GPIO_TO_PIN(4, 1)
+#define DA850_PRUSS_CAN_TRX_PIN	GPIO_TO_PIN(2, 0)
 
 #define DA850_MII_MDIO_CLKEN_PIN	GPIO_TO_PIN(2, 6)
 
@@ -1055,7 +1056,7 @@ static __init int da850_evm_init_cpufreq(void) { return 0; }
 
 const short da850_evm_pruss_can_pins[] = {
 	DA850_PRUSS_PRU0_R31_0, DA850_PRUSS_PRU1_R30_15,
-	DA850_PRUSS_PRU1_R31_18,
+	DA850_PRUSS_PRU1_R31_18, DA850_GPIO2_0,
 	-1
 };
 
@@ -1073,6 +1074,14 @@ static int __init da850_evm_setup_pruss_can(void)
 	val |= BIT(3);
 	__raw_writel(val, cfg_chip3_reg);
 
+	/* value = 0 to enable the CAN transceiver */
+	ret = gpio_request_one(DA850_PRUSS_CAN_TRX_PIN,
+					GPIOF_OUT_INIT_LOW, "pruss_can_en");
+	if (ret) {
+		pr_warning("Cannot setup GPIO %d\n", DA850_PRUSS_CAN_TRX_PIN);
+		gpio_free(DA850_PRUSS_CAN_TRX_PIN);
+	}
+
 	return ret;
 }
 
Signature · 1 line
1.7.2.3
#30 lore raw

PATCH v2 9/13 can: pruss CAN driver.

In reply to Subhasish Ghosh

This patch adds support for the CAN device emulated on PRUSS.

---
 drivers/net/can/Kconfig                     |    1 +
 drivers/net/can/Makefile                    |    1 +
 drivers/net/can/da8xx_pruss/Kconfig         |   73 ++
 drivers/net/can/da8xx_pruss/Makefile        |    7 +
 drivers/net/can/da8xx_pruss/pruss_can.c     |  758 +++++++++++++++++
 drivers/net/can/da8xx_pruss/pruss_can_api.c | 1227 +++++++++++++++++++++++++++
 drivers/net/can/da8xx_pruss/pruss_can_api.h |  290 +++++++
 7 files changed, 2357 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/da8xx_pruss/Kconfig
 create mode 100644 drivers/net/can/da8xx_pruss/Makefile
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can.c
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.c
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.h
drivers/net/can/Kconfig +1 -0
index d5a9db6..ae8f0f9 100644
@@ -112,6 +112,7 @@ config PCH_CAN
 	  This driver can access CAN bus.
 
 source "drivers/net/can/mscan/Kconfig"
+source "drivers/net/can/da8xx_pruss/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
 
drivers/net/can/Makefile +1 -0
index 07ca159..849cdbf 100644
@@ -14,6 +14,7 @@ obj-$(CONFIG_CAN_SJA1000)	+= sja1000/
 obj-$(CONFIG_CAN_MSCAN)		+= mscan/
 obj-$(CONFIG_CAN_AT91)		+= at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
+obj-$(CONFIG_CAN_TI_DA8XX_PRU)	+= da8xx_pruss/
 obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
 obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
 obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
drivers/net/can/da8xx_pruss/Kconfig new file +73 -0

73 lines not on this page · raw message

drivers/net/can/da8xx_pruss/Makefile new file +7 -0
new file mode 100644
index 0000000..48f3055
@@ -0,0 +1,7 @@
+#
+# Makefile for CAN Lite emulation
+#
+can_emu-objs :=   pruss_can.o \
+                  pruss_can_api.o
+
+obj-$(CONFIG_CAN_TI_DA8XX_PRU)    += can_emu.o
drivers/net/can/da8xx_pruss/pruss_can.c new file +758 -0

758 lines not on this page · raw message

drivers/net/can/da8xx_pruss/pruss_can_api.c new file +1227 -0

1,227 lines not on this page · raw message

drivers/net/can/da8xx_pruss/pruss_can_api.h new file +290 -0

290 lines not on this page · raw message

Signature · 1 line
1.7.2.3
#31 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 53 lines · Subhasish Ghosh
drivers/net/can/da8xx_pruss/Kconfig new file +53 -0
+config DA8XX_PRU_CANID_MBX0
+	hex "CANID for mailbox 0"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 0
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX1
+	hex "CANID for mailbox 1"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	 ---help---
+	Enter the CANID for mailbox 1
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX2
+	hex "CANID for mailbox 2"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 2
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX3
+	hex "CANID for mailbox 3"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 3
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX4
+	hex "CANID for mailbox 4"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 4
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX5
+	hex "CANID for mailbox 5"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 5
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX6
+	hex "CANID for mailbox 6"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---

drivers/net/can/da8xx_pruss/Kconfig line 73

Quoted 10 lines
drivers/net/can/da8xx_pruss/Kconfig new file +10 -0
+	Enter the CANID for mailbox 6
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX7
+	hex "CANID for mailbox 7"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 7
+	Default value is set to 0x123, change this as required.

Why is filling the mailboxes the job of kernel config?

Regards,
Kurt

#32 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Hello,

I had a discussion regarding this with Wolfgang:

http://www.mail-archive.com/[email protected]/msg00324.html

The problem here is that we must configure the mailbox ID's and this support is not available in the socketCan sub-system.

--------------------------------------------------
From: "Kurt Van Dijck" <[email protected]>
Sent: Friday, February 11, 2011 8:36 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "Wolfgang Grandegger" <[email protected]>; "open list:CAN NETWORK DRIVERS" <[email protected]>; "open list:CAN NETWORK DRIVERS" <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 68 lines · Subhasish Ghosh
Quoted 53 lines
drivers/net/can/da8xx_pruss/Kconfig new file +53 -0
+config DA8XX_PRU_CANID_MBX0
+ hex "CANID for mailbox 0"
+ depends on CAN_TI_DA8XX_PRU
+ default "0x123"
+ ---help---
+ Enter the CANID for mailbox 0
+ Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX1
+ hex "CANID for mailbox 1"
+ depends on CAN_TI_DA8XX_PRU
+ default "0x123"
+ ---help---
+ Enter the CANID for mailbox 1
+ Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX2
+ hex "CANID for mailbox 2"
+ depends on CAN_TI_DA8XX_PRU
+ default "0x123"
+ ---help---
+ Enter the CANID for mailbox 2
+ Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX3
+ hex "CANID for mailbox 3"
+ depends on CAN_TI_DA8XX_PRU
+ default "0x123"
+ ---help---
+ Enter the CANID for mailbox 3
+ Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX4
+ hex "CANID for mailbox 4"
+ depends on CAN_TI_DA8XX_PRU
+ default "0x123"
+ ---help---
+ Enter the CANID for mailbox 4
+ Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX5
+ hex "CANID for mailbox 5"
+ depends on CAN_TI_DA8XX_PRU
+ default "0x123"
+ ---help---
+ Enter the CANID for mailbox 5
+ Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX6
+ hex "CANID for mailbox 6"
+ depends on CAN_TI_DA8XX_PRU
+ default "0x123"
+ ---help---

drivers/net/can/da8xx_pruss/Kconfig line 73

Quoted 10 lines
drivers/net/can/da8xx_pruss/Kconfig new file +10 -0
+ Enter the CANID for mailbox 6
+ Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX7
+ hex "CANID for mailbox 7"
+ depends on CAN_TI_DA8XX_PRU
+ default "0x123"
+ ---help---
+ Enter the CANID for mailbox 7
+ Default value is set to 0x123, change this as required.

Why is filling the mailboxes the job of kernel config?

Regards,
Kurt

#33 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 8 lines · Subhasish Ghosh

Hello,

I had a discussion regarding this with Wolfgang:

http://www.mail-archive.com/[email protected]/msg00324.html

The problem here is that we must configure the mailbox ID's and this support is not available in the socketCan sub-system.

To understand you correctly. A mailbox (or message object) can *only* receive messages with the pre-programmed CAN id? Isn't there a chance to receive all or a range of CAN ids? That's a very unusual piece of hardware. Anyway, using kernel configuration parameters to define the CAN id's would be the less flexible method. The user will not have a chance to change them at run-time. Using SysFS files would already be much better.

Wolfgang.

A message that is not in the archive (sent off-list or not yet received): [email protected]

#35 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 11 lines · Wolfgang Grandegger
Quoted 8 lines · Subhasish Ghosh

Hello,

I had a discussion regarding this with Wolfgang:

http://www.mail-archive.com/socketcan-users at lists.berlios.de/msg00324.html

The problem here is that we must configure the mailbox ID's and this support is not available in the socketCan sub-system.

Using SysFS files would already be much better.

Ack.

Kurt

#36 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

In reply to Wolfgang Grandegger

That is correct, we receive only pre-programmed CAN ids and "all" or "range" implementation is not there in the PRU firmware. Will check the sysfs option and update.

--------------------------------------------------
From: "Wolfgang Grandegger" <[email protected]>
Sent: Monday, February 14, 2011 12:53 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: "Kurt Van Dijck" <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "open list:CAN NETWORK DRIVERS" <[email protected]>; "open list:CAN NETWORK DRIVERS" <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 19 lines · Subhasish Ghosh
Quoted 8 lines

Hello,

I had a discussion regarding this with Wolfgang:

http://www.mail-archive.com/[email protected]/msg00324.html

The problem here is that we must configure the mailbox ID's and this support is not available in the socketCan sub-system.

To understand you correctly. A mailbox (or message object) can *only* receive messages with the pre-programmed CAN id? Isn't there a chance to receive all or a range of CAN ids? That's a very unusual piece of hardware. Anyway, using kernel configuration parameters to define the CAN id's would be the less flexible method. The user will not have a chance to change them at run-time. Using SysFS files would already be much better.

Wolfgang.

#37 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Hi Subhasish,

Quoted 2 lines · Subhasish Ghosh

That is correct, we receive only pre-programmed CAN ids and "all" or "range" implementation is not there in the PRU firmware.

I'm curious about that CAN hardware and firmware. I found a nice block diagram in [PATCH 0/13]:

http://marc.info/?l=linux-arm-kernel&m=129743511311286&w=4

So, one PRU is used for TX and the second for RX. Who is providing the firmware you are using? Wouldn't it be possible to provide a firmware for RX using just one message object (mailbox) with some buffering or fifo? That would fit much better the SocketCAN approach favoring Basic CAN controllers (in contrast to Full CAN [1]). And such an implementation seems even simpler too me requiring less PRU resources. And how about RTR and Extended CAN IDs? Is that supported?

Thanks,

Wolfgang.

[1] http://www.kvaser.com/en/about-can/the-can-protocol/18.html

#38 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

In reply to Subhasish Ghosh

Hello,

Quoted 2 lines · Subhasish Ghosh

That is correct, we receive only pre-programmed CAN ids and "all" or "range" implementation is not there in the PRU firmware.

I'd really like to see that you add a "all" implementation to the firmware. Or even better use the standard id/mask approach.

cheers, Marc

Signature · 4 lines
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

_______________________________________________
Socketcan-core mailing list [email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

#39 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Hello,

The problem with the "all" implementation is that it hogs the ARM/DSP heavily and that's the reason why we specifically avoided this in our firmware design. Hence, implementing this condition spoils the whole purpose of the PRU!!

--------------------------------------------------
From: "Marc Kleine-Budde" <[email protected]>
Sent: Monday, February 14, 2011 3:05 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: "Wolfgang Grandegger" <[email protected]>; "Kurt Van Dijck" <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "open list:CAN NETWORK DRIVERS" <[email protected]>; "open list:CAN NETWORK DRIVERS" <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 09/13] can: pruss CAN driver.

Hello,

Quoted 2 lines · Subhasish Ghosh

That is correct, we receive only pre-programmed CAN ids and "all" or "range" implementation is not there in the PRU firmware.

I'd really like to see that you add a "all" implementation to the firmware. Or even better use the standard id/mask approach.

cheers, Marc

Signature · 4 lines
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   | 
#40 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 6 lines · Subhasish Ghosh

Hello,

The problem with the "all" implementation is that it hogs the ARM/DSP heavily and that's the reason why we specifically avoided this in our firmware design.
Hence, implementing this condition spoils the whole purpose of the PRU!!

What about implementing the standard id/mask approach?

if (canid & mask == id & mask)

	aceept();
else
	discard();

To keep the hot-path as small as possible, the id & mask operation is done during setup, only one. This is probably just an additional "and" operation (the "& mask"). This opens the way to act like a normal can controller.

As long as we don't have any support for hardware filters in socketcan, it's a good choice to use sysfs to configure your filters.

Have a look at [1] and [2] for how to use sysfs files.

cheers, Marc

[1]
http://git.kernel.org/linus/3a5655a5b545e9647c3437473ee3d815fe1b9050

[2]
http://git.kernel.org/linus/fef52b0171dfd7dd9b85c9cc201bd433b42a8ded

Signature · 4 lines
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

_______________________________________________
Socketcan-core mailing list [email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

#41 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

In reply to Subhasish Ghosh
Quoted 6 lines · Subhasish Ghosh

Hello,

The problem with the "all" implementation is that it hogs the ARM/DSP heavily and that's the reason why we specifically avoided this in our firmware design.
Hence, implementing this condition spoils the whole purpose of the PRU!!

Well, I doubt that a CAN controller just supporting 8 CAN identifiers will make many CAN users happy. Anyway, the CAN identifiers could/should be configured via SysFS files (as Marc suggested).

Wolfgang.

Quoted 25 lines

--------------------------------------------------
From: "Marc Kleine-Budde" <[email protected]>
Sent: Monday, February 14, 2011 3:05 PM
To: "Subhasish Ghosh" <subhasish-EvXpCiN+lbve9wHmmfpqLFaTQe2KTcn/@public.gmane.org>
Cc: "Wolfgang Grandegger" <[email protected]>; "Kurt Van Dijck" <kurt.van.dijck-/[email protected]>; <davinci-linux-open-source-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/@public.gmane.org>; <linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org>; <[email protected]>; <[email protected]>; <sachi-EvXpCiN+lbve9wHmmfpqLFaTQe2KTcn/@public.gmane.org>; "open list:CAN NETWORK DRIVERS" <[email protected]>; "open list:CAN NETWORK
DRIVERS" <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 09/13] can: pruss CAN driver.

Hello,

Quoted 2 lines · Subhasish Ghosh

That is correct, we receive only pre-programmed CAN ids and "all" or "range" implementation is not there in the PRU firmware.

I'd really like to see that you add a "all" implementation to the firmware. Or even better use the standard id/mask approach.

cheers, Marc

#42 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

In reply to Subhasish Ghosh

Hi,

I looked a bit at the TX path:

drivers/net/can/da8xx_pruss/pruss_can.c line 124

Quoted 9 lines · Subhasish Ghosh
drivers/net/can/da8xx_pruss/pruss_can.c new file +9 -0
+static int omapl_pru_can_set_bittiming(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct can_bittiming *bt = &priv->can.bittiming;
+	long bit_error = 0;
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) {
+		dev_warn(priv->dev, "WARN: Triple"
+			 "sampling not set due to h/w limitations");

You should not have enabled CAN_CTRLMODE_3_SAMPLES in the first place?

Quoted 8 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +8 -0
+	}
+	if (pru_can_calc_timing(priv->dev, priv->can.clock.freq,
+				bt->bitrate) != 0)
+		return -EINVAL;
+	bit_error =
+	    (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) -
+	      bt->bitrate) * 1000) / bt->bitrate;
+	if (bit_error) {

drivers/net/can/da8xx_pruss/pruss_can.c line 142

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+		bit_error =
+		    (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) -
+		      bt->bitrate) * 1000000) / bt->bitrate;
+		printk(KERN_INFO "\nBitrate error %ld.%ld%%\n",
+			bit_error / 10000, bit_error % 1000);
+	} else
+		printk(KERN_INFO "\nBitrate error 0.0%%\n");
+
+	return 0;
+}

I wonder how much of this code is duplicated from drivers/net/can/dev.c ?

drivers/net/can/da8xx_pruss/pruss_can.c line 210

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+static netdev_tx_t omapl_pru_can_start_xmit(struct sk_buff *skb,
+					    struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	int count;
+	u8 *data = cf->data;
+	u8 dlc = cf->can_dlc;
+	u8 *ptr8data = NULL;
+

most drivers start with:

	if (can_dropped_invalid_skb(dev, skb))
		return NETDEV_TX_OK;

Review

Quoted 1 line
(quoted diff) +1 -0
+	netif_stop_queue(ndev);

why would you stop when you just resumed the queue?

Quoted 21 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +21 -0
+	if (cf->can_id & CAN_EFF_FLAG)	/* Extended frame format */
+		*((u32 *) &priv->can_tx_hndl.strcanmailbox) =
+		    (cf->can_id & CAN_EFF_MASK) | PRU_CANMID_IDE;
+	else			/* Standard frame format */
+		*((u32 *) &priv->can_tx_hndl.strcanmailbox) =
+		    (cf->can_id & CAN_SFF_MASK) << 18;
+
+	if (cf->can_id & CAN_RTR_FLAG)	/* Remote transmission request */
+		*((u32 *) &priv->can_tx_hndl.strcanmailbox) |= CAN_RTR_FLAG;
+
+	ptr8data = &priv->can_tx_hndl.strcanmailbox.u8data7 + (dlc - 1);
+	for (count = 0; count < (u8) dlc; count++) {
+		*ptr8data-- = *data++;
+	}
+	*((u32 *) &priv->can_tx_hndl.strcanmailbox.u16datalength) = (u32) dlc;
+/*
+ * search for the next available mbx
+ * if the next mbx is busy, then try the next + 1
+ * do this until the head is reached.
+ * if still unable to tx, stop accepting any packets
+ * if able to tx and the head is reached, then reset next to tail, i.e mbx0

drivers/net/can/da8xx_pruss/pruss_can.c line 242

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+ * if head is not reached, then just point to the next mbx
+ */
+	for (; priv->tx_next <= priv->tx_head; priv->tx_next++) {
+		priv->can_tx_hndl.ecanmailboxnumber =
+		    (can_mailbox_number) priv->tx_next;
+		if (-1 == pru_can_write_data_to_mailbox(priv->dev,
+					&priv->can_tx_hndl)) {
+			if (priv->tx_next == priv->tx_head) {
+				priv->tx_next = priv->tx_tail;
+				if (!netif_queue_stopped(ndev))

If you get here, the queue is not stopped. This test is therefore useless.

Quoted 54 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +53 -0
+					netif_stop_queue(ndev);	/* IF stalled */
+				dev_err(priv->dev,
+					"%s: no tx mbx available", __func__);
+				return NETDEV_TX_BUSY;
+			} else
+				continue;
+		} else {
+			/* set transmit request */
+			pru_can_tx(priv->dev, priv->tx_next, CAN_TX_PRU_1);
+			pru_can_tx_mode_set(priv->dev, false, ecanreceive);
+			pru_can_tx_mode_set(priv->dev, true, ecantransmit);
+			pru_can_start_abort_tx(priv->dev, PRU_CAN_START);
+			priv->tx_next++;
+			can_put_echo_skb(skb, ndev, 0);
+			break;
+		}
+	}
+	if (priv->tx_next > priv->tx_head) {
+		priv->tx_next = priv->tx_tail;
+	}
+	return NETDEV_TX_OK;
+}
+
+
 
+irqreturn_t omapl_tx_can_intr(int irq, void *dev_id)
+{
+	struct net_device *ndev = dev_id;
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	u32 bit_set, mbxno;
+
+	pru_can_get_intr_status(priv->dev, &priv->can_tx_hndl);
+	if ((PRU_CAN_ISR_BIT_CCI & priv->can_tx_hndl.u32interruptstatus)
+	    || (PRU_CAN_ISR_BIT_SRDI & priv->can_tx_hndl.u32interruptstatus)) {
+		__can_debug("tx_int_status = 0x%X\n",
+			    priv->can_tx_hndl.u32interruptstatus);
+		can_free_echo_skb(ndev, 0);
+	} else {
+		for (bit_set = 0; ((priv->can_tx_hndl.u32interruptstatus & 0xFF)
+						>> bit_set != 0); bit_set++)
+		;
+		if (0 == bit_set) {
+			__can_err("%s: invalid mailbox number\n", __func__);
+			can_free_echo_skb(ndev, 0);
+		} else {
+			mbxno = bit_set - 1;	/* mail box numbering starts from 0 */
+			if (PRU_CAN_ISR_BIT_ESI & priv->can_tx_hndl.
+			    u32interruptstatus) {
+				/* read gsr and ack pru */
+				pru_can_get_global_status(priv->dev, &priv->can_tx_hndl);
+				omapl_pru_can_err(ndev,
+						  priv->can_tx_hndl.
+						  u32interruptstatus,

drivers/net/can/da8xx_pruss/pruss_can.c line 461

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+						  priv->can_tx_hndl.
+						  u32globalstatus);
+			} else {
+				stats->tx_packets++;
+				/* stats->tx_bytes += dlc; */
+				/*can_get_echo_skb(ndev, 0);*/
+			}
+		}
+	}
+	if (netif_queue_stopped(ndev))

you can call netif_wake_queue(ndev) multiple times, so there is no need for netif_queue_stopped()

Quoted 5 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +5 -0
+		netif_wake_queue(ndev);
+
+	can_get_echo_skb(ndev, 0);
+	pru_can_tx_mode_set(priv->dev, true, ecanreceive);
+	return IRQ_HANDLED;

drivers/net/can/da8xx_pruss/pruss_can.c line 494

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+}
+
+static int omapl_pru_can_open(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	int err;
+
+	/* register interrupt handler */
+	err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED,
+			  "pru_can_irq", ndev);

you're doing a lot of work _in_ the irq handler. Maybe threaded irq?

drivers/net/can/da8xx_pruss/pruss_can.c line 533

Quoted 5 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +5 -0
+static int omapl_pru_can_close(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+
+	if (!netif_queue_stopped(ndev))

check is not needed.

drivers/net/can/da8xx_pruss/pruss_can.c line 541

Quoted 8 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +8 -0
+		netif_stop_queue(ndev);
+
+	close_candev(ndev);
+
+	free_irq(priv->trx_irq, ndev);
+	return 0;
+}
+

Regards,
Kurt

#43 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

--------------------------------------------------
From: "Kurt Van Dijck" <[email protected]>
Sent: Friday, February 11, 2011 8:50 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "Wolfgang Grandegger" <[email protected]>; "open list:CAN NETWORK DRIVERS" <[email protected]>; "open list:CAN NETWORK DRIVERS" <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 5 lines

Hi,

I looked a bit at the TX path:

On Fri, Feb 11, 2011 at 08:21:28PM +0530, Subhasish Ghosh wrote:

Quoted 10 lines

drivers/net/can/da8xx_pruss/pruss_can.c line 124

Quoted 9 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +9 -0
+static int omapl_pru_can_set_bittiming(struct net_device *ndev)
+{
+ struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ long bit_error = 0;
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) {
+ dev_warn(priv->dev, "WARN: Triple"
+ "sampling not set due to h/w limitations");

You should not have enabled CAN_CTRLMODE_3_SAMPLES in the first place?

SG - Ok Will remove.

Quoted 11 lines
Quoted 11 lines

+ }
+ if (pru_can_calc_timing(priv->dev, priv->can.clock.freq,
+ bt->bitrate) != 0)
+ return -EINVAL;
+ bit_error =

drivers/net/can/da8xx_pruss/pruss_can.c new file +6 -0
+     (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) -
+       bt->bitrate) * 1000) / bt->bitrate;
+ if (bit_error) {
+ bit_error =
+     (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) -
+       bt->bitrate) * 1000000) / bt->bitrate;
Quoted 8 lines

drivers/net/can/da8xx_pruss/pruss_can.c line 114

Quoted 7 lines

+ printk(KERN_INFO "\nBitrate error %ld.%ld%%\n",
+ bit_error / 10000, bit_error % 1000);
+ } else
+ printk(KERN_INFO "\nBitrate error 0.0%%\n");

drivers/net/can/da8xx_pruss/pruss_can.c new file +3 -0
+
+ return 0;
+}

I wonder how much of this code is duplicated from drivers/net/can/dev.c ?

SG - Well, I just followed ti_hecc.c :-)

Quoted 14 lines

drivers/net/can/da8xx_pruss/pruss_can.c line 210

Quoted 11 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+static netdev_tx_t omapl_pru_can_start_xmit(struct sk_buff *skb,
+     struct net_device *ndev)
+{
+ struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ int count;
+ u8 *data = cf->data;
+ u8 dlc = cf->can_dlc;
+ u8 *ptr8data = NULL;
+

most drivers start with:
if (can_dropped_invalid_skb(dev, skb))
return NETDEV_TX_OK;

SG - Will do.

Quoted 3 lines
Quoted 2 lines

+ netif_stop_queue(ndev);

why would you stop when you just resumed the queue?

SG - I do not want more than one transmit request at one time. Hence, on entering the transmit I am using netif_stop_queue to disable tx.

Quoted 25 lines
Quoted 25 lines

+ if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */
+ *((u32 *) &priv->can_tx_hndl.strcanmailbox) =

drivers/net/can/da8xx_pruss/pruss_can.c new file +19 -0
+     (cf->can_id & CAN_EFF_MASK) | PRU_CANMID_IDE;
+ else /* Standard frame format */
+ *((u32 *) &priv->can_tx_hndl.strcanmailbox) =
+     (cf->can_id & CAN_SFF_MASK) << 18;
+
+ if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */
+ *((u32 *) &priv->can_tx_hndl.strcanmailbox) |= CAN_RTR_FLAG;
+
+ ptr8data = &priv->can_tx_hndl.strcanmailbox.u8data7 + (dlc - 1);
+ for (count = 0; count < (u8) dlc; count++) {
+ *ptr8data-- = *data++;
+ }
+ *((u32 *) &priv->can_tx_hndl.strcanmailbox.u16datalength) = (u32) dlc;
+/*
+ * search for the next available mbx
+ * if the next mbx is busy, then try the next + 1
+ * do this until the head is reached.
+ * if still unable to tx, stop accepting any packets
+ * if able to tx and the head is reached, then reset next to tail, i.e 

mbx0
+ * if head is not reached, then just point to the next mbx
+ */
+ for (; priv->tx_next <= priv->tx_head; priv->tx_next++) {

Quoted 8 lines
Quoted 7 lines

+ priv->can_tx_hndl.ecanmailboxnumber =
+ (can_mailbox_number) priv->tx_next;
+ if (-1 == pru_can_write_data_to_mailbox(priv->dev,
+ &priv->can_tx_hndl)) {
+ if (priv->tx_next == priv->tx_head) {
+ priv->tx_next = priv->tx_tail;
+ if (!netif_queue_stopped(ndev))

If you get here, the queue is not stopped. This test is therefore useless.

SG -Ok, will remove

Quoted 58 lines
Quoted 58 lines

+ netif_stop_queue(ndev); /* IF stalled */
+ dev_err(priv->dev,
+ "%s: no tx mbx available", __func__);
+ return NETDEV_TX_BUSY;
+ } else
+ continue;
+ } else {
+ /* set transmit request */
+ pru_can_tx(priv->dev, priv->tx_next, CAN_TX_PRU_1);
+ pru_can_tx_mode_set(priv->dev, false, ecanreceive);
+ pru_can_tx_mode_set(priv->dev, true, ecantransmit);
+ pru_can_start_abort_tx(priv->dev, PRU_CAN_START);
+ priv->tx_next++;
+ can_put_echo_skb(skb, ndev, 0);
+ break;
+ }
+ }
+ if (priv->tx_next > priv->tx_head) {
+ priv->tx_next = priv->tx_tail;
+ }
+ return NETDEV_TX_OK;

drivers/net/can/da8xx_pruss/pruss_can.c new file +36 -0
+}
+
+
 
+irqreturn_t omapl_tx_can_intr(int irq, void *dev_id)
+{
+ struct net_device *ndev = dev_id;
+ struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ u32 bit_set, mbxno;
+
+ pru_can_get_intr_status(priv->dev, &priv->can_tx_hndl);
+ if ((PRU_CAN_ISR_BIT_CCI & priv->can_tx_hndl.u32interruptstatus)
+     || (PRU_CAN_ISR_BIT_SRDI & priv->can_tx_hndl.u32interruptstatus)) {
+ __can_debug("tx_int_status = 0x%X\n",
+     priv->can_tx_hndl.u32interruptstatus);
+ can_free_echo_skb(ndev, 0);
+ } else {
+ for (bit_set = 0; ((priv->can_tx_hndl.u32interruptstatus & 0xFF)
+ >> bit_set != 0); bit_set++)
+ ;
+ if (0 == bit_set) {
+ __can_err("%s: invalid mailbox number\n", __func__);
+ can_free_echo_skb(ndev, 0);
+ } else {
+ mbxno = bit_set - 1; /* mail box numbering starts from 0 */
+ if (PRU_CAN_ISR_BIT_ESI & priv->can_tx_hndl.
+     u32interruptstatus) {
+ /* read gsr and ack pru */
+ pru_can_get_global_status(priv->dev, &priv->can_tx_hndl);
+ omapl_pru_can_err(ndev,
+   priv->can_tx_hndl.
+   u32interruptstatus,
+   priv->can_tx_hndl.
+   u32globalstatus);
+ } else {
+ stats->tx_packets++;
Quoted 8 lines
Quoted 6 lines

+ /* stats->tx_bytes += dlc; */
+ /*can_get_echo_skb(ndev, 0);*/
+ }
+ }
+ }
+ if (netif_queue_stopped(ndev))

you can call netif_wake_queue(ndev) multiple times, so there is no need for netif_queue_stopped()

SG -Ok, will remove

Quoted 8 lines
Quoted 8 lines

+ netif_wake_queue(ndev);

drivers/net/can/da8xx_pruss/pruss_can.c new file +7 -0
+
+ can_get_echo_skb(ndev, 0);
+ pru_can_tx_mode_set(priv->dev, true, ecanreceive);
+ return IRQ_HANDLED;
+}
+
+static int omapl_pru_can_open(struct net_device *ndev)
Quoted 9 lines

drivers/net/can/da8xx_pruss/pruss_can.c line 494

Quoted 7 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +7 -0
+{
+ struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+ int err;
+
+ /* register interrupt handler */
+ err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED,
+   "pru_can_irq", ndev);

you're doing a lot of work _in_ the irq handler. Maybe threaded irq?

SG -Ok, will do

Quoted 6 lines

drivers/net/can/da8xx_pruss/pruss_can.c line 533

Quoted 5 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +5 -0
+static int omapl_pru_can_close(struct net_device *ndev)
+{
+ struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+
+ if (!netif_queue_stopped(ndev))

check is not needed.

SG -Ok, will remove

Quoted 11 lines

drivers/net/can/da8xx_pruss/pruss_can.c line 541

Quoted 8 lines

+ netif_stop_queue(ndev);

drivers/net/can/da8xx_pruss/pruss_can.c new file +7 -0
+
+ close_candev(ndev);
+
+ free_irq(priv->trx_irq, ndev);
+ return 0;
+}
+

Regards,
Kurt

#44 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 2 lines · Subhasish Ghosh

--------------------------------------------------
From: "Kurt Van Dijck" <kurt.van.dijck-/[email protected]>

...

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

+ /* register interrupt handler */
+ err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED,
+ "pru_can_irq", ndev);

you're doing a lot of work _in_ the irq handler. Maybe threaded irq?

SG -Ok, will do

No, please use NAPI instead.

Wolfgang

#45 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 5 lines · Subhasish Ghosh
Quoted 2 lines

--------------------------------------------------
From: "Kurt Van Dijck" <[email protected]>

...

Quoted 8 lines
Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

+ /* register interrupt handler */
+ err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED,
+ "pru_can_irq", ndev);

you're doing a lot of work _in_ the irq handler. Maybe threaded irq?

SG -Ok, will do

No, please use NAPI instead.

We are using h/w filters, so the number of interrupts coming into the processor are not hogging it. I feel that we may not require an interrupt mitigation.

-Subhasish

#46 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Quoted 10 lines · Subhasish Ghosh
Quoted 10 lines · Subhasish Ghosh
Quoted 2 lines

--------------------------------------------------
From: "Kurt Van Dijck" <[email protected]>

...

Quoted 5 lines
Quoted 5 lines
Quoted 3 lines

+ /* register interrupt handler */
+ err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED,
+ "pru_can_irq", ndev);

you're doing a lot of work _in_ the irq handler. Maybe threaded irq?

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

SG -Ok, will do

No, please use NAPI instead.

We are using h/w filters, so the number of interrupts coming into the processor are not hogging it.
I feel that we may not require an interrupt mitigation.

As davem stated the other day, all new drivers should use NAPI.

regards, Marc

Signature · 12 lines
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 262 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110218/ce017071/attachment.sig>
#47 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Ok, will do NAPI

Quoted 10 lines · Subhasish Ghosh
Quoted 10 lines · Subhasish Ghosh
Quoted 2 lines

--------------------------------------------------
From: "Kurt Van Dijck" <[email protected]>

...

Quoted 5 lines
Quoted 5 lines
Quoted 3 lines

+ /* register interrupt handler */
+ err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED,
+ "pru_can_irq", ndev);

you're doing a lot of work _in_ the irq handler. Maybe threaded irq?

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

SG -Ok, will do

No, please use NAPI instead.

We are using h/w filters, so the number of interrupts coming into the processor are not hogging it.
I feel that we may not require an interrupt mitigation.

As davem stated the other day, all new drivers should use NAPI.

regards, Marc

Signature · 3 lines
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 | 

A message that is not in the archive (sent off-list or not yet received): 1297435892-28278-10-git-send-email-subhasish-EvXpCiN+lbve9wHmmfpqLFaTQe2KTcn/@public.gmane.org

#49 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Hello,

thanks for your contribution.

First some general comments:

- Please send a separate patch for the Socket-CAN driver.
Or are there good reasons why we should look at the other 12 patches?

- Please run checkpatch.pl and fix the reported warnings. There are many.

- You are using casts extensively. Please get rid of them.

- Decoding the variable type into the name is *deprecated* in Linux.

- Also very long names are *deprecated* in Linux.

- Don't use typedef's.

Some more comments inline...

Quoted 17 lines · Subhasish Ghosh

This patch adds support for the CAN device emulated on PRUSS.

---
 drivers/net/can/Kconfig                     |    1 +
 drivers/net/can/Makefile                    |    1 +
 drivers/net/can/da8xx_pruss/Kconfig         |   73 ++
 drivers/net/can/da8xx_pruss/Makefile        |    7 +
 drivers/net/can/da8xx_pruss/pruss_can.c     |  758 +++++++++++++++++
 drivers/net/can/da8xx_pruss/pruss_can_api.c | 1227 +++++++++++++++++++++++++++
 drivers/net/can/da8xx_pruss/pruss_can_api.h |  290 +++++++
 7 files changed, 2357 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/da8xx_pruss/Kconfig
 create mode 100644 drivers/net/can/da8xx_pruss/Makefile
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can.c
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.c
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.h

Why not s/da8xx_pruss/pruss_can/ ?

Quoted 17 lines
drivers/net/can/Kconfig +1 -0
index d5a9db6..ae8f0f9 100644
@@ -112,6 +112,7 @@ config PCH_CAN
 	  This driver can access CAN bus.
 
 source "drivers/net/can/mscan/Kconfig"
+source "drivers/net/can/da8xx_pruss/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
 
drivers/net/can/Makefile +0 -0
index 07ca159..849cdbf 100644

drivers/net/can/Makefile obj-$(CONFIG_CAN_SJA1000) += sja1000/ · line 17

Quoted 4 lines
drivers/net/can/Makefile +1 -0
@@ -14,6 +14,7 @@ obj-$(CONFIG_CAN_SJA1000)	+= sja1000/
 obj-$(CONFIG_CAN_MSCAN)		+= mscan/
 obj-$(CONFIG_CAN_AT91)		+= at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
+obj-$(CONFIG_CAN_TI_DA8XX_PRU)	+= da8xx_pruss/

Please use a common name/prefix, e.g. pruss_can.

Quoted 72 lines
drivers/net/can/Makefile +0 -0
 obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
 obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
 obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
drivers/net/can/da8xx_pruss/Kconfig new file +63 -0
new file mode 100644
index 0000000..8b68f68
@@ -0,0 +1,73 @@
+#
+# CAN Lite Kernel Configuration
+#
+config CAN_TI_DA8XX_PRU
+	depends on CAN_DEV && ARCH_DAVINCI && ARCH_DAVINCI_DA850
+	tristate "PRU based CAN emulation for DA8XX"
+	---help---
+	Enable this to emulate a CAN controller on the PRU of DA8XX.
+	If not sure, mark N
+
+config DA8XX_PRU_CANID_MBX0
+	hex "CANID for mailbox 0"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 0
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX1
+	hex "CANID for mailbox 1"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	 ---help---
+	Enter the CANID for mailbox 1
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX2
+	hex "CANID for mailbox 2"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 2
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX3
+	hex "CANID for mailbox 3"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 3
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX4
+	hex "CANID for mailbox 4"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 4
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX5
+	hex "CANID for mailbox 5"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 5
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX6
+	hex "CANID for mailbox 6"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---

drivers/net/can/da8xx_pruss/Kconfig line 73

Quoted 10 lines
drivers/net/can/da8xx_pruss/Kconfig new file +10 -0
+	Enter the CANID for mailbox 6
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX7
+	hex "CANID for mailbox 7"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 7
+	Default value is set to 0x123, change this as required.

Well, defining CAN identifiers via Kconfig entries is really wired. Could you please explain why that's necessary and what's so special with that CAN hardware. We need a better solution if the CAN controller cannot handle and CAN id.

drivers/net/can/da8xx_pruss/Makefile line 7

Quoted 13 lines
drivers/net/can/da8xx_pruss/Makefile new file +7 -0
new file mode 100644
index 0000000..48f3055
@@ -0,0 +1,7 @@
+#
+# Makefile for CAN Lite emulation
+#
+can_emu-objs :=   pruss_can.o \
+                  pruss_can_api.o
+
+obj-$(CONFIG_CAN_TI_DA8XX_PRU)    += can_emu.o

Again another name.

Quoted 79 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +73 -0
new file mode 100644
index 0000000..1b3afde
@@ -0,0 +1,758 @@
+/*
+ *  TI DA8XX PRU CAN Emulation device driver
+ *  Author: subhasish-EvXpCiN+lbve9wHmmfpqLFaTQe2KTcn/@public.gmane.org
+ *
+ *  This driver supports TI's PRU CAN Emulation and the
+ *  specs for the same is available at <http://www.ti.com>
+ *
+ *  Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *  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 version 2.
+ *
+ *  This program is distributed as is WITHOUT ANY WARRANTY of any
+ *  kind, whether express or implied; without even the implied warranty
+ *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/clk.h>
+#include <linux/types.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <mach/da8xx.h>
+#include "pruss_can_api.h"
+
+#define DRV_NAME "da8xx_pruss_can"
+#define DRV_DESC "TI PRU CAN Controller Driver v0.1"
+#define PRU_CAN_START		1
+#define PRU_CAN_STOP		0
+#define MB_MIN			0
+#define MB_MAX			7
+
+#define PRU_CANMID_IDE			BIT(29)	/* Extended frame format */
+
+#define PRU_CAN_ISR_BIT_CCI		BIT(15)
+#define PRU_CAN_ISR_BIT_ESI		BIT(14)
+#define PRU_CAN_ISR_BIT_SRDI		BIT(13)
+#define PRU_CAN_ISR_BIT_RRI		BIT(8)
+
+#define PRU_CAN_MBXSR_BIT_STATE		BIT(7)
+#define PRU_CAN_MBXSR_BIT_TC		BIT(6)
+#define PRU_CAN_MBXSR_BIT_ERR		BIT(5)
+#define PRU_CAN_MBXSR_BIT_OF		BIT(0)
+
+#define PRU_CAN_GSR_BIT_TXM		BIT(7)
+#define PRU_CAN_GSR_BIT_RXM		BIT(6)
+#define PRU_CAN_GSR_BIT_CM		BIT(5)
+#define PRU_CAN_GSR_BIT_EPM		BIT(4)
+#define PRU_CAN_GSR_BIT_BFM		BIT(3)
+#define RTR_MBX_NO			8
+#define MAX_INIT_RETRIES		20
+#define L138_PRU_ARM_FREQ		312000
+#define DFLT_PRU_FREQ			156000000
+#define DFLT_PRU_BITRATE		125000
+
+#define CONFIG_DA8XX_PRU_CANID_MBX0	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX1	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX2	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX3	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX4	0x123

drivers/net/can/da8xx_pruss/pruss_can.c line 83

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+#define CONFIG_DA8XX_PRU_CANID_MBX5	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX6	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX7	0x123
+
+#ifdef __CAN_DEBUG
+#define __can_debug(fmt, args...) printk(KERN_DEBUG "can_debug: " fmt, ## args)
+#else
+#define __can_debug(fmt, args...)
+#endif
+#define __can_err(fmt, args...) printk(KERN_ERR "can_err: " fmt, ## args)

Please use dev_dbg, dev_err, ... or netif_dbg, netdev_err, ... instead.

Quoted 20 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +20 -0
+/*
+ * omapl_pru can private data
+ */
+struct omapl_pru_can_priv {
+	struct can_priv can;
+	struct workqueue_struct *pru_can_wQ;
+	struct work_struct rx_work;
+	struct net_device *ndev;
+	struct device *dev; /* pdev->dev */
+	struct clk *clk_timer;
+	u32 timer_freq;
+	can_emu_app_hndl can_tx_hndl;
+	can_emu_app_hndl can_rx_hndl;
+	const struct firmware *fw_rx;
+	const struct firmware *fw_tx;
+	spinlock_t mbox_lock;
+	u32 trx_irq;
+	u32 tx_head;
+	u32 tx_tail;
+	u32 tx_next;

drivers/net/can/da8xx_pruss/pruss_can.c line 114

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+	u32 rx_next;
+};
+
+static int omapl_pru_can_get_state(const struct net_device *ndev,
+				   enum can_state *state)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	*state = priv->can.state;
+	return 0;
+}

There is no need for that function as you handle state changes in the interrupt context.

drivers/net/can/da8xx_pruss/pruss_can.c line 125

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+static int omapl_pru_can_set_bittiming(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct can_bittiming *bt = &priv->can.bittiming;
+	long bit_error = 0;
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) {
+		dev_warn(priv->dev, "WARN: Triple"
+			 "sampling not set due to h/w limitations");
+	}

No need for this test as you have not set the CAN_CTRLMODE_3_SAMPLES bit in priv.ctrlmode_supported.

Quoted 6 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +6 -0
+	if (pru_can_calc_timing(priv->dev, priv->can.clock.freq,
+				bt->bitrate) != 0)
+		return -EINVAL;
+	bit_error =
+	    (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) -
+	      bt->bitrate) * 1000) / bt->bitrate;

drivers/net/can/da8xx_pruss/pruss_can.c line 141

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+	if (bit_error) {
+		bit_error =
+		    (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) -
+		      bt->bitrate) * 1000000) / bt->bitrate;
+		printk(KERN_INFO "\nBitrate error %ld.%ld%%\n",
+			bit_error / 10000, bit_error % 1000);
+	} else
+		printk(KERN_INFO "\nBitrate error 0.0%%\n");
+
+	return 0;

Please use the pre-calculated bit-timing parameters. Have a look to the SJA1000 driver for further information:

http://lxr.linux.no/#linux+v2.6.37/drivers/net/can/sja1000/sja1000.c#L202

In general, please use the bit-timing interface of Socket-CAN and drop you own.

Quoted 20 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +20 -0
+}
+
+static void omapl_pru_can_stop(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	u16 int_mask = 0;
+
+	pru_can_mask_ints(priv->dev, int_mask);	/* mask all ints */
+	pru_can_start_abort_tx(priv->dev, PRU_CAN_STOP);
+	priv->can.state = CAN_STATE_STOPPED;
+}
+
+/*
+ * This is to just set the can state to ERROR_ACTIVE
+ *	ip link set canX up type can bitrate 125000
+ */
+static void omapl_pru_can_start(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	u16 int_mask = 0xFFFF;

drivers/net/can/da8xx_pruss/pruss_can.c line 171

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+
+	if (priv->can.state != CAN_STATE_STOPPED)
+		omapl_pru_can_stop(ndev);
+
+	pru_can_mask_ints(priv->dev, int_mask);	/* unmask all ints */
+
+	pru_can_get_global_status(priv->dev, &priv->can_tx_hndl);
+	pru_can_get_global_status(priv->dev, &priv->can_rx_hndl);
+
+	if (PRU_CAN_GSR_BIT_EPM & priv->can_tx_hndl.u32globalstatus)

Decoding the variable type into the name is deprecated in Linux.

Quoted 12 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +12 -0
+		priv->can.state = CAN_STATE_ERROR_PASSIVE;
+	else if (PRU_CAN_GSR_BIT_BFM & priv->can_tx_hndl.u32globalstatus)
+		priv->can.state = CAN_STATE_BUS_OFF;
+	else
+		priv->can.state = CAN_STATE_ERROR_ACTIVE;
+}
+
+static int omapl_pru_can_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+	int ret = 0;
+
+	switch (mode) {

drivers/net/can/da8xx_pruss/pruss_can.c line 193

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+	case CAN_MODE_START:
+		omapl_pru_can_start(ndev);
+		if (netif_queue_stopped(ndev))
+			netif_wake_queue(ndev);
+		break;
+	case CAN_MODE_STOP:
+		omapl_pru_can_stop(ndev);
+		if (!netif_queue_stopped(ndev))
+			netif_stop_queue(ndev);
+		break;

This case is not supported.

Quoted 17 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +17 -0
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+	return ret;
+}
+
+static netdev_tx_t omapl_pru_can_start_xmit(struct sk_buff *skb,
+					    struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	int count;
+	u8 *data = cf->data;
+	u8 dlc = cf->can_dlc;
+	u8 *ptr8data = NULL;
+

drivers/net/can/da8xx_pruss/pruss_can.c line 217

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +7 -0
+	netif_stop_queue(ndev);
+	if (cf->can_id & CAN_EFF_FLAG)	/* Extended frame format */
+		*((u32 *) &priv->can_tx_hndl.strcanmailbox) =
+		    (cf->can_id & CAN_EFF_MASK) | PRU_CANMID_IDE;
 
 
 
+	else			/* Standard frame format */
+		*((u32 *) &priv->can_tx_hndl.strcanmailbox) =
+		    (cf->can_id & CAN_SFF_MASK) << 18;

You use many of such wired expressions. strcanmailbox is a struct

  typedef struct {
	u16 u16extendedidentifier;
	u16 u16baseidentifier;
        ...

} can_mail_box_structure;

and you obviously set the fist two fields. In contrast, the member u16extendedidentifier is never directly used. Puh, that's magic. Please make your code more transparent and readable.

Quoted 8 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +8 -0
+	if (cf->can_id & CAN_RTR_FLAG)	/* Remote transmission request */
+		*((u32 *) &priv->can_tx_hndl.strcanmailbox) |= CAN_RTR_FLAG;
+
+	ptr8data = &priv->can_tx_hndl.strcanmailbox.u8data7 + (dlc - 1);
+	for (count = 0; count < (u8) dlc; count++) {
+		*ptr8data-- = *data++;
+	}
+	*((u32 *) &priv->can_tx_hndl.strcanmailbox.u16datalength) = (u32) dlc;

...

drivers/net/can/da8xx_pruss/pruss_can.c line 481

Quoted 13 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +13 -0
+irqreturn_t omapl_rx_can_intr(int irq, void *dev_id)
+{
+
+	struct net_device *ndev = dev_id;
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	u32 intc_status = 0;
+
+	intc_status = pru_can_get_intc_status(priv->dev);
+	if (intc_status & 4)
+		return omapl_tx_can_intr(irq, dev_id);
+	if (intc_status & 2) {
+		if (!work_pending(&priv->rx_work))
+			queue_work(priv->pru_can_wQ, &priv->rx_work);

You handle RX in a work queue!? Please use NAPI instead.

Quoted 168 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +168 -0
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int omapl_pru_can_open(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	int err;
+
+	/* register interrupt handler */
+	err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED,
+			  "pru_can_irq", ndev);
+	if (err) {
+		dev_err(priv->dev, "error requesting rx interrupt\n");
+		goto exit_trx_irq;
+	}
+	/* common open */
+	err = open_candev(ndev);
+	if (err) {
+		dev_err(priv->dev, "open_candev() failed %d\n", err);
+		goto exit_open;
+	}
+
+	pru_can_emu_init(priv->dev, priv->can.clock.freq);
+	priv->tx_tail = MB_MIN;
+	priv->tx_head = MB_MAX;
+
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX0, 0);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX1, 1);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX2, 2);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX3, 3);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX4, 4);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX5, 5);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX6, 6);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX7, 7);
+
+	omapl_pru_can_start(ndev);
+	netif_start_queue(ndev);
+	return 0;
+
+exit_open:
+	free_irq(priv->trx_irq, ndev);
+exit_trx_irq:
+	return err;
+}
+
+static int omapl_pru_can_close(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+
+	if (!netif_queue_stopped(ndev))
+		netif_stop_queue(ndev);
+
+	close_candev(ndev);
+
+	free_irq(priv->trx_irq, ndev);
+	return 0;
+}
+
+static const struct net_device_ops omapl_pru_can_netdev_ops = {
+	.ndo_open		= omapl_pru_can_open,
+	.ndo_stop		= omapl_pru_can_close,
+	.ndo_start_xmit		= omapl_pru_can_start_xmit,
+};
+
+static int __devinit omapl_pru_can_probe(struct platform_device *pdev)
+{
+	struct net_device *ndev = NULL;
+	const struct da8xx_pru_can_data *pdata;
+	struct omapl_pru_can_priv *priv = NULL;
+	struct device *dev = &pdev->dev;
+	u32 err;
+
+	pdata = dev->platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "platform data not found\n");
+		return -EINVAL;
+	}
+
+	ndev = alloc_candev(sizeof(struct omapl_pru_can_priv), MB_MAX + 1);
+	if (!ndev) {
+		dev_err(&pdev->dev, "alloc_candev failed\n");
+		err = -ENOMEM;
+		goto probe_exit;
+	}
+	priv = netdev_priv(ndev);
+
+	priv->trx_irq = platform_get_irq(to_platform_device(dev->parent), 0);
+	if (!priv->trx_irq) {
+		dev_err(&pdev->dev, "unable to get pru interrupt resources!\n");
+		err = -ENODEV;
+		goto probe_exit;
+	}
+
+	priv->ndev = ndev;
+	priv->dev = dev; /* priv->dev = pdev->dev */
+
+	priv->can.bittiming_const = NULL;
+	priv->can.do_set_bittiming = omapl_pru_can_set_bittiming;
+	priv->can.do_set_mode = omapl_pru_can_set_mode;
+	priv->can.do_get_state = omapl_pru_can_get_state;
+	priv->can_tx_hndl.u8prunumber = CAN_TX_PRU_1;
+	priv->can_rx_hndl.u8prunumber = CAN_RX_PRU_0;
+
+	/* we support local echo, no arp */
+	ndev->flags |= (IFF_ECHO | IFF_NOARP);
+
+	/* pdev->dev->device_private->driver_data = ndev */
+	platform_set_drvdata(pdev, ndev);
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+	ndev->netdev_ops = &omapl_pru_can_netdev_ops;
+
+	priv->can.clock.freq = pruss_get_clk_freq(priv->dev);
+
+	priv->clk_timer = clk_get(&pdev->dev, "pll1_sysclk2");
+	if (IS_ERR(priv->clk_timer)) {
+		dev_err(&pdev->dev, "no timer clock available\n");
+		err = PTR_ERR(priv->clk_timer);
+		priv->clk_timer = NULL;
+		goto probe_exit_candev;
+	}
+	priv->timer_freq = clk_get_rate(priv->clk_timer);
+
+	err = register_candev(ndev);
+	if (err) {
+		dev_err(&pdev->dev, "register_candev() failed\n");
+		err = -ENODEV;
+		goto probe_exit_clk;
+	}
+
+	err = request_firmware(&priv->fw_tx, "PRU_CAN_Emulation_Tx.bin",
+			&pdev->dev);
+	if (err) {
+		dev_err(&pdev->dev, "can't load firmware\n");
+		err = -ENODEV;
+		goto probe_exit_clk;
+	}
+
+	dev_info(&pdev->dev, "fw_tx size %d. downloading...\n",
+		 priv->fw_tx->size);
+
+	err = request_firmware(&priv->fw_rx, "PRU_CAN_Emulation_Rx.bin",
+			&pdev->dev);
+	if (err) {
+		dev_err(&pdev->dev, "can't load firmware\n");
+		err = -ENODEV;
+		goto probe_release_fw;
+	}
+	dev_info(&pdev->dev, "fw_rx size %d. downloading...\n",
+		 priv->fw_rx->size);
+
+	/* init the pru */
+	pru_can_emu_init(priv->dev, priv->can.clock.freq);
+	udelay(200);
+
+	pruss_enable(priv->dev, CAN_RX_PRU_0);
+	pruss_enable(priv->dev, CAN_TX_PRU_1);
+
+	/* download firmware into pru */
+	err = pruss_load(priv->dev, CAN_RX_PRU_0,
+		(u32 *)priv->fw_rx->data, (priv->fw_rx->size / 4));
+	if (err) {
+		dev_err(&pdev->dev, "firmware download error\n");
+		err = -ENODEV;
+		goto probe_release_fw_1;
+	}
+	err = pruss_load(priv->dev, CAN_TX_PRU_1,

drivers/net/can/da8xx_pruss/pruss_can.c line 659

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+		(u32 *)priv->fw_tx->data, (priv->fw_tx->size / 4));
+	if (err) {
+		dev_err(&pdev->dev, "firmware download error\n");
+		err = -ENODEV;
+		goto probe_release_fw_1;
+	}
+
+	if (pru_can_calc_timing(priv->dev, DFLT_PRU_FREQ,
+				DFLT_PRU_BITRATE) != 0)
+		return -EINVAL;

Please don't define a default bit-rate. It's error prune.

Quoted 223 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +99 -0
+
+	pruss_run(priv->dev, CAN_RX_PRU_0);
+	pruss_run(priv->dev, CAN_TX_PRU_1);
+
+	/*Create The Work Queue */
+	priv->pru_can_wQ = create_freezeable_workqueue("omapl_pru_wQ");
+	if (priv->pru_can_wQ == NULL) {
+		dev_err(&pdev->dev, "failed to create work queue\n");
+		err = -ENODEV;
+		goto probe_release_fw_1;
+	}
+
+	INIT_WORK(&priv->rx_work, omapl_pru_can_rx_wQ);
+	dev_info(&pdev->dev,
+		 "%s device registered (trx_irq = %d,  clk = %d)\n",
+		 DRV_NAME, priv->trx_irq, priv->can.clock.freq);
+
+	return 0;
+
+probe_release_fw_1:
+	release_firmware(priv->fw_rx);
+probe_release_fw:
+	release_firmware(priv->fw_tx);
+probe_exit_clk:
+	clk_put(priv->clk_timer);
+probe_exit_candev:
+	if (NULL != ndev)
+		free_candev(ndev);
+probe_exit:
+	return err;
+}
+
+static int __devexit omapl_pru_can_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+
+	omapl_pru_can_stop(ndev);
+
+	pru_can_emu_exit(priv->dev);
+	release_firmware(priv->fw_tx);
+	release_firmware(priv->fw_rx);
+	clk_put(priv->clk_timer);
+	flush_workqueue(priv->pru_can_wQ);
+	destroy_workqueue(priv->pru_can_wQ);
+	unregister_candev(ndev);
+	free_candev(ndev);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int omapl_pru_can_suspend(struct platform_device *pdev,
+			pm_message_t mesg)
+{
+	dev_info(&pdev->dev, "%s not yet implemented\n", __func__);
+	return 0;
+}
+
+static int omapl_pru_can_resume(struct platform_device *pdev)
+{
+	dev_info(&pdev->dev, "%s not yet implemented\n", __func__);
+	return 0;
+}
+#else
+#define omapl_pru_can_suspend NULL
+#define omapl_pru_can_resume NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver omapl_pru_can_driver = {
+	.probe		= omapl_pru_can_probe,
+	.remove		= __devexit_p(omapl_pru_can_remove),
+	.suspend	= omapl_pru_can_suspend,
+	.resume		= omapl_pru_can_resume,
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init omapl_pru_can_init(void)
+{
+	__can_debug(KERN_INFO DRV_DESC "\n");
+	return platform_driver_register(&omapl_pru_can_driver);
+}
+
+module_init(omapl_pru_can_init);
+
+static void __exit omapl_pru_can_exit(void)
+{
+	__can_debug(KERN_INFO DRV_DESC " unloaded\n");
+	platform_driver_unregister(&omapl_pru_can_driver);
+}
+
+module_exit(omapl_pru_can_exit);
+
+MODULE_AUTHOR("Subhasish Ghosh <subhasish-EvXpCiN+lbve9wHmmfpqLFaTQe2KTcn/@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("omapl pru CAN netdevice driver");
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +118 -0
new file mode 100644
index 0000000..2f7438a
@@ -0,0 +1,1227 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Wilfred Felix
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include "pruss_can_api.h"
+
+static can_emu_drv_inst gstr_can_inst[ecanmaxinst];
+
+/*
+ * pru_can_set_brp()	Updates the  BRP register of PRU0
+ * and PRU1 of OMAP L138. This API will be called by the
+ * Application to updtae the BRP register of PRU0 and PRU1
+ *
+ * param	u16bitrateprescaler		The can bus bitrate
+ * prescaler value be set
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_set_brp(struct device *dev, u16 u16bitrateprescaler)
+{
+
+	u32 u32offset;
+
+	if (u16bitrateprescaler > 255) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1);
+
+	u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1);
+
+	return 0;
+
+}
+
+/*
+ * pru_can_set_bit_timing()		Updates the timing register
+ * of PRU0 and PRU1 of OMAP L138. This API will be called by
+ * the Application to updtae the timing register of PRU0 and PRU1
+ *
+ * param	pstrbittiming		Pointer to structure holding
+ * the bit timing values for can bus.
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_set_bit_timing(struct device *dev,
+		can_bit_timing_consts *pstrbittiming)
+{
+
+	u32 u32offset;
+	u32 u32serregister;
+
+	u32serregister = 0;
+
+	if (pstrbittiming == NULL) {
+		return -1;
+	}
+
+	if ((pstrbittiming->u8syncjumpwidth > PRU_CAN_MAX_SJW) ||
+	    (pstrbittiming->u8phseg1 > PRU_CAN_MAX_PHSEG1) ||
+	    (pstrbittiming->u8phseg2 > PRU_CAN_MAX_PHSEG2)) {
+		return -1;
+	}
+
+	u32serregister = u32serregister |
+			((pstrbittiming->u8syncjumpwidth << 7) |
+			(pstrbittiming->u8phseg1 << 3) |
+			(pstrbittiming->u8phseg2));
+
+	u32offset = (PRU_CAN_TX_TIMING_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u32serregister, 1);
+
+	u32offset = (PRU_CAN_RX_TIMING_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u32serregister, 1);
+
+	return 0;
+}
+
+
+/*
+ * pru_can_calc_timing()
+ * Updates the  timing values of PRU0 and PRU1 of OMAP L138.
+ * This API will be called by the
+ * Application to updtae the timing values of PRU0 and PRU1
+ *
+ * return   SUCCESS or FAILURE
+ */
+
+s16 pru_can_calc_timing(struct device *dev, u32 pru_freq, u32 bit_rate)
+{
+	u16 u16phaseseg1;
+	u16 u16phaseseg2;
+	u32 u32offset;
+	u32 u32timing_value;
+	u32 u32setup_value;
+	u32timing_value = TIMER_CLK_FREQ / bit_rate;
+	u32offset = (PRU_CAN_TIMING_VAL_TX);
+	pruss_writel(dev, u32offset, (u32 *) &u32timing_value, 4);
+	pruss_readl(dev, u32offset, (u32 *) &u32timing_value, 4);
+	u32setup_value =
+	    (GPIO_SETUP_DELAY * (pru_freq / 1000000) / 1000) /
+	    DELAY_LOOP_LENGTH;
+	u32offset = (PRU_CAN_TIMING_VAL_TX_SJW);
+	pruss_writel(dev, u32offset, (u32 *) &u32setup_value, 4);
+	u16phaseseg1 = (u16) (u32timing_value / 2);
+	u16phaseseg2 = u32timing_value - u16phaseseg1;

drivers/net/can/da8xx_pruss/pruss_can_api.c line 128

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+	u16phaseseg1 -= TIMER_SETUP_DELAY;
+	u16phaseseg2 -= TIMER_SETUP_DELAY;
+	u32setup_value = (u16phaseseg1 << 16) | u16phaseseg2;
+	u32offset = (PRU_CAN_TIMING_VAL_RX);
+	pruss_writel(dev, u32offset, (u32 *) &u32setup_value, 4);
+	u32offset = (PRU_CAN_TIMING_VAL_RX + 4);
+	pruss_writel(dev, u32offset, (u32 *) &u32timing_value, 4);
+
+	return 0;
+}

Why can't you use the common Socket-CAN bit-timing infrastructure.

Quoted 40 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +40 -0
+/*
+ * pru_can_write_data_to_mailbox()
+ * Updates the transmit mailboxes of PRU1 of OMAP L138.
+ * This API will be called by the Application to update
+ * the transmit mailboxes of PRU1
+ *
+ * param  pu16canframedata	Can mailbox data buffer
+ *
+ * param  u8mailboxnum		Mailbox to be updated
+ *
+ * return SUCCESS or FAILURE
+ */
+s16 pru_can_write_data_to_mailbox(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl)
+{
+	s16 s16subrtnretval;
+	u32 u32offset;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	switch ((u8) pstremuapphndl->ecanmailboxnumber) {
+	case 0:
+		u32offset = (PRU_CAN_TX_MAILBOX0);
+		break;
+	case 1:
+		u32offset = (PRU_CAN_TX_MAILBOX1);
+		break;
+	case 2:
+		u32offset = (PRU_CAN_TX_MAILBOX2);
+		break;
+	case 3:
+		u32offset = (PRU_CAN_TX_MAILBOX3);
+		break;
+	case 4:
+		u32offset = (PRU_CAN_TX_MAILBOX4);
+		break;
+	case 5:
+		u32offset = (PRU_CAN_TX_MAILBOX5);

drivers/net/can/da8xx_pruss/pruss_can_api.c line 179

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+		break;
+	case 6:
+		u32offset = (PRU_CAN_TX_MAILBOX6);
+		break;
+	case 7:
+		u32offset = (PRU_CAN_TX_MAILBOX7);
+		break;
+	default:
+		return -1;
+	}

There are more efficient ways to implemet that, e.g. by using an array.

Quoted 51 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +51 -0
+	s16subrtnretval = pruss_writel(dev, u32offset,
+		(u32 *) &(pstremuapphndl->strcanmailbox), 4);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * pru_can_get_data_from_mailbox()
+ * Receive data from the receive mailboxes of PRU0  of OMAP L138.
+ * This API will be called by the Application to get data from
+ * the receive mailboxes of PRU0
+ *
+ * param  pu16canframedata	Can mailbox data buffer
+ *
+ * param  u8mailboxnum		Mailbox to be updated
+ *
+ * return SUCCESS or FAILURE
+ */
+s16 pru_can_get_data_from_mailbox(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)
+{
+	s16 s16subrtnretval;
+	u32 u32offset;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	switch ((u8) pstremuapphndl->ecanmailboxnumber) {
+	case 0:
+		u32offset = (PRU_CAN_RX_MAILBOX0);
+		break;
+	case 1:
+		u32offset = (PRU_CAN_RX_MAILBOX1);
+		break;
+	case 2:
+		u32offset = (PRU_CAN_RX_MAILBOX2);
+		break;
+	case 3:
+		u32offset = (PRU_CAN_RX_MAILBOX3);
+		break;
+	case 4:
+		u32offset = (PRU_CAN_RX_MAILBOX4);
+		break;
+	case 5:
+		u32offset = (PRU_CAN_RX_MAILBOX5);
+		break;
+	case 6:
+		u32offset = (PRU_CAN_RX_MAILBOX6);

drivers/net/can/da8xx_pruss/pruss_can_api.c line 241

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+		break;
+	case 7:
+		u32offset = (PRU_CAN_RX_MAILBOX7);
+		break;
+	case 8:
+		u32offset = (PRU_CAN_RX_MAILBOX8);
+		break;
+	default:
+		return -1;
+	}

Ditto.

Quoted 645 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +645 -0
+	s16subrtnretval =
+	    pruss_readl(dev, u32offset,
+		  (u32 *) &(pstremuapphndl->strcanmailbox),
+				  4);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * pru_can_receive_id_map()
+ * Receive mailboxes ID Mapping of PRU0  of OMAP L138.
+ * This API will be called by the Application
+ * to map the IDs  to receive mailboxes of PRU0
+ *
+ * param  u32nodeid		Can node ID
+ *
+ * param  ecanmailboxno		Mailbox to be mapped
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_rx_id_map(struct device *dev, u32 u32nodeid,
+		can_mailbox_number ecanmailboxno)
+{
+
+	pruss_writel(dev, (PRU_CAN_ID_MAP +
+		(((u8) ecanmailboxno) * 4)), (u32 *) &u32nodeid, 1);
+
+	return 0;
+}
+
+/*
+ * pru_can_get_intr_status()
+ * Gets the interrupts status register value.
+ * This API will be called by the Application
+ * to get the interrupts status register value
+ *
+ * param  u8prunumber	PRU number for which IntStatusReg
+ * has to be read
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_get_intr_status(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)
+{
+	u32 u32offset;
+	s16 s16subrtnretval = -1;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) {
+		u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER);
+	} else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) {
+		u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER);
+	} else {
+		return -1;
+	}
+
+	s16subrtnretval = pruss_readl(dev, u32offset,
+		(u32 *) &pstremuapphndl->u32interruptstatus, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * pru_can_get_global_status()	Gets the globalstatus
+ * register value. This API will be called by the Application
+ * to  get the global status register value
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_get_global_status(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)
+{
+	u32 u32offset;
+	int s16subrtnretval = -1;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) {
+		u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER);
+	} else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) {
+		u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER);
+	} else {
+		return -1;
+	}
+
+	s16subrtnretval = pruss_readl(dev, u32offset,
+		(u32 *) &pstremuapphndl->u32globalstatus, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * pru_can_get_mailbox_status()		Gets the mailbox status
+ * register value. This API will be called by the Application
+ * to get the mailbox status register value
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_get_mailbox_status(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)
+{
+	u32 u32offset;
+	s16 s16subrtnretval = -1;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) {
+		switch (pstremuapphndl->ecanmailboxnumber) {
+		case 0:
+			u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER);
+			break;
+		case 1:
+			u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER);
+			break;
+		case 2:
+			u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER);
+			break;
+		case 3:
+			u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER);
+			break;
+		case 4:
+			u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER);
+			break;
+		case 5:
+			u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER);
+			break;
+		case 6:
+			u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER);
+			break;
+		case 7:
+			u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER);
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) {
+		switch (pstremuapphndl->ecanmailboxnumber) {
+		case 0:
+			u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER);
+			break;
+		case 1:
+			u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER);
+			break;
+		case 2:
+			u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER);
+			break;
+		case 3:
+			u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER);
+			break;
+		case 4:
+			u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER);
+			break;
+		case 5:
+			u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER);
+			break;
+		case 6:
+			u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER);
+			break;
+		case 7:
+			u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER);
+			break;
+		case 8:
+			u32offset = (PRU_CAN_RX_MAILBOX8_STATUS_REGISTER);
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	else {
+		return -1;
+	}
+
+	s16subrtnretval = pruss_readl(dev, u32offset,
+		(u32 *) &pstremuapphndl->u32mailboxstatus, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
+s16 pru_can_tx_mode_set(struct device *dev, bool btransfer_flag,
+				can_transfer_direction ecan_trx)
+{
+	u32 u32offset;
+	u32 u32value;
+
+	if (ecan_trx == ecantransmit) {
+		u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER);
+		pruss_readl(dev, u32offset, &u32value, 1);
+		if (btransfer_flag == true) {
+			u32value &= 0x1F;
+			u32value |= 0x80;
+		} else {
+			u32value &= 0x7F;
+		}
+		pruss_writel(dev, u32offset, &u32value, 1);
+		u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER);
+		pruss_writel(dev, u32offset, &u32value, 1);
+	} else if (ecan_trx == ecanreceive) {
+		u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER);
+		pruss_readl(dev, u32offset, &u32value, 1);
+		if (btransfer_flag == true) {
+			u32value &= 0x1F;
+			u32value |= 0x40;
+		} else {
+			u32value &= 0xBF;
+		}
+		pruss_writel(dev, u32offset, &u32value, 1);
+		u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER);
+		pruss_writel(dev, u32offset, &u32value, 1);
+	} else
+		return -1;
+
+	return 0;
+}
+
+/*
+ * pru_can_config_mode_set()		Sets the timing value
+ * for data transfer. This API will be called by the Application
+ * to set timing valus for data transfer
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_config_mode_set(struct device *dev, bool bconfigmodeflag)
+{
+
+	u32 u32bitrateprescaler;
+	u32 u32canbittiming;
+
+	pruss_readl(dev, (PRU_CAN_TX_CLOCK_BRP_REGISTER),
+			(u32 *) &u32bitrateprescaler, 1);
+	pruss_readl(dev, (PRU_CAN_TX_TIMING_REGISTER),
+			(u32 *) &u32canbittiming, 1);
+
+	if (bconfigmodeflag == 1) {
+		pru_can_calc_timing(dev, u32canbittiming, u32bitrateprescaler);
+	}
+
+	else {
+		pru_can_calc_timing(dev, 0, 0);
+	}
+
+	return 0;
+}
+
+/*
+ * pru_can_emu_init()		Initializes the Can
+ * Emulation Parameters. This API will be called by the Application
+ * to Initialize the Can Emulation Parameters
+ *
+ * param    u32pruclock         PRU Clock value
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_init(struct device *dev, u32 u32pruclock)
+{
+	u32 u32offset;
+	u32 u32value;
+	s16 s16subrtnretval = -1;
+	u8 u8loop;
+
+	for (u8loop = 0; u8loop < (u8) ecanmaxinst; u8loop++) {
+		gstr_can_inst[u8loop].bcaninststate = (bool) 0;
+		gstr_can_inst[u8loop].ecantransferdirection =
+		    (can_transfer_direction) 0;
+		gstr_can_inst[u8loop].u32apphandlerptr = 0;
+	}
+
+	u32offset = (PRU_CAN_TX_GLOBAL_CONTROL_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000040;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000040;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_INTERRUPT_MASK_REGISTER & 0xFFFF);
+	u32value = 0x00004000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval =
+	    pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_ERROR_COUNTER_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_TIMING_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_ERROR_COUNTER_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_POLARITY0 & 0xFFFF);
+	u32value = 0xFFFFFFFF;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRUSS_INTC_POLARITY1 & 0xFFFF);
+	u32value = 0xFFFFFFFF;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRUSS_INTC_TYPE0 & 0xFFFF);
+	u32value = 0x1C000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRUSS_INTC_TYPE1 & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HSTINTENIDXCLR & 0xFFFF);
+	u32value = 0x0;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_GLBLEN & 0xFFFF);
+	u32value = 0x1;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	/* tx intr map arm->pru */
+	u32offset = (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF);
+	u32value = 0x0;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HOSTMAP0 & 0xFFFF);
+	u32value = 0x03020100;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HOSTMAP1 & 0xFFFF);
+	u32value = 0x07060504;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HOSTMAP2 & 0xFFFF);
+	u32value = 0x0000908;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_CHANMAP0 & 0xFFFF);
+	u32value = 0;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_CHANMAP8 & 0xFFFF);
+	u32value = 0x00020200;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 19;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 19;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 18;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 18;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 34;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 34;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HOSTINTEN & 0xFFFF);
+	u32value = 0x5;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+/* PRU0 - Rx Internal Registers Initializations */
+
+	u32offset = (PRU_CAN_RX_GLOBAL_CONTROL_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000040;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_INTERRUPT_MASK_REGISTER & 0xFFFF);
+	u32value = 0x00004000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x0000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_ERROR_COUNTER_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_TIMING_REGISTER & 0xFFFF);
+	u32value = 0x0000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);

drivers/net/can/da8xx_pruss/pruss_can_api.c line 897

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}

Above I realize a lot of duplicated code. Could be handled by an array more efficiently, I believe.

Quoted 258 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +258 -0
+	return 0;
+}
+
+
+/*
+ * pru_can_emu_open()		Opens the can emu for
+ * application to use. This API will be called by the Application
+ * to Open the can emu for application to use.
+ *
+ * param	pstremuapphndl	Pointer to application handler
+ * structure
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_open(struct device *dev, can_emu_app_hndl *pstremuapphndl)
+{
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	if (gstr_can_inst[pstremuapphndl->ecaninstance].bcaninststate == 1) {
+		return -1;
+	}
+
+	gstr_can_inst[(u8) pstremuapphndl->ecaninstance].
+					bcaninststate = (bool)1;
+	gstr_can_inst[(u8) pstremuapphndl->
+		ecaninstance].ecantransferdirection =
+		(can_transfer_direction)(u8)pstremuapphndl->ecantransferdirection;
+	gstr_can_inst[(u8) pstremuapphndl->ecaninstance].
+		u32apphandlerptr = (u32) pstremuapphndl;
+
+	return 0;
+}
+
+
+/*
+ * brief    pru_can_emu_close()	Closes the can emu for other
+ * applications to use. This API will be called by the Application to Close
+ * the can emu for other applications to use
+ *
+ * param	pstremuapphndl	Pointer to application handler structure
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_close(struct device *dev, can_emu_app_hndl *pstremuapphndl)
+{
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+	if (gstr_can_inst[pstremuapphndl->ecaninstance].bcaninststate == 0) {
+		return -1;
+	}
+	if ((u32) pstremuapphndl != gstr_can_inst[(u8) pstremuapphndl->
+			ecaninstance].u32apphandlerptr){
+		return -1;
+	}
+	gstr_can_inst[(u8) pstremuapphndl->ecaninstance].bcaninststate
+		= (bool) 0;
+	gstr_can_inst[(u8) pstremuapphndl->
+	ecaninstance].ecantransferdirection = (can_transfer_direction) 0;
+	gstr_can_inst[(u8) pstremuapphndl->ecaninstance].u32apphandlerptr = 0;
+
+	return 0;
+}
+
+/*
+ * brief    pru_can_emu_exit()	Diables all the PRUs
+ * This API will be called by the Application to disable all PRUs
+ * param	None
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_exit(struct device *dev)
+{
+	s16 s16subrtnretval;
+
+	s16subrtnretval = pruss_disable(dev, CAN_RX_PRU_0);
+	if (s16subrtnretval == -1)
+		return -1;
+	s16subrtnretval = pruss_disable(dev, CAN_TX_PRU_1);
+	if (s16subrtnretval == -1)
+		return -1;
+
+	return 0;
+}
+
+s16 pru_can_emu_sreset(struct device *dev)
+{
+	return 0;
+}
+
+s16 pru_can_tx(struct device *dev, u8 u8mailboxnumber, u8 u8prunumber)
+{
+	u32 u32offset = 0;
+	u32 u32value = 0;
+	s16 s16subrtnretval = -1;
+
+	if (DA8XX_PRUCORE_1 == u8prunumber) {
+		switch (u8mailboxnumber) {
+		case 0:
+			u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 1:
+			u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 2:
+			u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 3:
+			u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 4:
+			u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 5:
+			u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 6:
+			u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 7:
+			u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		default:
+			return -1;
+		}
+	} else {
+
+		u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER & 0xFFFF);
+		u32value = 0x00000000;
+		s16subrtnretval = pruss_readl(dev, u32offset,
+						(u32 *) &u32value, 1);
+		if (s16subrtnretval == -1) {
+			return -1;
+		}
+		u32value = u32value & ~(1 << u8mailboxnumber);
+		s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+		if (s16subrtnretval == -1) {
+			return -1;
+		}
+
+		switch (u8mailboxnumber) {
+		case 0:
+			u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 1:
+			u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 2:
+			u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 3:
+			u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 4:
+			u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 5:
+			u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 6:
+			u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 7:
+			u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER & 0xFFFF);

drivers/net/can/da8xx_pruss/pruss_can_api.c line 1165

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		default:
+			return -1;
+		}

Ditto.

Quoted 156 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +62 -0
+	}
+	return 0;
+}
+
+s16 pru_can_start_abort_tx(struct device *dev, bool bcantransmitabortflag)
+{
+	u32 u32offset;
+	u32 u32value;
+	s16 s16subrtnretval;
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+
+	u32offset = (PRUSS_INTC_STATIDXSET & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	return 0;
+}
+
+s16 pru_can_mask_ints(struct device *dev, u32 int_mask)
+{
+	return 0;
+}
+
+int pru_can_get_error_cnt(struct device *dev, u8 u8prunumber)
+{
+	return 0;
+}
+
+int pru_can_get_intc_status(struct device *dev)
+{
+	u32 u32offset = 0;
+	u32 u32getvalue = 0;
+	u32 u32clrvalue = 0;
+
+	u32offset = (PRUSS_INTC_STATCLRINT1 & 0xFFFF);
+	pruss_readl(dev, u32offset, (u32 *) &u32getvalue, 1);
+
+	if (u32getvalue & 4)
+		u32clrvalue = 34;	/* CLR Event 34 */
+
+	if (u32getvalue & 2)
+		u32clrvalue = 33;	/* CLR Event 33  */
+
+	if (u32clrvalue) {
+		u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+		pruss_writel(dev, u32offset, &u32clrvalue, 1);
+	} else
+		return -1;
+
+	return u32getvalue;
+}
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +88 -0
new file mode 100644
index 0000000..7550456
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Ganeshan N
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _PRU_CAN_API_H_
+#define _PRU_CAN_API_H_
+
+#include <linux/types.h>
+#include <linux/mfd/pruss/da8xx_pru.h>
+
+
+#define CAN_BIT_TIMINGS			(0x273)
+
+/* Timer Clock is sourced from DDR freq (PLL1 SYS CLK 2) */
+#define	TIMER_CLK_FREQ			132000000
+
+#define TIMER_SETUP_DELAY		14
+#define GPIO_SETUP_DELAY		150
+
+#define CAN_RX_PRU_0			PRUSS_NUM0
+#define CAN_TX_PRU_1			PRUSS_NUM1
+
+/* Number of Instruction in the Delay loop */
+#define DELAY_LOOP_LENGTH		2
+
+#define PRU1_BASE_ADDR			0x2000
+
+#define PRU_CAN_TX_GLOBAL_CONTROL_REGISTER		(PRU1_BASE_ADDR)
+#define PRU_CAN_TX_GLOBAL_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x04)
+#define PRU_CAN_TX_INTERRUPT_MASK_REGISTER		(PRU1_BASE_ADDR	+ 0x08)
+#define PRU_CAN_TX_INTERRUPT_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x0C)
+#define PRU_CAN_TX_MAILBOX0_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x10)
+#define PRU_CAN_TX_MAILBOX1_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x14)
+#define PRU_CAN_TX_MAILBOX2_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x18)
+#define PRU_CAN_TX_MAILBOX3_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x1C)
+#define PRU_CAN_TX_MAILBOX4_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x20)
+#define PRU_CAN_TX_MAILBOX5_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x24)
+#define PRU_CAN_TX_MAILBOX6_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x28)
+#define PRU_CAN_TX_MAILBOX7_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x2C)
+#define PRU_CAN_TX_ERROR_COUNTER_REGISTER		(PRU1_BASE_ADDR	+ 0x30)
+#define PRU_CAN_TX_TIMING_REGISTER			(PRU1_BASE_ADDR	+ 0x34)
+#define PRU_CAN_TX_CLOCK_BRP_REGISTER			(PRU1_BASE_ADDR	+ 0x38)
+
+#define PRU_CAN_TX_MAILBOX0				(PRU1_BASE_ADDR	+ 0x40)
+#define PRU_CAN_TX_MAILBOX1				(PRU1_BASE_ADDR	+ 0x50)
+#define PRU_CAN_TX_MAILBOX2				(PRU1_BASE_ADDR	+ 0x60)
+#define PRU_CAN_TX_MAILBOX3				(PRU1_BASE_ADDR	+ 0x70)
+#define PRU_CAN_TX_MAILBOX4				(PRU1_BASE_ADDR	+ 0x80)
+#define PRU_CAN_TX_MAILBOX5				(PRU1_BASE_ADDR	+ 0x90)
+#define PRU_CAN_TX_MAILBOX6				(PRU1_BASE_ADDR	+ 0xA0)
+#define PRU_CAN_TX_MAILBOX7				(PRU1_BASE_ADDR	+ 0xB0)
+
+#define PRU_CAN_TIMING_VAL_TX				(PRU1_BASE_ADDR	+ 0xC0)
+#define PRU_CAN_TIMING_VAL_TX_SJW			(PRU1_BASE_ADDR	+ 0xC4)
+#define PRU_CAN_TRANSMIT_FRAME				(PRU1_BASE_ADDR	+ 0xE0)
+
+#define PRU0_BASE_ADDR					0
+
+#define PRU_CAN_RX_GLOBAL_CONTROL_REGISTER		(PRU0_BASE_ADDR)
+#define PRU_CAN_RX_GLOBAL_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x04)
+#define PRU_CAN_RX_INTERRUPT_MASK_REGISTER		(PRU0_BASE_ADDR	+ 0x08)
+#define PRU_CAN_RX_INTERRUPT_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x0C)
+#define PRU_CAN_RX_MAILBOX0_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x10)
+#define PRU_CAN_RX_MAILBOX1_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x14)
+#define PRU_CAN_RX_MAILBOX2_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x18)
+#define PRU_CAN_RX_MAILBOX3_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x1C)
+#define PRU_CAN_RX_MAILBOX4_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x20)
+#define PRU_CAN_RX_MAILBOX5_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x24)
+#define PRU_CAN_RX_MAILBOX6_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x28)
+#define PRU_CAN_RX_MAILBOX7_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x2C)
+#define PRU_CAN_RX_MAILBOX8_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x30)
+#define PRU_CAN_RX_ERROR_COUNTER_REGISTER		(PRU0_BASE_ADDR	+ 0x34)
+#define PRU_CAN_RX_TIMING_REGISTER			(PRU0_BASE_ADDR	+ 0x38)
+#define PRU_CAN_RX_CLOCK_BRP_REGISTER			(PRU0_BASE_ADDR	+ 0x3C)
+
+#define PRU_CAN_RX_MAILBOX0				(PRU0_BASE_ADDR	+ 0x40)
+#define PRU_CAN_RX_MAILBOX1				(PRU0_BASE_ADDR	+ 0x50)
+#define PRU_CAN_RX_MAILBOX2				(PRU0_BASE_ADDR	+ 0x60)

drivers/net/can/da8xx_pruss/pruss_can_api.h line 98

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+#define PRU_CAN_RX_MAILBOX3				(PRU0_BASE_ADDR	+ 0x70)
+#define PRU_CAN_RX_MAILBOX4				(PRU0_BASE_ADDR	+ 0x80)
+#define PRU_CAN_RX_MAILBOX5				(PRU0_BASE_ADDR	+ 0x90)
+#define PRU_CAN_RX_MAILBOX6				(PRU0_BASE_ADDR	+ 0xA0)
+#define PRU_CAN_RX_MAILBOX7				(PRU0_BASE_ADDR	+ 0xB0)
+#define PRU_CAN_RX_MAILBOX8				(PRU0_BASE_ADDR	+ 0xC0)
+
+#define PRU_CAN_TIMING_VAL_RX				(PRU0_BASE_ADDR	+ 0xD0)
+#define PRU_CAN_RECEIVE_FRAME				(PRU0_BASE_ADDR	+ 0xD4)
+#define PRU_CAN_ID_MAP					(PRU0_BASE_ADDR	+ 0xF0)

Please consider using a struct to define the register layout. This would make you code much much more readable and compact:

	writel(&regs->brp_register, bitrateprescaler);

instead of:

	u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER);
	pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1);
Quoted 76 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +76 -0
+#define PRU_CAN_ERROR_ACTIVE				128
+
+#define CAN_ACK_FAILED					0xE
+#define CAN_ARBTR_FAIL					0xD
+#define CAN_BIT_ERROR					0xC
+#define CAN_TRANSMISSION_SUCCESS			0xA
+
+#define STD_DATA_FRAME					0x1
+#define EXTD_DATA_FRAME					0x2
+#define STD_REMOTE_FRAME				0x3
+#define EXTD_REMOTE_FRAME				0x4
+
+#define PRU_CAN_MAX_SJW					8
+#define PRU_CAN_MAX_PHSEG1				25
+#define PRU_CAN_MAX_PHSEG2				25
+
+#define DA8XX_PRUCANCORE_0_REGS				0x7000
+#define DA8XX_PRUCANCORE_1_REGS				0x7800
+#define PRU0_PROG_RAM_START_OFFSET			0x8000
+#define PRU1_PROG_RAM_START_OFFSET			0xC000
+#define PRU_CAN_INIT_MAX_TIMEOUT			0xFF
+
+typedef enum {
+	ecaninst0 = 0,
+	ecaninst1,
+	ecanmaxinst
+} can_instance_enum;
+
+typedef enum {
+	ecanmailbox0 = 0,
+	ecanmailbox1,
+	ecanmailbox2,
+	ecanmailbox3,
+	ecanmailbox4,
+	ecanmailbox5,
+	ecanmailbox6,
+	ecanmailbox7
+} can_mailbox_number;
+
+typedef enum {
+	ecandirectioninit = 0,
+	ecantransmit,
+	ecanreceive
+} can_transfer_direction;
+
+typedef struct {
+	u16 u16extendedidentifier;
+	u16 u16baseidentifier;
+	u8 u8data7;
+	u8 u8data6;
+	u8 u8data5;
+	u8 u8data4;
+	u8 u8data3;
+	u8 u8data2;
+	u8 u8data1;
+	u8 u8data0;
+	u16 u16datalength;
+	u16 u16crc;
+} can_mail_box_structure;
+
+typedef struct {
+	can_transfer_direction ecantransferdirection;
+} can_mailbox_config;
+
+typedef struct {
+	can_instance_enum ecaninstance;
+	can_transfer_direction ecantransferdirection;
+	can_mail_box_structure strcanmailbox;
+	can_mailbox_number ecanmailboxnumber;
+	u8 u8prunumber;
+	u32 u32globalstatus;
+	u32 u32interruptstatus;
+	u32 u32mailboxstatus;
+} can_emu_app_hndl;
+
+typedef struct {

drivers/net/can/da8xx_pruss/pruss_can_api.h line 185

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+	bool bcaninststate;
+	can_transfer_direction ecantransferdirection;
+	u32 u32apphandlerptr;
+} can_emu_drv_inst;
+
+typedef struct {
+	u8 u8syncjumpwidth;
+	u8 u8phseg1;
+	u8 u8phseg2;
+} can_bit_timing_consts;

Don't use typedef's!

Thanks.

Wolfgang.

#50 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

In reply to a message not in the archive
Quoted 1 line · Subhasish Ghosh

This patch adds support for the CAN device emulated on PRUSS.

Is this a software CAN device running on the omap dsp? Nice :) My first impression is that this driver needs a lot of work, but we'll help you.

Being new to the OMAP world here are soo many names for the same thing, i.e. the CAN core and driver:

- da8cc
- pruss
- omap
- pru

These or combination of these are used all over the code. I'm preferring one common prefix. I like the PRU_ prefix for the defines and the pru_ for functions.

Please don't encode the variable type into their names (a.k.a. polish notation), e.g. it's "u8 data" not "u8 u8data", No typedefs, enums just like defines in uppercase.

Get rid of the of the extra layer in pruss_can_api.c.

More comments inline:

Quoted 46 lines
---
 drivers/net/can/Kconfig                     |    1 +
 drivers/net/can/Makefile                    |    1 +
 drivers/net/can/da8xx_pruss/Kconfig         |   73 ++
 drivers/net/can/da8xx_pruss/Makefile        |    7 +
 drivers/net/can/da8xx_pruss/pruss_can.c     |  758 +++++++++++++++++
 drivers/net/can/da8xx_pruss/pruss_can_api.c | 1227 +++++++++++++++++++++++++++
 drivers/net/can/da8xx_pruss/pruss_can_api.h |  290 +++++++
 7 files changed, 2357 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/da8xx_pruss/Kconfig
 create mode 100644 drivers/net/can/da8xx_pruss/Makefile
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can.c
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.c
 create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.h
drivers/net/can/Kconfig +1 -0
index d5a9db6..ae8f0f9 100644
@@ -112,6 +112,7 @@ config PCH_CAN
 	  This driver can access CAN bus.
 
 source "drivers/net/can/mscan/Kconfig"
+source "drivers/net/can/da8xx_pruss/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
 
drivers/net/can/Makefile +1 -0
index 07ca159..849cdbf 100644
@@ -14,6 +14,7 @@ obj-$(CONFIG_CAN_SJA1000)	+= sja1000/
 obj-$(CONFIG_CAN_MSCAN)		+= mscan/
 obj-$(CONFIG_CAN_AT91)		+= at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
+obj-$(CONFIG_CAN_TI_DA8XX_PRU)	+= da8xx_pruss/
 obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
 obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
 obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
drivers/net/can/da8xx_pruss/Kconfig new file +0 -0
new file mode 100644
index 0000000..8b68f68

drivers/net/can/da8xx_pruss/Kconfig line 2

Quoted 2 lines
drivers/net/can/da8xx_pruss/Kconfig new file +2 -0
@@ -0,0 +1,73 @@
+#
+# CAN Lite Kernel Configuration
     ^^^^^^^^^^^^^^^

what's can lite?

Quoted 61 lines
drivers/net/can/da8xx_pruss/Kconfig new file +61 -0
+#
+config CAN_TI_DA8XX_PRU
+	depends on CAN_DEV && ARCH_DAVINCI && ARCH_DAVINCI_DA850
+	tristate "PRU based CAN emulation for DA8XX"
+	---help---
+	Enable this to emulate a CAN controller on the PRU of DA8XX.
+	If not sure, mark N
+
+config DA8XX_PRU_CANID_MBX0
+	hex "CANID for mailbox 0"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 0
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX1
+	hex "CANID for mailbox 1"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	 ---help---
+	Enter the CANID for mailbox 1
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX2
+	hex "CANID for mailbox 2"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 2
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX3
+	hex "CANID for mailbox 3"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 3
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX4
+	hex "CANID for mailbox 4"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 4
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX5
+	hex "CANID for mailbox 5"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 5
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX6
+	hex "CANID for mailbox 6"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---

drivers/net/can/da8xx_pruss/Kconfig line 73

Quoted 10 lines
drivers/net/can/da8xx_pruss/Kconfig new file +10 -0
+	Enter the CANID for mailbox 6
+	Default value is set to 0x123, change this as required.
+
+config DA8XX_PRU_CANID_MBX7
+	hex "CANID for mailbox 7"
+	depends on CAN_TI_DA8XX_PRU
+	default "0x123"
+	---help---
+	Enter the CANID for mailbox 7
+	Default value is set to 0x123, change this as required.

This doesn't fit to the Socketcan abstraction of a CAN card. Please remove. After this "da8xx_pruss/Kconfig" just contains the CAN_TI_DA8XX_PRU symbol, which can be added directly to drivers/net/can/Kconfig. (With a perhaps a simpler kconfig symbol name.)

drivers/net/can/da8xx_pruss/Makefile line 5

Quoted 11 lines
drivers/net/can/da8xx_pruss/Makefile new file +5 -0
new file mode 100644
index 0000000..48f3055
@@ -0,0 +1,7 @@
+#
+# Makefile for CAN Lite emulation
+#
+can_emu-objs :=   pruss_can.o \
+                  pruss_can_api.o

Do we need two c file? I haven't look at them, yet.

Quoted 8 lines
drivers/net/can/da8xx_pruss/Makefile new file +2 -0
+
+obj-$(CONFIG_CAN_TI_DA8XX_PRU)    += can_emu.o
drivers/net/can/da8xx_pruss/pruss_can.c new file +0 -0
new file mode 100644
index 0000000..1b3afde

drivers/net/can/da8xx_pruss/pruss_can.c line 8

Quoted 8 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +8 -0
@@ -0,0 +1,758 @@
+/*
+ *  TI DA8XX PRU CAN Emulation device driver
+ *  Author: subhasish-EvXpCiN+lbve9wHmmfpqLFaTQe2KTcn/@public.gmane.org
+ *
+ *  This driver supports TI's PRU CAN Emulation and the
+ *  specs for the same is available at <http://www.ti.com>
+ *
+ *  Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/

You'll probably do some work on the driver, so add a 2011, here :)

Quoted 19 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +19 -0
+ *
+ *  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 version 2.
+ *
+ *  This program is distributed as is WITHOUT ANY WARRANTY of any
+ *  kind, whether express or implied; without even the implied warranty
+ *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>

drivers/net/can/da8xx_pruss/pruss_can.c line 37

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/clk.h>
+#include <linux/types.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <mach/da8xx.h>
+#include "pruss_can_api.h"

Do we need a separate header file?

drivers/net/can/da8xx_pruss/pruss_can.c line 44

Quoted 7 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +7 -0
+
+#define DRV_NAME "da8xx_pruss_can"
+#define DRV_DESC "TI PRU CAN Controller Driver v0.1"
+#define PRU_CAN_START		1
+#define PRU_CAN_STOP		0
+#define MB_MIN			0
+#define MB_MAX			7

please add the common PRU_ prefis to the MB_*, too.

Quoted 9 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +9 -0
+
+#define PRU_CANMID_IDE			BIT(29)	/* Extended frame format */
+
+#define PRU_CAN_ISR_BIT_CCI		BIT(15)
+#define PRU_CAN_ISR_BIT_ESI		BIT(14)
+#define PRU_CAN_ISR_BIT_SRDI		BIT(13)
+#define PRU_CAN_ISR_BIT_RRI		BIT(8)
+
+#define PRU_CAN_MBXSR_BIT_STATE		BIT(7)

drivers/net/can/da8xx_pruss/pruss_can.c line 63

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+#define PRU_CAN_MBXSR_BIT_TC		BIT(6)
+#define PRU_CAN_MBXSR_BIT_ERR		BIT(5)
+#define PRU_CAN_MBXSR_BIT_OF		BIT(0)
+
+#define PRU_CAN_GSR_BIT_TXM		BIT(7)
+#define PRU_CAN_GSR_BIT_RXM		BIT(6)
+#define PRU_CAN_GSR_BIT_CM		BIT(5)
+#define PRU_CAN_GSR_BIT_EPM		BIT(4)
+#define PRU_CAN_GSR_BIT_BFM		BIT(3)
+#define RTR_MBX_NO			8

We don't have special mailboxes for RTR, pleae remove.

add the PRU_ prefix here, too.

drivers/net/can/da8xx_pruss/pruss_can.c line 66

Quoted 3 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +3 -0
+#define MAX_INIT_RETRIES		20
+#define L138_PRU_ARM_FREQ		312000
+#define DFLT_PRU_FREQ			156000000

Any change that you get these values from the a clock device? e.g.:
http://lxr.linux.no/linux+v2.6.37/drivers/net/can/flexcan.c#L909
http://lxr.linux.no/linux+v2.6.37/drivers/net/can/flexcan.c#L946

drivers/net/can/da8xx_pruss/pruss_can.c line 67

Quoted 1 line
drivers/net/can/da8xx_pruss/pruss_can.c new file +1 -0
+#define DFLT_PRU_BITRATE		125000

Please remove, we don't have a default bitrate.

drivers/net/can/da8xx_pruss/pruss_can.c line 76

Quoted 9 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +9 -0
+
+#define CONFIG_DA8XX_PRU_CANID_MBX0	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX1	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX2	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX3	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX4	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX5	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX6	0x123
+#define CONFIG_DA8XX_PRU_CANID_MBX7	0x123

as alreadt said, we don't have default canids, please remove.

drivers/net/can/da8xx_pruss/pruss_can.c line 83

Quoted 7 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +7 -0
+
+#ifdef __CAN_DEBUG
+#define __can_debug(fmt, args...) printk(KERN_DEBUG "can_debug: " fmt, ## args)
+#else
+#define __can_debug(fmt, args...)
+#endif
+#define __can_err(fmt, args...) printk(KERN_ERR "can_err: " fmt, ## args)

please use pr_<level> and and friends, use pr_fmt to set a common prefix. Or even better use netdev_<level>.

drivers/net/can/da8xx_pruss/pruss_can.c line 93

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+
+/*
+ * omapl_pru can private data
+ */
+struct omapl_pru_can_priv {
+	struct can_priv can;
+	struct workqueue_struct *pru_can_wQ;
+	struct work_struct rx_work;
+	struct net_device *ndev;
+	struct device *dev; /* pdev->dev */

nitpick: pointless comment :)

drivers/net/can/da8xx_pruss/pruss_can.c line 97

Quoted 4 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +4 -0
+	struct clk *clk_timer;
+	u32 timer_freq;
+	can_emu_app_hndl can_tx_hndl;
+	can_emu_app_hndl can_rx_hndl;

please no new typedefs.

drivers/net/can/da8xx_pruss/pruss_can.c line 105

Quoted 8 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +8 -0
+	const struct firmware *fw_rx;
+	const struct firmware *fw_tx;
+	spinlock_t mbox_lock;
+	u32 trx_irq;
+	u32 tx_head;
+	u32 tx_tail;
+	u32 tx_next;
+	u32 rx_next;

If these don't reflect register values, just use "unsigned int"s here.

drivers/net/can/da8xx_pruss/pruss_can.c line 109

Quoted 4 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +4 -0
+};
+
+static int omapl_pru_can_get_state(const struct net_device *ndev,
+				   enum can_state *state)

just for consistency most other can drivers use "struct net_device *dev".

Quoted 6 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +6 -0
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	*state = priv->can.state;
+	return 0;
+}
+

drivers/net/can/da8xx_pruss/pruss_can.c line 125

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +10 -0
+static int omapl_pru_can_set_bittiming(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct can_bittiming *bt = &priv->can.bittiming;
+	long bit_error = 0;
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) {
+		dev_warn(priv->dev, "WARN: Triple"
+			 "sampling not set due to h/w limitations");
+	}

No need to check this, just set the modes you support in "priv->can.ctrlmode_supported".

http://lxr.linux.no/linux+v2.6.37/drivers/net/can/at91_can.c#L1091

drivers/net/can/da8xx_pruss/pruss_can.c line 127

Quoted 2 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +2 -0
+	if (pru_can_calc_timing(priv->dev, priv->can.clock.freq,
+				bt->bitrate) != 0)

The above function does _set_ the bit timing into the hardware, calling it "can_calc_timing" is a bit misleading.

Don't calculate the bit timing yoursef. Please define your bittiming_const and set it, see:

http://lxr.linux.no/linux+v2.6.37/drivers/net/can/at91_can.c#L173
http://lxr.linux.no/linux+v2.6.37/drivers/net/can/at91_can.c#L1088

Quoted 1,885 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +631 -0
+		return -EINVAL;
+	bit_error =
+	    (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) -
+	      bt->bitrate) * 1000) / bt->bitrate;
+	if (bit_error) {
+		bit_error =
+		    (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) -
+		      bt->bitrate) * 1000000) / bt->bitrate;
+		printk(KERN_INFO "\nBitrate error %ld.%ld%%\n",
+			bit_error / 10000, bit_error % 1000);
+	} else
+		printk(KERN_INFO "\nBitrate error 0.0%%\n");
+
+	return 0;
+}
+
+static void omapl_pru_can_stop(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	u16 int_mask = 0;
+
+	pru_can_mask_ints(priv->dev, int_mask);	/* mask all ints */
+	pru_can_start_abort_tx(priv->dev, PRU_CAN_STOP);
+	priv->can.state = CAN_STATE_STOPPED;
+}
+
+/*
+ * This is to just set the can state to ERROR_ACTIVE
+ *	ip link set canX up type can bitrate 125000
+ */
+static void omapl_pru_can_start(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	u16 int_mask = 0xFFFF;
+
+	if (priv->can.state != CAN_STATE_STOPPED)
+		omapl_pru_can_stop(ndev);
+
+	pru_can_mask_ints(priv->dev, int_mask);	/* unmask all ints */
+
+	pru_can_get_global_status(priv->dev, &priv->can_tx_hndl);
+	pru_can_get_global_status(priv->dev, &priv->can_rx_hndl);
+
+	if (PRU_CAN_GSR_BIT_EPM & priv->can_tx_hndl.u32globalstatus)
+		priv->can.state = CAN_STATE_ERROR_PASSIVE;
+	else if (PRU_CAN_GSR_BIT_BFM & priv->can_tx_hndl.u32globalstatus)
+		priv->can.state = CAN_STATE_BUS_OFF;
+	else
+		priv->can.state = CAN_STATE_ERROR_ACTIVE;
+}
+
+static int omapl_pru_can_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+	int ret = 0;
+
+	switch (mode) {
+	case CAN_MODE_START:
+		omapl_pru_can_start(ndev);
+		if (netif_queue_stopped(ndev))
+			netif_wake_queue(ndev);
+		break;
+	case CAN_MODE_STOP:
+		omapl_pru_can_stop(ndev);
+		if (!netif_queue_stopped(ndev))
+			netif_stop_queue(ndev);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+	return ret;
+}
+
+static netdev_tx_t omapl_pru_can_start_xmit(struct sk_buff *skb,
+					    struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	int count;
+	u8 *data = cf->data;
+	u8 dlc = cf->can_dlc;
+	u8 *ptr8data = NULL;
+
+	netif_stop_queue(ndev);
+	if (cf->can_id & CAN_EFF_FLAG)	/* Extended frame format */
+		*((u32 *) &priv->can_tx_hndl.strcanmailbox) =
+		    (cf->can_id & CAN_EFF_MASK) | PRU_CANMID_IDE;
+	else			/* Standard frame format */
+		*((u32 *) &priv->can_tx_hndl.strcanmailbox) =
+		    (cf->can_id & CAN_SFF_MASK) << 18;
+
+	if (cf->can_id & CAN_RTR_FLAG)	/* Remote transmission request */
+		*((u32 *) &priv->can_tx_hndl.strcanmailbox) |= CAN_RTR_FLAG;
+
+	ptr8data = &priv->can_tx_hndl.strcanmailbox.u8data7 + (dlc - 1);
+	for (count = 0; count < (u8) dlc; count++) {
+		*ptr8data-- = *data++;
+	}
+	*((u32 *) &priv->can_tx_hndl.strcanmailbox.u16datalength) = (u32) dlc;
+/*
+ * search for the next available mbx
+ * if the next mbx is busy, then try the next + 1
+ * do this until the head is reached.
+ * if still unable to tx, stop accepting any packets
+ * if able to tx and the head is reached, then reset next to tail, i.e mbx0
+ * if head is not reached, then just point to the next mbx
+ */
+	for (; priv->tx_next <= priv->tx_head; priv->tx_next++) {
+		priv->can_tx_hndl.ecanmailboxnumber =
+		    (can_mailbox_number) priv->tx_next;
+		if (-1 == pru_can_write_data_to_mailbox(priv->dev,
+					&priv->can_tx_hndl)) {
+			if (priv->tx_next == priv->tx_head) {
+				priv->tx_next = priv->tx_tail;
+				if (!netif_queue_stopped(ndev))
+					netif_stop_queue(ndev);	/* IF stalled */
+				dev_err(priv->dev,
+					"%s: no tx mbx available", __func__);
+				return NETDEV_TX_BUSY;
+			} else
+				continue;
+		} else {
+			/* set transmit request */
+			pru_can_tx(priv->dev, priv->tx_next, CAN_TX_PRU_1);
+			pru_can_tx_mode_set(priv->dev, false, ecanreceive);
+			pru_can_tx_mode_set(priv->dev, true, ecantransmit);
+			pru_can_start_abort_tx(priv->dev, PRU_CAN_START);
+			priv->tx_next++;
+			can_put_echo_skb(skb, ndev, 0);
+			break;
+		}
+	}
+	if (priv->tx_next > priv->tx_head) {
+		priv->tx_next = priv->tx_tail;
+	}
+	return NETDEV_TX_OK;
+}
+
+static int omapl_pru_can_rx(struct net_device *ndev, u32 mbxno)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	u32 pru_can_mbx_data;
+	u8 *data = NULL;
+	u8 *ptr8data = NULL;
+	int count = 0;
+
+	skb = alloc_can_skb(ndev, &cf);
+	if (!skb) {
+		if (printk_ratelimit())
+			dev_err(priv->dev,
+				"alloc_can_skb() failed\n");
+		return -ENOMEM;
+	}
+	data = cf->data;
+	/*      get payload */
+	priv->can_rx_hndl.ecanmailboxnumber = (can_mailbox_number) mbxno;
+	if (pru_can_get_data_from_mailbox(priv->dev, &priv->can_rx_hndl)) {
+		__can_err("failed to get data from mailbox\n");
+		return -EAGAIN;
+	}
+	/* give ownweship to pru */
+	pru_can_tx(priv->dev, mbxno, CAN_RX_PRU_0);
+
+	/* get data length code */
+	cf->can_dlc =
+	    get_can_dlc(*
+			((u32 *) &priv->can_rx_hndl.strcanmailbox.
+			 u16datalength) & 0xF);
+	if (cf->can_dlc <= 4) {
+		ptr8data =
+		    &priv->can_rx_hndl.strcanmailbox.u8data3 + (4 -
+								cf->can_dlc);
+		for (count = 0; count < cf->can_dlc; count++) {
+			*data++ = *ptr8data++;
+		}
+	} else {
+		ptr8data = &priv->can_rx_hndl.strcanmailbox.u8data3;
+		for (count = 0; count < 4; count++) {
+			*data++ = *ptr8data++;
+		}
+		ptr8data =
+		    &priv->can_rx_hndl.strcanmailbox.u8data4 - (cf->can_dlc -
+								5);
+		for (count = 0; count < cf->can_dlc - 4; count++) {
+			*data++ = *ptr8data++;
+		}
+	}
+
+	pru_can_mbx_data = *((u32 *) &priv->can_rx_hndl.strcanmailbox);
+	/* get id extended or std */
+	if (pru_can_mbx_data & PRU_CANMID_IDE)
+		cf->can_id = (pru_can_mbx_data & CAN_EFF_MASK) | CAN_EFF_FLAG;
+	else
+		cf->can_id = (pru_can_mbx_data >> 18) & CAN_SFF_MASK;
+
+	if (pru_can_mbx_data & CAN_RTR_FLAG)
+		cf->can_id |= CAN_RTR_FLAG;
+
+	netif_rx_ni(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+	return 0;
+}
+
+static int omapl_pru_can_err(struct net_device *ndev, int int_status,
+			     int err_status)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	int tx_err_cnt, rx_err_cnt;
+
+	/* propogate the error condition to the can stack */
+	skb = alloc_can_err_skb(ndev, &cf);
+	if (!skb) {
+		if (printk_ratelimit())
+			dev_err(priv->dev,
+				"alloc_can_err_skb() failed\n");
+		return -ENOMEM;
+	}
+
+	if (err_status & PRU_CAN_GSR_BIT_EPM) {	/* error passive int */
+		priv->can.state = CAN_STATE_ERROR_PASSIVE;
+		++priv->can.can_stats.error_passive;
+		cf->can_id |= CAN_ERR_CRTL;
+		tx_err_cnt = pru_can_get_error_cnt(priv->dev, CAN_TX_PRU_1);
+		rx_err_cnt = pru_can_get_error_cnt(priv->dev, CAN_RX_PRU_0);
+		if (tx_err_cnt > 127)
+			cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+		if (rx_err_cnt > 127)
+			cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+
+		dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n");
+	}
+
+	if (err_status & PRU_CAN_GSR_BIT_BFM) {
+		priv->can.state = CAN_STATE_BUS_OFF;
+		cf->can_id |= CAN_ERR_BUSOFF;
+		/*
+		 *      Disable all interrupts in bus-off to avoid int hog
+		 *      this should be handled by the pru
+		 */
+		pru_can_mask_ints(priv->dev, 0xFFFF);
+		can_bus_off(ndev);
+		dev_dbg(priv->ndev->dev.parent, "Bus off mode\n");
+	}
+
+	netif_rx(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+	return 0;
+}
+
+void omapl_pru_can_rx_wQ(struct work_struct *work)
+{
+	struct omapl_pru_can_priv *priv = container_of(work,
+			struct omapl_pru_can_priv, rx_work);
+	struct net_device *ndev = priv->ndev;
+	u32 bit_set, mbxno = 0;
+
+	if (-1 == pru_can_get_intr_status(priv->dev, &priv->can_rx_hndl))
+		return;
+
+	if (PRU_CAN_ISR_BIT_RRI & priv->can_rx_hndl.u32interruptstatus) {
+		mbxno = RTR_MBX_NO;
+		omapl_pru_can_rx(ndev, mbxno);
+	} else {
+		/* Extract the mboxno from the status */
+		for (bit_set = 0; ((priv->can_rx_hndl.u32interruptstatus & 0xFF)
+						>> bit_set != 0); bit_set++)
+		;
+		if (0 == bit_set) {
+			dev_err(priv->dev,
+				"%s: invalid mailbox number: %X\n", __func__,
+				priv->can_rx_hndl.u32interruptstatus);
+		} else {
+			mbxno = bit_set - 1;
+			if (PRU_CAN_ISR_BIT_ESI & priv->can_rx_hndl.
+			    u32interruptstatus) {
+				pru_can_get_global_status(priv->dev,
+					&priv->can_rx_hndl);
+				omapl_pru_can_err(ndev,
+				priv->can_rx_hndl.u32interruptstatus,
+				priv->can_rx_hndl.u32globalstatus);
+			} else {
+				omapl_pru_can_rx(ndev, mbxno);
+			}
+		}
+	}
+}
+
+irqreturn_t omapl_tx_can_intr(int irq, void *dev_id)
+{
+	struct net_device *ndev = dev_id;
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	u32 bit_set, mbxno;
+
+	pru_can_get_intr_status(priv->dev, &priv->can_tx_hndl);
+	if ((PRU_CAN_ISR_BIT_CCI & priv->can_tx_hndl.u32interruptstatus)
+	    || (PRU_CAN_ISR_BIT_SRDI & priv->can_tx_hndl.u32interruptstatus)) {
+		__can_debug("tx_int_status = 0x%X\n",
+			    priv->can_tx_hndl.u32interruptstatus);
+		can_free_echo_skb(ndev, 0);
+	} else {
+		for (bit_set = 0; ((priv->can_tx_hndl.u32interruptstatus & 0xFF)
+						>> bit_set != 0); bit_set++)
+		;
+		if (0 == bit_set) {
+			__can_err("%s: invalid mailbox number\n", __func__);
+			can_free_echo_skb(ndev, 0);
+		} else {
+			mbxno = bit_set - 1;	/* mail box numbering starts from 0 */
+			if (PRU_CAN_ISR_BIT_ESI & priv->can_tx_hndl.
+			    u32interruptstatus) {
+				/* read gsr and ack pru */
+				pru_can_get_global_status(priv->dev, &priv->can_tx_hndl);
+				omapl_pru_can_err(ndev,
+						  priv->can_tx_hndl.
+						  u32interruptstatus,
+						  priv->can_tx_hndl.
+						  u32globalstatus);
+			} else {
+				stats->tx_packets++;
+				/* stats->tx_bytes += dlc; */
+				/*can_get_echo_skb(ndev, 0);*/
+			}
+		}
+	}
+	if (netif_queue_stopped(ndev))
+		netif_wake_queue(ndev);
+
+	can_get_echo_skb(ndev, 0);
+	pru_can_tx_mode_set(priv->dev, true, ecanreceive);
+	return IRQ_HANDLED;
+}
+
+irqreturn_t omapl_rx_can_intr(int irq, void *dev_id)
+{
+
+	struct net_device *ndev = dev_id;
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	u32 intc_status = 0;
+
+	intc_status = pru_can_get_intc_status(priv->dev);
+	if (intc_status & 4)
+		return omapl_tx_can_intr(irq, dev_id);
+	if (intc_status & 2) {
+		if (!work_pending(&priv->rx_work))
+			queue_work(priv->pru_can_wQ, &priv->rx_work);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int omapl_pru_can_open(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+	int err;
+
+	/* register interrupt handler */
+	err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED,
+			  "pru_can_irq", ndev);
+	if (err) {
+		dev_err(priv->dev, "error requesting rx interrupt\n");
+		goto exit_trx_irq;
+	}
+	/* common open */
+	err = open_candev(ndev);
+	if (err) {
+		dev_err(priv->dev, "open_candev() failed %d\n", err);
+		goto exit_open;
+	}
+
+	pru_can_emu_init(priv->dev, priv->can.clock.freq);
+	priv->tx_tail = MB_MIN;
+	priv->tx_head = MB_MAX;
+
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX0, 0);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX1, 1);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX2, 2);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX3, 3);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX4, 4);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX5, 5);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX6, 6);
+	pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX7, 7);
+
+	omapl_pru_can_start(ndev);
+	netif_start_queue(ndev);
+	return 0;
+
+exit_open:
+	free_irq(priv->trx_irq, ndev);
+exit_trx_irq:
+	return err;
+}
+
+static int omapl_pru_can_close(struct net_device *ndev)
+{
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+
+	if (!netif_queue_stopped(ndev))
+		netif_stop_queue(ndev);
+
+	close_candev(ndev);
+
+	free_irq(priv->trx_irq, ndev);
+	return 0;
+}
+
+static const struct net_device_ops omapl_pru_can_netdev_ops = {
+	.ndo_open		= omapl_pru_can_open,
+	.ndo_stop		= omapl_pru_can_close,
+	.ndo_start_xmit		= omapl_pru_can_start_xmit,
+};
+
+static int __devinit omapl_pru_can_probe(struct platform_device *pdev)
+{
+	struct net_device *ndev = NULL;
+	const struct da8xx_pru_can_data *pdata;
+	struct omapl_pru_can_priv *priv = NULL;
+	struct device *dev = &pdev->dev;
+	u32 err;
+
+	pdata = dev->platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "platform data not found\n");
+		return -EINVAL;
+	}
+
+	ndev = alloc_candev(sizeof(struct omapl_pru_can_priv), MB_MAX + 1);
+	if (!ndev) {
+		dev_err(&pdev->dev, "alloc_candev failed\n");
+		err = -ENOMEM;
+		goto probe_exit;
+	}
+	priv = netdev_priv(ndev);
+
+	priv->trx_irq = platform_get_irq(to_platform_device(dev->parent), 0);
+	if (!priv->trx_irq) {
+		dev_err(&pdev->dev, "unable to get pru interrupt resources!\n");
+		err = -ENODEV;
+		goto probe_exit;
+	}
+
+	priv->ndev = ndev;
+	priv->dev = dev; /* priv->dev = pdev->dev */
+
+	priv->can.bittiming_const = NULL;
+	priv->can.do_set_bittiming = omapl_pru_can_set_bittiming;
+	priv->can.do_set_mode = omapl_pru_can_set_mode;
+	priv->can.do_get_state = omapl_pru_can_get_state;
+	priv->can_tx_hndl.u8prunumber = CAN_TX_PRU_1;
+	priv->can_rx_hndl.u8prunumber = CAN_RX_PRU_0;
+
+	/* we support local echo, no arp */
+	ndev->flags |= (IFF_ECHO | IFF_NOARP);
+
+	/* pdev->dev->device_private->driver_data = ndev */
+	platform_set_drvdata(pdev, ndev);
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+	ndev->netdev_ops = &omapl_pru_can_netdev_ops;
+
+	priv->can.clock.freq = pruss_get_clk_freq(priv->dev);
+
+	priv->clk_timer = clk_get(&pdev->dev, "pll1_sysclk2");
+	if (IS_ERR(priv->clk_timer)) {
+		dev_err(&pdev->dev, "no timer clock available\n");
+		err = PTR_ERR(priv->clk_timer);
+		priv->clk_timer = NULL;
+		goto probe_exit_candev;
+	}
+	priv->timer_freq = clk_get_rate(priv->clk_timer);
+
+	err = register_candev(ndev);
+	if (err) {
+		dev_err(&pdev->dev, "register_candev() failed\n");
+		err = -ENODEV;
+		goto probe_exit_clk;
+	}
+
+	err = request_firmware(&priv->fw_tx, "PRU_CAN_Emulation_Tx.bin",
+			&pdev->dev);
+	if (err) {
+		dev_err(&pdev->dev, "can't load firmware\n");
+		err = -ENODEV;
+		goto probe_exit_clk;
+	}
+
+	dev_info(&pdev->dev, "fw_tx size %d. downloading...\n",
+		 priv->fw_tx->size);
+
+	err = request_firmware(&priv->fw_rx, "PRU_CAN_Emulation_Rx.bin",
+			&pdev->dev);
+	if (err) {
+		dev_err(&pdev->dev, "can't load firmware\n");
+		err = -ENODEV;
+		goto probe_release_fw;
+	}
+	dev_info(&pdev->dev, "fw_rx size %d. downloading...\n",
+		 priv->fw_rx->size);
+
+	/* init the pru */
+	pru_can_emu_init(priv->dev, priv->can.clock.freq);
+	udelay(200);
+
+	pruss_enable(priv->dev, CAN_RX_PRU_0);
+	pruss_enable(priv->dev, CAN_TX_PRU_1);
+
+	/* download firmware into pru */
+	err = pruss_load(priv->dev, CAN_RX_PRU_0,
+		(u32 *)priv->fw_rx->data, (priv->fw_rx->size / 4));
+	if (err) {
+		dev_err(&pdev->dev, "firmware download error\n");
+		err = -ENODEV;
+		goto probe_release_fw_1;
+	}
+	err = pruss_load(priv->dev, CAN_TX_PRU_1,
+		(u32 *)priv->fw_tx->data, (priv->fw_tx->size / 4));
+	if (err) {
+		dev_err(&pdev->dev, "firmware download error\n");
+		err = -ENODEV;
+		goto probe_release_fw_1;
+	}
+
+	if (pru_can_calc_timing(priv->dev, DFLT_PRU_FREQ,
+				DFLT_PRU_BITRATE) != 0)
+		return -EINVAL;
+
+	pruss_run(priv->dev, CAN_RX_PRU_0);
+	pruss_run(priv->dev, CAN_TX_PRU_1);
+
+	/*Create The Work Queue */
+	priv->pru_can_wQ = create_freezeable_workqueue("omapl_pru_wQ");
+	if (priv->pru_can_wQ == NULL) {
+		dev_err(&pdev->dev, "failed to create work queue\n");
+		err = -ENODEV;
+		goto probe_release_fw_1;
+	}
+
+	INIT_WORK(&priv->rx_work, omapl_pru_can_rx_wQ);
+	dev_info(&pdev->dev,
+		 "%s device registered (trx_irq = %d,  clk = %d)\n",
+		 DRV_NAME, priv->trx_irq, priv->can.clock.freq);
+
+	return 0;
+
+probe_release_fw_1:
+	release_firmware(priv->fw_rx);
+probe_release_fw:
+	release_firmware(priv->fw_tx);
+probe_exit_clk:
+	clk_put(priv->clk_timer);
+probe_exit_candev:
+	if (NULL != ndev)
+		free_candev(ndev);
+probe_exit:
+	return err;
+}
+
+static int __devexit omapl_pru_can_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct omapl_pru_can_priv *priv = netdev_priv(ndev);
+
+	omapl_pru_can_stop(ndev);
+
+	pru_can_emu_exit(priv->dev);
+	release_firmware(priv->fw_tx);
+	release_firmware(priv->fw_rx);
+	clk_put(priv->clk_timer);
+	flush_workqueue(priv->pru_can_wQ);
+	destroy_workqueue(priv->pru_can_wQ);
+	unregister_candev(ndev);
+	free_candev(ndev);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int omapl_pru_can_suspend(struct platform_device *pdev,
+			pm_message_t mesg)
+{
+	dev_info(&pdev->dev, "%s not yet implemented\n", __func__);
+	return 0;
+}
+
+static int omapl_pru_can_resume(struct platform_device *pdev)
+{
+	dev_info(&pdev->dev, "%s not yet implemented\n", __func__);
+	return 0;
+}
+#else
+#define omapl_pru_can_suspend NULL
+#define omapl_pru_can_resume NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver omapl_pru_can_driver = {
+	.probe		= omapl_pru_can_probe,
+	.remove		= __devexit_p(omapl_pru_can_remove),
+	.suspend	= omapl_pru_can_suspend,
+	.resume		= omapl_pru_can_resume,
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init omapl_pru_can_init(void)
+{
+	__can_debug(KERN_INFO DRV_DESC "\n");
+	return platform_driver_register(&omapl_pru_can_driver);
+}
+
+module_init(omapl_pru_can_init);
+
+static void __exit omapl_pru_can_exit(void)
+{
+	__can_debug(KERN_INFO DRV_DESC " unloaded\n");
+	platform_driver_unregister(&omapl_pru_can_driver);
+}
+
+module_exit(omapl_pru_can_exit);
+
+MODULE_AUTHOR("Subhasish Ghosh <subhasish-EvXpCiN+lbve9wHmmfpqLFaTQe2KTcn/@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("omapl pru CAN netdevice driver");
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +1227 -0
new file mode 100644
index 0000000..2f7438a
@@ -0,0 +1,1227 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Wilfred Felix
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include "pruss_can_api.h"
+
+static can_emu_drv_inst gstr_can_inst[ecanmaxinst];
+
+/*
+ * pru_can_set_brp()	Updates the  BRP register of PRU0
+ * and PRU1 of OMAP L138. This API will be called by the
+ * Application to updtae the BRP register of PRU0 and PRU1
+ *
+ * param	u16bitrateprescaler		The can bus bitrate
+ * prescaler value be set
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_set_brp(struct device *dev, u16 u16bitrateprescaler)
+{
+
+	u32 u32offset;
+
+	if (u16bitrateprescaler > 255) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1);
+
+	u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1);
+
+	return 0;
+
+}
+
+/*
+ * pru_can_set_bit_timing()		Updates the timing register
+ * of PRU0 and PRU1 of OMAP L138. This API will be called by
+ * the Application to updtae the timing register of PRU0 and PRU1
+ *
+ * param	pstrbittiming		Pointer to structure holding
+ * the bit timing values for can bus.
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_set_bit_timing(struct device *dev,
+		can_bit_timing_consts *pstrbittiming)
+{
+
+	u32 u32offset;
+	u32 u32serregister;
+
+	u32serregister = 0;
+
+	if (pstrbittiming == NULL) {
+		return -1;
+	}
+
+	if ((pstrbittiming->u8syncjumpwidth > PRU_CAN_MAX_SJW) ||
+	    (pstrbittiming->u8phseg1 > PRU_CAN_MAX_PHSEG1) ||
+	    (pstrbittiming->u8phseg2 > PRU_CAN_MAX_PHSEG2)) {
+		return -1;
+	}
+
+	u32serregister = u32serregister |
+			((pstrbittiming->u8syncjumpwidth << 7) |
+			(pstrbittiming->u8phseg1 << 3) |
+			(pstrbittiming->u8phseg2));
+
+	u32offset = (PRU_CAN_TX_TIMING_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u32serregister, 1);
+
+	u32offset = (PRU_CAN_RX_TIMING_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u32serregister, 1);
+
+	return 0;
+}
+
+
+/*
+ * pru_can_calc_timing()
+ * Updates the  timing values of PRU0 and PRU1 of OMAP L138.
+ * This API will be called by the
+ * Application to updtae the timing values of PRU0 and PRU1
+ *
+ * return   SUCCESS or FAILURE
+ */
+
+s16 pru_can_calc_timing(struct device *dev, u32 pru_freq, u32 bit_rate)
+{
+	u16 u16phaseseg1;
+	u16 u16phaseseg2;
+	u32 u32offset;
+	u32 u32timing_value;
+	u32 u32setup_value;
+	u32timing_value = TIMER_CLK_FREQ / bit_rate;
+	u32offset = (PRU_CAN_TIMING_VAL_TX);
+	pruss_writel(dev, u32offset, (u32 *) &u32timing_value, 4);
+	pruss_readl(dev, u32offset, (u32 *) &u32timing_value, 4);
+	u32setup_value =
+	    (GPIO_SETUP_DELAY * (pru_freq / 1000000) / 1000) /
+	    DELAY_LOOP_LENGTH;
+	u32offset = (PRU_CAN_TIMING_VAL_TX_SJW);
+	pruss_writel(dev, u32offset, (u32 *) &u32setup_value, 4);
+	u16phaseseg1 = (u16) (u32timing_value / 2);
+	u16phaseseg2 = u32timing_value - u16phaseseg1;
+	u16phaseseg1 -= TIMER_SETUP_DELAY;
+	u16phaseseg2 -= TIMER_SETUP_DELAY;
+	u32setup_value = (u16phaseseg1 << 16) | u16phaseseg2;
+	u32offset = (PRU_CAN_TIMING_VAL_RX);
+	pruss_writel(dev, u32offset, (u32 *) &u32setup_value, 4);
+	u32offset = (PRU_CAN_TIMING_VAL_RX + 4);
+	pruss_writel(dev, u32offset, (u32 *) &u32timing_value, 4);
+
+	return 0;
+}
+
+/*
+ * pru_can_write_data_to_mailbox()
+ * Updates the transmit mailboxes of PRU1 of OMAP L138.
+ * This API will be called by the Application to update
+ * the transmit mailboxes of PRU1
+ *
+ * param  pu16canframedata	Can mailbox data buffer
+ *
+ * param  u8mailboxnum		Mailbox to be updated
+ *
+ * return SUCCESS or FAILURE
+ */
+s16 pru_can_write_data_to_mailbox(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl)
+{
+	s16 s16subrtnretval;
+	u32 u32offset;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	switch ((u8) pstremuapphndl->ecanmailboxnumber) {
+	case 0:
+		u32offset = (PRU_CAN_TX_MAILBOX0);
+		break;
+	case 1:
+		u32offset = (PRU_CAN_TX_MAILBOX1);
+		break;
+	case 2:
+		u32offset = (PRU_CAN_TX_MAILBOX2);
+		break;
+	case 3:
+		u32offset = (PRU_CAN_TX_MAILBOX3);
+		break;
+	case 4:
+		u32offset = (PRU_CAN_TX_MAILBOX4);
+		break;
+	case 5:
+		u32offset = (PRU_CAN_TX_MAILBOX5);
+		break;
+	case 6:
+		u32offset = (PRU_CAN_TX_MAILBOX6);
+		break;
+	case 7:
+		u32offset = (PRU_CAN_TX_MAILBOX7);
+		break;
+	default:
+		return -1;
+	}
+
+	s16subrtnretval = pruss_writel(dev, u32offset,
+		(u32 *) &(pstremuapphndl->strcanmailbox), 4);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * pru_can_get_data_from_mailbox()
+ * Receive data from the receive mailboxes of PRU0  of OMAP L138.
+ * This API will be called by the Application to get data from
+ * the receive mailboxes of PRU0
+ *
+ * param  pu16canframedata	Can mailbox data buffer
+ *
+ * param  u8mailboxnum		Mailbox to be updated
+ *
+ * return SUCCESS or FAILURE
+ */
+s16 pru_can_get_data_from_mailbox(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)
+{
+	s16 s16subrtnretval;
+	u32 u32offset;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	switch ((u8) pstremuapphndl->ecanmailboxnumber) {
+	case 0:
+		u32offset = (PRU_CAN_RX_MAILBOX0);
+		break;
+	case 1:
+		u32offset = (PRU_CAN_RX_MAILBOX1);
+		break;
+	case 2:
+		u32offset = (PRU_CAN_RX_MAILBOX2);
+		break;
+	case 3:
+		u32offset = (PRU_CAN_RX_MAILBOX3);
+		break;
+	case 4:
+		u32offset = (PRU_CAN_RX_MAILBOX4);
+		break;
+	case 5:
+		u32offset = (PRU_CAN_RX_MAILBOX5);
+		break;
+	case 6:
+		u32offset = (PRU_CAN_RX_MAILBOX6);
+		break;
+	case 7:
+		u32offset = (PRU_CAN_RX_MAILBOX7);
+		break;
+	case 8:
+		u32offset = (PRU_CAN_RX_MAILBOX8);
+		break;
+	default:
+		return -1;
+	}
+
+	s16subrtnretval =
+	    pruss_readl(dev, u32offset,
+		  (u32 *) &(pstremuapphndl->strcanmailbox),
+				  4);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * pru_can_receive_id_map()
+ * Receive mailboxes ID Mapping of PRU0  of OMAP L138.
+ * This API will be called by the Application
+ * to map the IDs  to receive mailboxes of PRU0
+ *
+ * param  u32nodeid		Can node ID
+ *
+ * param  ecanmailboxno		Mailbox to be mapped
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_rx_id_map(struct device *dev, u32 u32nodeid,
+		can_mailbox_number ecanmailboxno)
+{
+
+	pruss_writel(dev, (PRU_CAN_ID_MAP +
+		(((u8) ecanmailboxno) * 4)), (u32 *) &u32nodeid, 1);
+
+	return 0;
+}
+
+/*
+ * pru_can_get_intr_status()
+ * Gets the interrupts status register value.
+ * This API will be called by the Application
+ * to get the interrupts status register value
+ *
+ * param  u8prunumber	PRU number for which IntStatusReg
+ * has to be read
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_get_intr_status(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)
+{
+	u32 u32offset;
+	s16 s16subrtnretval = -1;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) {
+		u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER);
+	} else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) {
+		u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER);
+	} else {
+		return -1;
+	}
+
+	s16subrtnretval = pruss_readl(dev, u32offset,
+		(u32 *) &pstremuapphndl->u32interruptstatus, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * pru_can_get_global_status()	Gets the globalstatus
+ * register value. This API will be called by the Application
+ * to  get the global status register value
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_get_global_status(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)
+{
+	u32 u32offset;
+	int s16subrtnretval = -1;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) {
+		u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER);
+	} else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) {
+		u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER);
+	} else {
+		return -1;
+	}
+
+	s16subrtnretval = pruss_readl(dev, u32offset,
+		(u32 *) &pstremuapphndl->u32globalstatus, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * pru_can_get_mailbox_status()		Gets the mailbox status
+ * register value. This API will be called by the Application
+ * to get the mailbox status register value
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_get_mailbox_status(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)
+{
+	u32 u32offset;
+	s16 s16subrtnretval = -1;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) {
+		switch (pstremuapphndl->ecanmailboxnumber) {
+		case 0:
+			u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER);
+			break;
+		case 1:
+			u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER);
+			break;
+		case 2:
+			u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER);
+			break;
+		case 3:
+			u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER);
+			break;
+		case 4:
+			u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER);
+			break;
+		case 5:
+			u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER);
+			break;
+		case 6:
+			u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER);
+			break;
+		case 7:
+			u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER);
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) {
+		switch (pstremuapphndl->ecanmailboxnumber) {
+		case 0:
+			u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER);
+			break;
+		case 1:
+			u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER);
+			break;
+		case 2:
+			u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER);
+			break;
+		case 3:
+			u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER);
+			break;
+		case 4:
+			u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER);
+			break;
+		case 5:
+			u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER);
+			break;
+		case 6:
+			u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER);
+			break;
+		case 7:
+			u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER);
+			break;
+		case 8:
+			u32offset = (PRU_CAN_RX_MAILBOX8_STATUS_REGISTER);
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	else {
+		return -1;
+	}
+
+	s16subrtnretval = pruss_readl(dev, u32offset,
+		(u32 *) &pstremuapphndl->u32mailboxstatus, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
+s16 pru_can_tx_mode_set(struct device *dev, bool btransfer_flag,
+				can_transfer_direction ecan_trx)
+{
+	u32 u32offset;
+	u32 u32value;
+
+	if (ecan_trx == ecantransmit) {
+		u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER);
+		pruss_readl(dev, u32offset, &u32value, 1);
+		if (btransfer_flag == true) {
+			u32value &= 0x1F;
+			u32value |= 0x80;
+		} else {
+			u32value &= 0x7F;
+		}
+		pruss_writel(dev, u32offset, &u32value, 1);
+		u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER);
+		pruss_writel(dev, u32offset, &u32value, 1);
+	} else if (ecan_trx == ecanreceive) {
+		u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER);
+		pruss_readl(dev, u32offset, &u32value, 1);
+		if (btransfer_flag == true) {
+			u32value &= 0x1F;
+			u32value |= 0x40;
+		} else {
+			u32value &= 0xBF;
+		}
+		pruss_writel(dev, u32offset, &u32value, 1);
+		u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER);
+		pruss_writel(dev, u32offset, &u32value, 1);
+	} else
+		return -1;
+
+	return 0;
+}
+
+/*
+ * pru_can_config_mode_set()		Sets the timing value
+ * for data transfer. This API will be called by the Application
+ * to set timing valus for data transfer
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_config_mode_set(struct device *dev, bool bconfigmodeflag)
+{
+
+	u32 u32bitrateprescaler;
+	u32 u32canbittiming;
+
+	pruss_readl(dev, (PRU_CAN_TX_CLOCK_BRP_REGISTER),
+			(u32 *) &u32bitrateprescaler, 1);
+	pruss_readl(dev, (PRU_CAN_TX_TIMING_REGISTER),
+			(u32 *) &u32canbittiming, 1);
+
+	if (bconfigmodeflag == 1) {
+		pru_can_calc_timing(dev, u32canbittiming, u32bitrateprescaler);
+	}
+
+	else {
+		pru_can_calc_timing(dev, 0, 0);
+	}
+
+	return 0;
+}
+
+/*
+ * pru_can_emu_init()		Initializes the Can
+ * Emulation Parameters. This API will be called by the Application
+ * to Initialize the Can Emulation Parameters
+ *
+ * param    u32pruclock         PRU Clock value
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_init(struct device *dev, u32 u32pruclock)
+{
+	u32 u32offset;
+	u32 u32value;
+	s16 s16subrtnretval = -1;
+	u8 u8loop;
+
+	for (u8loop = 0; u8loop < (u8) ecanmaxinst; u8loop++) {
+		gstr_can_inst[u8loop].bcaninststate = (bool) 0;
+		gstr_can_inst[u8loop].ecantransferdirection =
+		    (can_transfer_direction) 0;
+		gstr_can_inst[u8loop].u32apphandlerptr = 0;
+	}
+
+	u32offset = (PRU_CAN_TX_GLOBAL_CONTROL_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000040;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000040;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_INTERRUPT_MASK_REGISTER & 0xFFFF);
+	u32value = 0x00004000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval =
+	    pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000001;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_ERROR_COUNTER_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_TIMING_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_ERROR_COUNTER_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_POLARITY0 & 0xFFFF);
+	u32value = 0xFFFFFFFF;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRUSS_INTC_POLARITY1 & 0xFFFF);
+	u32value = 0xFFFFFFFF;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRUSS_INTC_TYPE0 & 0xFFFF);
+	u32value = 0x1C000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRUSS_INTC_TYPE1 & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HSTINTENIDXCLR & 0xFFFF);
+	u32value = 0x0;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_GLBLEN & 0xFFFF);
+	u32value = 0x1;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	/* tx intr map arm->pru */
+	u32offset = (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF);
+	u32value = 0x0;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HOSTMAP0 & 0xFFFF);
+	u32value = 0x03020100;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HOSTMAP1 & 0xFFFF);
+	u32value = 0x07060504;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HOSTMAP2 & 0xFFFF);
+	u32value = 0x0000908;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_CHANMAP0 & 0xFFFF);
+	u32value = 0;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_CHANMAP8 & 0xFFFF);
+	u32value = 0x00020200;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 19;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 19;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 18;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 18;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 34;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 34;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRUSS_INTC_HOSTINTEN & 0xFFFF);
+	u32value = 0x5;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+/* PRU0 - Rx Internal Registers Initializations */
+
+	u32offset = (PRU_CAN_RX_GLOBAL_CONTROL_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000040;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_INTERRUPT_MASK_REGISTER & 0xFFFF);
+	u32value = 0x00004000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x0000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_ERROR_COUNTER_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_TIMING_REGISTER & 0xFFFF);
+	u32value = 0x0000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	return 0;
+}
+
+
+/*
+ * pru_can_emu_open()		Opens the can emu for
+ * application to use. This API will be called by the Application
+ * to Open the can emu for application to use.
+ *
+ * param	pstremuapphndl	Pointer to application handler
+ * structure
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_open(struct device *dev, can_emu_app_hndl *pstremuapphndl)
+{
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+
+	if (gstr_can_inst[pstremuapphndl->ecaninstance].bcaninststate == 1) {
+		return -1;
+	}
+
+	gstr_can_inst[(u8) pstremuapphndl->ecaninstance].
+					bcaninststate = (bool)1;
+	gstr_can_inst[(u8) pstremuapphndl->
+		ecaninstance].ecantransferdirection =
+		(can_transfer_direction)(u8)pstremuapphndl->ecantransferdirection;
+	gstr_can_inst[(u8) pstremuapphndl->ecaninstance].
+		u32apphandlerptr = (u32) pstremuapphndl;
+
+	return 0;
+}
+
+
+/*
+ * brief    pru_can_emu_close()	Closes the can emu for other
+ * applications to use. This API will be called by the Application to Close
+ * the can emu for other applications to use
+ *
+ * param	pstremuapphndl	Pointer to application handler structure
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_close(struct device *dev, can_emu_app_hndl *pstremuapphndl)
+{
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}
+	if (gstr_can_inst[pstremuapphndl->ecaninstance].bcaninststate == 0) {
+		return -1;
+	}
+	if ((u32) pstremuapphndl != gstr_can_inst[(u8) pstremuapphndl->
+			ecaninstance].u32apphandlerptr){
+		return -1;
+	}
+	gstr_can_inst[(u8) pstremuapphndl->ecaninstance].bcaninststate
+		= (bool) 0;
+	gstr_can_inst[(u8) pstremuapphndl->
+	ecaninstance].ecantransferdirection = (can_transfer_direction) 0;
+	gstr_can_inst[(u8) pstremuapphndl->ecaninstance].u32apphandlerptr = 0;
+
+	return 0;
+}
+
+/*
+ * brief    pru_can_emu_exit()	Diables all the PRUs
+ * This API will be called by the Application to disable all PRUs
+ * param	None
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_exit(struct device *dev)
+{
+	s16 s16subrtnretval;
+
+	s16subrtnretval = pruss_disable(dev, CAN_RX_PRU_0);
+	if (s16subrtnretval == -1)
+		return -1;
+	s16subrtnretval = pruss_disable(dev, CAN_TX_PRU_1);
+	if (s16subrtnretval == -1)
+		return -1;
+
+	return 0;
+}
+
+s16 pru_can_emu_sreset(struct device *dev)
+{
+	return 0;
+}
+
+s16 pru_can_tx(struct device *dev, u8 u8mailboxnumber, u8 u8prunumber)
+{
+	u32 u32offset = 0;
+	u32 u32value = 0;
+	s16 s16subrtnretval = -1;
+
+	if (DA8XX_PRUCORE_1 == u8prunumber) {
+		switch (u8mailboxnumber) {
+		case 0:
+			u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 1:
+			u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 2:
+			u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 3:
+			u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 4:
+			u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 5:
+			u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 6:
+			u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 7:
+			u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		default:
+			return -1;
+		}
+	} else {
+
+		u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER & 0xFFFF);
+		u32value = 0x00000000;
+		s16subrtnretval = pruss_readl(dev, u32offset,
+						(u32 *) &u32value, 1);
+		if (s16subrtnretval == -1) {
+			return -1;
+		}
+		u32value = u32value & ~(1 << u8mailboxnumber);
+		s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+		if (s16subrtnretval == -1) {
+			return -1;
+		}
+
+		switch (u8mailboxnumber) {
+		case 0:
+			u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 1:
+			u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 2:
+			u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 3:
+			u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 4:
+			u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 5:
+			u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 6:
+			u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 7:
+			u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000000;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		default:
+			return -1;
+		}
+	}
+	return 0;
+}
+
+s16 pru_can_start_abort_tx(struct device *dev, bool bcantransmitabortflag)
+{
+	u32 u32offset;
+	u32 u32value;
+	s16 s16subrtnretval;
+	u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+
+	u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+
+	u32offset = (PRUSS_INTC_STATIDXSET & 0xFFFF);
+	u32value = 32;
+	s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	return 0;
+}
+
+s16 pru_can_mask_ints(struct device *dev, u32 int_mask)
+{
+	return 0;
+}
+
+int pru_can_get_error_cnt(struct device *dev, u8 u8prunumber)
+{
+	return 0;
+}
+
+int pru_can_get_intc_status(struct device *dev)
+{
+	u32 u32offset = 0;
+	u32 u32getvalue = 0;
+	u32 u32clrvalue = 0;
+
+	u32offset = (PRUSS_INTC_STATCLRINT1 & 0xFFFF);
+	pruss_readl(dev, u32offset, (u32 *) &u32getvalue, 1);
+
+	if (u32getvalue & 4)
+		u32clrvalue = 34;	/* CLR Event 34 */
+
+	if (u32getvalue & 2)
+		u32clrvalue = 33;	/* CLR Event 33  */
+
+	if (u32clrvalue) {
+		u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+		pruss_writel(dev, u32offset, &u32clrvalue, 1);
+	} else
+		return -1;
+
+	return u32getvalue;
+}
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +15 -0
new file mode 100644
index 0000000..7550456
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Ganeshan N
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _PRU_CAN_API_H_

drivers/net/can/da8xx_pruss/pruss_can_api.h line 25

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+#define _PRU_CAN_API_H_
+
+#include <linux/types.h>
+#include <linux/mfd/pruss/da8xx_pru.h>
+
+
+#define CAN_BIT_TIMINGS			(0x273)
+
+/* Timer Clock is sourced from DDR freq (PLL1 SYS CLK 2) */
+#define	TIMER_CLK_FREQ			132000000

any change to get this from a clk_dev?

drivers/net/can/da8xx_pruss/pruss_can_api.h line 34

Quoted 9 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +9 -0
+
+#define TIMER_SETUP_DELAY		14
+#define GPIO_SETUP_DELAY		150
+
+#define CAN_RX_PRU_0			PRUSS_NUM0
+#define CAN_TX_PRU_1			PRUSS_NUM1
+
+/* Number of Instruction in the Delay loop */
+#define DELAY_LOOP_LENGTH		2

please create a struct describing your register layout.

Quoted 5 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +5 -0
+
+#define PRU1_BASE_ADDR			0x2000
+
+#define PRU_CAN_TX_GLOBAL_CONTROL_REGISTER		(PRU1_BASE_ADDR)
+#define PRU_CAN_TX_GLOBAL_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x04)

drivers/net/can/da8xx_pruss/pruss_can_api.h line 49

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+#define PRU_CAN_TX_INTERRUPT_MASK_REGISTER		(PRU1_BASE_ADDR	+ 0x08)
+#define PRU_CAN_TX_INTERRUPT_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x0C)
+#define PRU_CAN_TX_MAILBOX0_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x10)
+#define PRU_CAN_TX_MAILBOX1_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x14)
+#define PRU_CAN_TX_MAILBOX2_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x18)
+#define PRU_CAN_TX_MAILBOX3_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x1C)
+#define PRU_CAN_TX_MAILBOX4_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x20)
+#define PRU_CAN_TX_MAILBOX5_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x24)
+#define PRU_CAN_TX_MAILBOX6_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x28)
+#define PRU_CAN_TX_MAILBOX7_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x2C)

please use an array for the mailbox status register.

drivers/net/can/da8xx_pruss/pruss_can_api.h line 61

Quoted 12 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +12 -0
+#define PRU_CAN_TX_ERROR_COUNTER_REGISTER		(PRU1_BASE_ADDR	+ 0x30)
+#define PRU_CAN_TX_TIMING_REGISTER			(PRU1_BASE_ADDR	+ 0x34)
+#define PRU_CAN_TX_CLOCK_BRP_REGISTER			(PRU1_BASE_ADDR	+ 0x38)
+
+#define PRU_CAN_TX_MAILBOX0				(PRU1_BASE_ADDR	+ 0x40)
+#define PRU_CAN_TX_MAILBOX1				(PRU1_BASE_ADDR	+ 0x50)
+#define PRU_CAN_TX_MAILBOX2				(PRU1_BASE_ADDR	+ 0x60)
+#define PRU_CAN_TX_MAILBOX3				(PRU1_BASE_ADDR	+ 0x70)
+#define PRU_CAN_TX_MAILBOX4				(PRU1_BASE_ADDR	+ 0x80)
+#define PRU_CAN_TX_MAILBOX5				(PRU1_BASE_ADDR	+ 0x90)
+#define PRU_CAN_TX_MAILBOX6				(PRU1_BASE_ADDR	+ 0xA0)
+#define PRU_CAN_TX_MAILBOX7				(PRU1_BASE_ADDR	+ 0xB0)

also use an array here

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+
+#define PRU_CAN_TIMING_VAL_TX				(PRU1_BASE_ADDR	+ 0xC0)
+#define PRU_CAN_TIMING_VAL_TX_SJW			(PRU1_BASE_ADDR	+ 0xC4)
+#define PRU_CAN_TRANSMIT_FRAME				(PRU1_BASE_ADDR	+ 0xE0)
+
+#define PRU0_BASE_ADDR					0
+
+#define PRU_CAN_RX_GLOBAL_CONTROL_REGISTER		(PRU0_BASE_ADDR)
+#define PRU_CAN_RX_GLOBAL_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x04)
+#define PRU_CAN_RX_INTERRUPT_MASK_REGISTER		(PRU0_BASE_ADDR	+ 0x08)

drivers/net/can/da8xx_pruss/pruss_can_api.h line 81

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+#define PRU_CAN_RX_INTERRUPT_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x0C)
+#define PRU_CAN_RX_MAILBOX0_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x10)
+#define PRU_CAN_RX_MAILBOX1_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x14)
+#define PRU_CAN_RX_MAILBOX2_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x18)
+#define PRU_CAN_RX_MAILBOX3_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x1C)
+#define PRU_CAN_RX_MAILBOX4_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x20)
+#define PRU_CAN_RX_MAILBOX5_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x24)
+#define PRU_CAN_RX_MAILBOX6_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x28)
+#define PRU_CAN_RX_MAILBOX7_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x2C)
+#define PRU_CAN_RX_MAILBOX8_STATUS_REGISTER		(PRU0_BASE_ADDR	+ 0x30)

...array

drivers/net/can/da8xx_pruss/pruss_can_api.h line 94

Quoted 13 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +13 -0
+#define PRU_CAN_RX_ERROR_COUNTER_REGISTER		(PRU0_BASE_ADDR	+ 0x34)
+#define PRU_CAN_RX_TIMING_REGISTER			(PRU0_BASE_ADDR	+ 0x38)
+#define PRU_CAN_RX_CLOCK_BRP_REGISTER			(PRU0_BASE_ADDR	+ 0x3C)
+
+#define PRU_CAN_RX_MAILBOX0				(PRU0_BASE_ADDR	+ 0x40)
+#define PRU_CAN_RX_MAILBOX1				(PRU0_BASE_ADDR	+ 0x50)
+#define PRU_CAN_RX_MAILBOX2				(PRU0_BASE_ADDR	+ 0x60)
+#define PRU_CAN_RX_MAILBOX3				(PRU0_BASE_ADDR	+ 0x70)
+#define PRU_CAN_RX_MAILBOX4				(PRU0_BASE_ADDR	+ 0x80)
+#define PRU_CAN_RX_MAILBOX5				(PRU0_BASE_ADDR	+ 0x90)
+#define PRU_CAN_RX_MAILBOX6				(PRU0_BASE_ADDR	+ 0xA0)
+#define PRU_CAN_RX_MAILBOX7				(PRU0_BASE_ADDR	+ 0xB0)
+#define PRU_CAN_RX_MAILBOX8				(PRU0_BASE_ADDR	+ 0xC0)

..array

The rx and tx register set look quite similar. Is it intended that you have 8 tx but 9 rx mailboxes? Anyway....make a struct descriing the register set and use it twice, one for rx and one for tx.

Quoted 22 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +22 -0
+
+#define PRU_CAN_TIMING_VAL_RX				(PRU0_BASE_ADDR	+ 0xD0)
+#define PRU_CAN_RECEIVE_FRAME				(PRU0_BASE_ADDR	+ 0xD4)
+#define PRU_CAN_ID_MAP					(PRU0_BASE_ADDR	+ 0xF0)
+
+#define PRU_CAN_ERROR_ACTIVE				128
+
+#define CAN_ACK_FAILED					0xE
+#define CAN_ARBTR_FAIL					0xD
+#define CAN_BIT_ERROR					0xC
+#define CAN_TRANSMISSION_SUCCESS			0xA
+
+#define STD_DATA_FRAME					0x1
+#define EXTD_DATA_FRAME					0x2
+#define STD_REMOTE_FRAME				0x3
+#define EXTD_REMOTE_FRAME				0x4
+
+#define PRU_CAN_MAX_SJW					8
+#define PRU_CAN_MAX_PHSEG1				25
+#define PRU_CAN_MAX_PHSEG2				25
+
+#define DA8XX_PRUCANCORE_0_REGS				0x7000

drivers/net/can/da8xx_pruss/pruss_can_api.h line 126

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+#define DA8XX_PRUCANCORE_1_REGS				0x7800
+#define PRU0_PROG_RAM_START_OFFSET			0x8000
+#define PRU1_PROG_RAM_START_OFFSET			0xC000
+#define PRU_CAN_INIT_MAX_TIMEOUT			0xFF
+
+typedef enum {
+	ecaninst0 = 0,
+	ecaninst1,
+	ecanmaxinst
+} can_instance_enum;

seens unused

drivers/net/can/da8xx_pruss/pruss_can_api.h line 137

Quoted 11 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +11 -0
+
+typedef enum {
+	ecanmailbox0 = 0,
+	ecanmailbox1,
+	ecanmailbox2,
+	ecanmailbox3,
+	ecanmailbox4,
+	ecanmailbox5,
+	ecanmailbox6,
+	ecanmailbox7
+} can_mailbox_number;

unused, too

drivers/net/can/da8xx_pruss/pruss_can_api.h line 143

Quoted 6 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +6 -0
+
+typedef enum {
+	ecandirectioninit = 0,
+	ecantransmit,
+	ecanreceive
+} can_transfer_direction;

please add a common prefix and please write them uppsercase.

drivers/net/can/da8xx_pruss/pruss_can_api.h line 155

Quoted 12 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +12 -0
+
+typedef struct {
+	u16 u16extendedidentifier;
+	u16 u16baseidentifier;
+	u8 u8data7;
+	u8 u8data6;
+	u8 u8data5;
+	u8 u8data4;
+	u8 u8data3;
+	u8 u8data2;
+	u8 u8data1;
+	u8 u8data0;

use an array for the data.

Quoted 8 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +8 -0
+	u16 u16datalength;
+	u16 u16crc;
+} can_mail_box_structure;
+
+typedef struct {
+	can_transfer_direction ecantransferdirection;
+} can_mailbox_config;
+

drivers/net/can/da8xx_pruss/pruss_can_api.h line 173

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+typedef struct {
+	can_instance_enum ecaninstance;
+	can_transfer_direction ecantransferdirection;
+	can_mail_box_structure strcanmailbox;
+	can_mailbox_number ecanmailboxnumber;
+	u8 u8prunumber;
+	u32 u32globalstatus;
+	u32 u32interruptstatus;
+	u32 u32mailboxstatus;
+} can_emu_app_hndl;

You already have defines your priv. No need for further structs.

drivers/net/can/da8xx_pruss/pruss_can_api.h line 179

Quoted 6 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +6 -0
+
+typedef struct {
+	bool bcaninststate;
+	can_transfer_direction ecantransferdirection;
+	u32 u32apphandlerptr;
+} can_emu_drv_inst;

dito

drivers/net/can/da8xx_pruss/pruss_can_api.h line 189

Quoted 11 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+
+typedef struct {
+	u8 u8syncjumpwidth;
+	u8 u8phseg1;
+	u8 u8phseg2;
+} can_bit_timing_consts;
+
+/* Field Definition Macros  */
+
+/* CONTROL */

get rid of all the following functions, you don't need that extra layer in the can driver.

Quoted 90 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +90 -0
+/*
+ * pru_can_set_brp() Updates the  BRP register of PRU.
+ */
+s16 pru_can_set_brp(struct device *dev, u16 u16prescaler);
+
+/*
+ * pru_can_set_bit_timing() Updates the  timing register of PRU
+ */
+s16 pru_can_set_bit_timing(struct device *dev,
+			can_bit_timing_consts *pstrbittiming);
+
+/*
+ * pru_can_calc_timing() Updates the timing values of PRU
+ */
+s16 pru_can_calc_timing(struct device *dev,
+			u32 u32bittiming, u32 u32bitrateprescaler);
+
+/*
+ * pru_can_write_data_to_mailbox() Updates the transmit mailboxes of PRU1
+ */
+s16 pru_can_write_data_to_mailbox(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl);
+
+/*
+ * pru_can_get_data_from_mailbox() Receive data from receive mailboxes
+ */
+s16 pru_can_get_data_from_mailbox(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl);
+
+/*
+ * pru_can_rx_id_map() Receive mailboxes ID Mapping of PRU0
+ */
+s16 pru_can_rx_id_map(struct device *dev,
+			u32 u32nodeid, can_mailbox_number ecanmailboxno);
+
+/*
+ *pru_can_get_intr_status() Get interrupts status register value
+ */
+s16 pru_can_get_intr_status(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl);
+
+
+/*
+ * pru_can_get_global_status() Get the globalstatus register value
+ */
+s16 pru_can_get_global_status(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl);
+
+/*
+ * pru_can_get_mailbox_status() Get mailbox status reg value
+ */
+s16 pru_can_get_mailbox_status(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl);
+
+/*
+ * pru_can_configuration_mode_set() Sets timing val for data transfer
+ */
+s16 pru_can_config_mode_set(struct device *dev,
+			bool bconfig_modeflag);
+
+/*
+ * pru_can_emu_init() Initializes Can Emulation Parameters
+ */
+s16 pru_can_emu_init(struct device *dev,
+			u32 u32pruclock);
+
+/*
+ * pru_can_emu_open() Opens can emu for application to use
+ */
+s16 pru_can_emu_open(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl);
+
+/*
+ * pru_can_emu_close() Closes can emu for applications to use
+ */
+s16 pru_can_emu_close(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl);
+
+/*
+ * pru_can_emu_exit() Diables all the PRUs
+ */
+s16 pru_can_emu_exit(struct device *dev);
+
+s16 pru_can_tx_mode_set(struct device *dev, bool btransfer_flag,
+			 can_transfer_direction ecan_trx);
+
+s16 pru_can_emu_sreset(struct device *dev);
+
+s16 pru_can_tx(struct device *dev,
+			u8 u8mailboxnumber, u8 u8prunumber);

drivers/net/can/da8xx_pruss/pruss_can_api.h line 290

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.h new file +10 -0
+
+s16 pru_can_start_abort_tx(struct device *dev,
+			bool btxabort_flag);
+
+s16 pru_can_mask_ints(struct device *dev, u32 int_mask);
+
+s32 pru_can_get_error_cnt(struct device *dev, u8 u8prunumber);
+
+s32 pru_can_get_intc_status(struct device *dev);
+#endif

regards, Marc

Signature · 4 lines
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

_______________________________________________
Socketcan-core mailing list [email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

#51 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

In reply to Subhasish Ghosh
Quoted 1 line · Subhasish Ghosh

This patch adds support for the CAN device emulated on PRUSS.

Hi Subhasish,

This is a detailed walk through the can driver. The pruss_can.c file mostly looks good, there are very tiny changes that I'm suggesting to improve the code. I assume that you wrote that file.

The pruss_can_api.c is a bit of a mess and looks like it was copied from some other code base and just barely changed to follow Linux coding style. I can tell from the main driver file that you can do better than that.

My recommendation for that would be to throw it away and reimplement the few parts that you actually need, in proper coding style. You can also try to fix the file according to the comments I give you below, but I assume that would be signficantly more work.

Moving everything into one file also makes things easier to read here and lets you identifer more quickly what is unused.

Arnd

drivers/net/can/da8xx_pruss/pruss_can.c line 83

Quoted 6 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +6 -0
+#ifdef __CAN_DEBUG
+#define __can_debug(fmt, args...) printk(KERN_DEBUG "can_debug: " fmt, ## args)
+#else
+#define __can_debug(fmt, args...)
+#endif
+#define __can_err(fmt, args...) printk(KERN_ERR "can_err: " fmt, ## args)

Better use the existing dev_dbg() and dev_err() macros that provide the same functionality in a more standard way. You already use them in some places, as I noticed.

If you don't have a way to pass a meaningful device, you can use pr_debug/pr_err.

Review

Quoted 2 lines
(quoted diff) +2 -0
+void omapl_pru_can_rx_wQ(struct work_struct *work)
+{

This is only used in the same file, so better make it static.

drivers/net/can/da8xx_pruss/pruss_can.c line 393

Quoted 2 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +2 -0
+	if (-1 == pru_can_get_intr_status(priv->dev, &priv->can_rx_hndl))
+		return;

Don't make up your own return values, just use the standard error codes, e.g. -EIO or -EAGAIN, whatever fits.

The more common way to write the comparison would be the other way round,

	if (pru_can_get_intr_status(priv->dev, &priv->can_rx_hndl) == -EAGAIN)
		return;

or, simpler

	if (pru_can_get_intr_status(priv->dev, &priv->can_rx_hndl))
		return;

Review

Quoted 2 lines
(quoted diff) +2 -0
+irqreturn_t omapl_tx_can_intr(int irq, void *dev_id)
+{

This also should be static

drivers/net/can/da8xx_pruss/pruss_can.c line 439

Quoted 3 lines
drivers/net/can/da8xx_pruss/pruss_can.c new file +3 -0
+		for (bit_set = 0; ((priv->can_tx_hndl.u32interruptstatus & 0xFF)
+						>> bit_set != 0); bit_set++)
+		;
	bit_set = fls(priv->can_tx_hndl.u32interruptstatus & 0xFF); ?
Quoted 1 line

+irqreturn_t omapl_rx_can_intr(int irq, void *dev_id)

static

drivers/net/can/da8xx_pruss/pruss_can_api.c

Quoted 5 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +0 -0
new file mode 100644
index 0000000..2f7438a

A lot of code in this file seems to be unused. Is that right? I would suggest adding only the code that is actually being used. If you add more functionality later, you can always add back the low-level functions, but dead code usually turns into broken code quickly.

Quoted 1 line

+static can_emu_drv_inst gstr_can_inst[ecanmaxinst];

This is global data and probably needs some for of locking

drivers/net/can/da8xx_pruss/pruss_can_api.c line 30

Quoted 12 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +12 -0
+/*
+ * pru_can_set_brp()	Updates the  BRP register of PRU0
+ * and PRU1 of OMAP L138. This API will be called by the
+ * Application to updtae the BRP register of PRU0 and PRU1
+ *
+ * param	u16bitrateprescaler		The can bus bitrate
+ * prescaler value be set
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_set_brp(struct device *dev, u16 u16bitrateprescaler)
+{

unused.

drivers/net/can/da8xx_pruss/pruss_can_api.c line 36

Quoted 5 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +5 -0
+	u32 u32offset;
+
+	if (u16bitrateprescaler > 255) {
+		return -1;
+	}

non-standard error code. It also doesn't match the comment, which claims it is SUCCESS or FAILURE, both of which are (rightfully) not defined.

drivers/net/can/da8xx_pruss/pruss_can_api.c line 42

Quoted 5 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +5 -0
+	u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1);
+
+	u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER);
+	pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1);

You pass a 32 bit pointer to a 16 bit local variable here. This has an undefined effect, and if you build this code on a big-endian platform, it cannot possibly do anything good.

pruss_writel() is defined in a funny way if it takes a thirty-two bit input argument by reference, rather than by value. What is going on there?

drivers/net/can/da8xx_pruss/pruss_can_api.c line 59

Quoted 2 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +2 -0
+s16 pru_can_set_bit_timing(struct device *dev,
+		can_bit_timing_consts *pstrbittiming)

unused.

drivers/net/can/da8xx_pruss/pruss_can_api.c

Quoted 2 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +2 -0
+	u32 u32offset;
+	u32 u32serregister;

It's a bit silly to put the name of the type into the name of a variable. You already spell it out in the definition.

drivers/net/can/da8xx_pruss/pruss_can_api.c line 150

Quoted 9 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +9 -0
+s16 pru_can_write_data_to_mailbox(struct device *dev,
+			can_emu_app_hndl *pstremuapphndl)
+{
+	s16 s16subrtnretval;
+	u32 u32offset;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}

nonstandard error code. Also, why the heck is type function return type s16 when the only possible return values are 0 and -1? Just make this an int.

Quoted 18 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +18 -0
+	switch ((u8) pstremuapphndl->ecanmailboxnumber) {
+	case 0:
+		u32offset = (PRU_CAN_TX_MAILBOX0);
+		break;
+	case 1:
+		u32offset = (PRU_CAN_TX_MAILBOX1);
+		break;
+	case 2:
+		u32offset = (PRU_CAN_TX_MAILBOX2);
+		break;
+	case 3:
+		u32offset = (PRU_CAN_TX_MAILBOX3);
+		break;
+	case 4:
+		u32offset = (PRU_CAN_TX_MAILBOX4);
+		break;
+	case 5:
+		u32offset = (PRU_CAN_TX_MAILBOX5);

drivers/net/can/da8xx_pruss/pruss_can_api.c line 179

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+		break;
+	case 6:
+		u32offset = (PRU_CAN_TX_MAILBOX6);
+		break;
+	case 7:
+		u32offset = (PRU_CAN_TX_MAILBOX7);
+		break;
+	default:
+		return -1;
+	}

Lovely switch statement. I'm sure you find a better way to express this ;-)

drivers/net/can/da8xx_pruss/pruss_can_api.c line 187

Quoted 7 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +7 -0
+	s16subrtnretval = pruss_writel(dev, u32offset,
+		(u32 *) &(pstremuapphndl->strcanmailbox), 4);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	return 0;
+}

return pruss_writel(...) ?

Quoted 11 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +11 -0
+
+/*
+ * pru_can_get_intr_status()
+ * Gets the interrupts status register value.
+ * This API will be called by the Application
+ * to get the interrupts status register value
+ *
+ * param  u8prunumber	PRU number for which IntStatusReg
+ * has to be read
+ *
+ * return   SUCCESS or FAILURE

drivers/net/can/da8xx_pruss/pruss_can_api.c line 294

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+ */
+s16 pru_can_get_intr_status(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)
+{
+	u32 u32offset;
+	s16 s16subrtnretval = -1;
+
+	if (pstremuapphndl == NULL) {
+		return -1;
+	}

In every function you check that pstremuapphndl is present. This seems rather pointless. How about just making sure you never pass a NULL value to these functions? That should not be hard at all from the high-level driver.

drivers/net/can/da8xx_pruss/pruss_can_api.c line 308

Quoted 13 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +13 -0
+	if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) {
+		u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER);
+	} else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) {
+		u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER);
+	} else {
+		return -1;
+	}
+
+	s16subrtnretval = pruss_readl(dev, u32offset,
+		(u32 *) &pstremuapphndl->u32interruptstatus, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}

You can also get rid of all these {} braces around one-line statements.

drivers/net/can/da8xx_pruss/pruss_can_api.c line 355

Quoted 9 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +9 -0
+/*
+ * pru_can_get_mailbox_status()		Gets the mailbox status
+ * register value. This API will be called by the Application
+ * to get the mailbox status register value
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_get_mailbox_status(struct device *dev,
+		can_emu_app_hndl *pstremuapphndl)

unused.

drivers/net/can/da8xx_pruss/pruss_can_api.c line 485

Quoted 8 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +8 -0
+/*
+ * pru_can_config_mode_set()		Sets the timing value
+ * for data transfer. This API will be called by the Application
+ * to set timing valus for data transfer
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_config_mode_set(struct device *dev, bool bconfigmodeflag)

unused.

Quoted 9 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +9 -0
+	u32offset = (PRU_CAN_TX_GLOBAL_CONTROL_REGISTER & 0xFFFF);
+	u32value = 0x00000000;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+
+	u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000040;

drivers/net/can/da8xx_pruss/pruss_can_api.c line 548

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}
+	u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+	u32value = 0x00000040;
+	s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+	if (s16subrtnretval == -1) {
+		return -1;
+	}

<skipping 50 (!) more of these>

After the third time of writing the same code, you should have noticed that there is some duplication involved that can trivially be reduced. A good way to express the same would be a table with the contents:

static struct pru_can_register_init {
	u16 offset;
	u32 value;
} = {
	{ PRU_CAN_TX_GLOBAL_CONTROL_REGISTER, 0, },
	{ PRU_CAN_TX_GLOBAL_STATUS_REGISTER, 0x40, },
	{ PRU_CAN_RX_GLOBAL_STATUS_REGISTER, 0x40, },
	...
};

drivers/net/can/da8xx_pruss/pruss_can_api.c line 912

Quoted 13 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +13 -0
+
+
+/*
+ * pru_can_emu_open()		Opens the can emu for
+ * application to use. This API will be called by the Application
+ * to Open the can emu for application to use.
+ *
+ * param	pstremuapphndl	Pointer to application handler
+ * structure
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_open(struct device *dev, can_emu_app_hndl *pstremuapphndl)

unused.

drivers/net/can/da8xx_pruss/pruss_can_api.c line 944

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+/*
+ * brief    pru_can_emu_close()	Closes the can emu for other
+ * applications to use. This API will be called by the Application to Close
+ * the can emu for other applications to use
+ *
+ * param	pstremuapphndl	Pointer to application handler structure
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_close(struct device *dev, can_emu_app_hndl *pstremuapphndl)

unused

drivers/net/can/da8xx_pruss/pruss_can_api.c line 989

Quoted 4 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +4 -0
+s16 pru_can_emu_sreset(struct device *dev)
+{
+	return 0;
+}

pointless.

Quoted 18 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +18 -0
+s16 pru_can_tx(struct device *dev, u8 u8mailboxnumber, u8 u8prunumber)
+{
+	u32 u32offset = 0;
+	u32 u32value = 0;
+	s16 s16subrtnretval = -1;
+
+	if (DA8XX_PRUCORE_1 == u8prunumber) {
+		switch (u8mailboxnumber) {
+		case 0:
+			u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+					(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 1:

drivers/net/can/da8xx_pruss/pruss_can_api.c line 1018

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+			u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+			u32value = 0x00000080;
+			s16subrtnretval = pruss_writel(dev, u32offset,
+						(u32 *) &u32value, 1);
+			if (s16subrtnretval == -1) {
+				return -1;
+			}
+			break;
+		case 2:
+			u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF);

Another pointless switch statement.

drivers/net/can/da8xx_pruss/pruss_can_api.c line 1203

Quoted 9 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +9 -0
+s16 pru_can_mask_ints(struct device *dev, u32 int_mask)
+{
+	return 0;
+}
+
+int pru_can_get_error_cnt(struct device *dev, u8 u8prunumber)
+{
+	return 0;
+}

useless.

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+int pru_can_get_intc_status(struct device *dev)
+{
+	u32 u32offset = 0;
+	u32 u32getvalue = 0;
+	u32 u32clrvalue = 0;
+
+	u32offset = (PRUSS_INTC_STATCLRINT1 & 0xFFFF);
+	pruss_readl(dev, u32offset, (u32 *) &u32getvalue, 1);
+
+	if (u32getvalue & 4)

drivers/net/can/da8xx_pruss/pruss_can_api.c line 1224

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+		u32clrvalue = 34;	/* CLR Event 34 */
+
+	if (u32getvalue & 2)
+		u32clrvalue = 33;	/* CLR Event 33  */
+
+	if (u32clrvalue) {
+		u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
+		pruss_writel(dev, u32offset, &u32clrvalue, 1);
+	} else
+		return -1;

Could the controller signal both event 34 and 33 simultaneously? The only user of this function looks at the individual bits of the return value again, which looks wrong for all possible return values here.

Quoted 13 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +13 -0
+#ifndef _PRU_CAN_API_H_
+#define CAN_BIT_TIMINGS			(0x273)
+
+/* Timer Clock is sourced from DDR freq (PLL1 SYS CLK 2) */
+#define	TIMER_CLK_FREQ			132000000
+
+#define TIMER_SETUP_DELAY		14
+#define GPIO_SETUP_DELAY		150
+
+#define CAN_RX_PRU_0			PRUSS_NUM0
+#define CAN_TX_PRU_1			PRUSS_NUM1
+
+/* Number of Instruction in the Delay loop */

drivers/net/can/da8xx_pruss/pruss_can_api.c

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+#define DELAY_LOOP_LENGTH		2
+
+#define PRU1_BASE_ADDR			0x2000
+
+#define PRU_CAN_TX_GLOBAL_CONTROL_REGISTER		(PRU1_BASE_ADDR)
+#define PRU_CAN_TX_GLOBAL_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x04)
+#define PRU_CAN_TX_INTERRUPT_MASK_REGISTER		(PRU1_BASE_ADDR	+ 0x08)
+#define PRU_CAN_TX_INTERRUPT_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x0C)
+#define PRU_CAN_TX_MAILBOX0_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x10)
+#define PRU_CAN_TX_MAILBOX1_STATUS_REGISTER		(PRU1_BASE_ADDR	+ 0x14)

The header file should be used for interfaces between the two .c files, don't mix that with hardware specific definitions. Sometimes you may want to have register number lists in a header, if that list is going to be used in multiple places. In this case, there is just one user, so better move all those definitions over there.

Quoted 12 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +12 -0
+typedef enum {
+	ecaninst0 = 0,
+	ecaninst1,
+	ecanmaxinst
+} can_instance_enum;
+
+typedef enum {
+	ecanmailbox0 = 0,
+	ecanmailbox1,
+	ecanmailbox2,
+	ecanmailbox3,
+	ecanmailbox4,

drivers/net/can/da8xx_pruss/pruss_can_api.c

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+	ecanmailbox5,
+	ecanmailbox6,
+	ecanmailbox7
+} can_mailbox_number;
+
+typedef enum {
+	ecandirectioninit = 0,
+	ecantransmit,
+	ecanreceive
+} can_transfer_direction;

The values are all unused, you only use the typedefs. IMHO it would be more sensible to just pass these as unsigned int or u32 values, but if you prefer, there is no reason to just do

typedef u32 can_mailbox_number;

etc.

Quoted 4 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +4 -0
+typedef struct {
+	u16 u16extendedidentifier;
+	u16 u16baseidentifier;
+	u8 u8data7;

drivers/net/can/da8xx_pruss/pruss_can_api.c

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+	u8 u8data6;
+	u8 u8data5;
+	u8 u8data4;
+	u8 u8data3;
+	u8 u8data2;
+	u8 u8data1;
+	u8 u8data0;
+	u16 u16datalength;
+	u16 u16crc;
+} can_mail_box_structure;

Please don't use typedef for complex data structures, and learn about better naming of identifiers. I would suggest writing this as

struct pru_can_mailbox {
	u16	ext_id;
	u16	base_id;
	u8	data[8]; /* note: reverse order */
	u16	len;
	u16	crc;
};

Sames rules apply to the other structures.

Arnd

#52 lore raw

Re: [PATCH v2 09/13] can: pruss CAN driver.

Hello,

Quoted 13 lines

This is a detailed walk through the can driver. The pruss_can.c file mostly looks good, there are very tiny changes that I'm suggesting to improve the code. I assume that you wrote that file.

The pruss_can_api.c is a bit of a mess and looks like it was copied from some other code base and just barely changed to follow Linux coding style. I can tell from the main driver file that you can do better than that.
My recommendation for that would be to throw it away and reimplement the few parts that you actually need, in proper coding style. You can also try to fix the file according to the comments I give you below, but I assume that would be signficantly more work.

Quoted 2 lines

Moving everything into one file also makes things easier to read here and lets you identifer more quickly what is unused.

SG - I have almost re-implemented the API layer, will be merging with the driver file itself.

Quoted 13 lines
Quoted 8 lines
(quoted diff) +2 -0
+#ifdef __CAN_DEBUG
+#define __can_debug(fmt, args...) printk(KERN_DEBUG "can_debug: " fmt, 

## args)

drivers/net/can/da8xx_pruss/pruss_can.c new file +4 -0
+#else
+#define __can_debug(fmt, args...)
+#endif
+#define __can_err(fmt, args...) printk(KERN_ERR "can_err: " fmt, ## 

args)

Better use the existing dev_dbg() and dev_err() macros that provide the same functionality in a more standard way. You already use them in some places, as I noticed.

Quoted 3 lines

If you don't have a way to pass a meaningful device, you can use pr_debug/pr_err.

SG - Ok

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+void omapl_pru_can_rx_wQ(struct work_struct *work)
+{

This is only used in the same file, so better make it static.

SG - This is removed, used NAPI as recommended.

Quoted 6 lines
Quoted 3 lines

+ if (-1 == pru_can_get_intr_status(priv->dev, &priv->can_rx_hndl))
+ return;

Don't make up your own return values, just use the standard error codes, e.g. -EIO or -EAGAIN, whatever fits.

SG - Ok

Quoted 10 lines

The more common way to write the comparison would be the other way round,

if (pru_can_get_intr_status(priv->dev, &priv->can_rx_hndl) == -EAGAIN) return;

or, simpler

if (pru_can_get_intr_status(priv->dev, &priv->can_rx_hndl)) return;

SG - Ok

Quoted 5 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+irqreturn_t omapl_tx_can_intr(int irq, void *dev_id)
+{

This also should be static

SG - Ok

Quoted 7 lines
Quoted 4 lines

+ for (bit_set = 0; ((priv->can_tx_hndl.u32interruptstatus & 0xFF)
+ >> bit_set != 0); bit_set++)
+ ;

bit_set = fls(priv->can_tx_hndl.u32interruptstatus & 0xFF); ?

SG - Ok

Quoted 3 lines
Quoted 1 line

+irqreturn_t omapl_rx_can_intr(int irq, void *dev_id)

static

SG - Ok

Quoted 13 lines

drivers/net/can/da8xx_pruss/pruss_can_api.c

Quoted 6 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +0 -0
new file mode 100644
index 0000000..2f7438a

A lot of code in this file seems to be unused. Is that right? I would suggest adding only the code that is actually being used. If you add more functionality later, you can always add back the low-level functions, but dead code usually turns into broken code quickly.

SG - Ok

Quoted 3 lines
Quoted 1 line

+static can_emu_drv_inst gstr_can_inst[ecanmaxinst];

This is global data and probably needs some for of locking

SG - Ok

Quoted 15 lines

drivers/net/can/da8xx_pruss/pruss_can_api.c line 30

Quoted 13 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +12 -0
+/*
+ * pru_can_set_brp() Updates the  BRP register of PRU0
+ * and PRU1 of OMAP L138. This API will be called by the
+ * Application to updtae the BRP register of PRU0 and PRU1
+ *
+ * param u16bitrateprescaler The can bus bitrate
+ * prescaler value be set
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_set_brp(struct device *dev, u16 u16bitrateprescaler)
+{

unused.

SG - Ok

Quoted 10 lines
Quoted 6 lines

+ u32 u32offset;
+
+ if (u16bitrateprescaler > 255) {
+ return -1;
+ }

non-standard error code. It also doesn't match the comment, which claims it is SUCCESS or FAILURE, both of which are (rightfully) not defined.

SG - Ok

Quoted 7 lines
Quoted 6 lines

+ u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER);
+ pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1); +
+ u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER);
+ pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1);

SG - Ok

Quoted 4 lines

You pass a 32 bit pointer to a 16 bit local variable here. This has an undefined effect, and if you build this code on a big-endian platform, it cannot possibly do anything good.

Quoted 9 lines

pruss_writel() is defined in a funny way if it takes a thirty-two bit input argument by reference, rather than by value. What is going on there?

Quoted 2 lines

+s16 pru_can_set_bit_timing(struct device *dev,
+ can_bit_timing_consts *pstrbittiming)

unused.

SG - Ok

Quoted 5 lines
Quoted 2 lines

+ u32 u32offset;
+ u32 u32serregister;

It's a bit silly to put the name of the type into the name of a variable. You already spell it out in the definition.

SG - Ok

Quoted 14 lines

drivers/net/can/da8xx_pruss/pruss_can_api.c line 150

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +9 -0
+s16 pru_can_write_data_to_mailbox(struct device *dev,
+ can_emu_app_hndl *pstremuapphndl)
+{
+ s16 s16subrtnretval;
+ u32 u32offset;
+
+ if (pstremuapphndl == NULL) {
+ return -1;
+ }

nonstandard error code. Also, why the heck is type function return type s16 when the only possible return values are 0 and -1? Just make this an int.

SG - Ok

Quoted 30 lines
Quoted 21 lines

+ switch ((u8) pstremuapphndl->ecanmailboxnumber) {
+ case 0:
+ u32offset = (PRU_CAN_TX_MAILBOX0);
+ break;
+ case 1:
+ u32offset = (PRU_CAN_TX_MAILBOX1);
+ break;
+ case 2:
+ u32offset = (PRU_CAN_TX_MAILBOX2);
+ break;
+ case 3:
+ u32offset = (PRU_CAN_TX_MAILBOX3);
+ break;
+ case 4:
+ u32offset = (PRU_CAN_TX_MAILBOX4);
+ break;
+ case 5:
+ u32offset = (PRU_CAN_TX_MAILBOX5);
+ break;
+ case 6:

Quoted 8 lines

+ u32offset = (PRU_CAN_TX_MAILBOX6);
+ break;
+ case 7:
+ u32offset = (PRU_CAN_TX_MAILBOX7);
+ break;
+ default:
+ return -1;
+ }

Quoted 1 line

Lovely switch statement. I'm sure you find a better way to express this ;-)

SG - Ok.

Quoted 10 lines
Quoted 8 lines

+ s16subrtnretval = pruss_writel(dev, u32offset,
+ (u32 *) &(pstremuapphndl->strcanmailbox), 4);
+ if (s16subrtnretval == -1) {
+ return -1;
+ }
+ return 0;
+}

return pruss_writel(...) ?

SG - Ok.

Quoted 19 lines
Quoted 19 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +18 -0
+
+/*
+ * pru_can_get_intr_status()
+ * Gets the interrupts status register value.
+ * This API will be called by the Application
+ * to get the interrupts status register value
+ *
+ * param  u8prunumber PRU number for which IntStatusReg
+ * has to be read
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_get_intr_status(struct device *dev,
+ can_emu_app_hndl *pstremuapphndl)
+{
+ u32 u32offset;
+ s16 s16subrtnretval = -1;
+
Quoted 8 lines
Quoted 3 lines

+ if (pstremuapphndl == NULL) {
+ return -1;
+ }

In every function you check that pstremuapphndl is present. This seems rather pointless. How about just making sure you never pass a NULL value to these functions? That should not be hard at all from the high-level driver.

SG - OK.

Quoted 15 lines
Quoted 14 lines

+ if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) {
+ u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER);
+ } else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) {
+ u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER);
+ } else {
+ return -1;
+ }
+
+ s16subrtnretval = pruss_readl(dev, u32offset,
+ (u32 *) &pstremuapphndl->u32interruptstatus, 1);
+ if (s16subrtnretval == -1) {
+ return -1;
+ }

Quoted 1 line

You can also get rid of all these {} braces around one-line statements.

SG - OK.

Quoted 12 lines

drivers/net/can/da8xx_pruss/pruss_can_api.c line 355

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +9 -0
+/*
+ * pru_can_get_mailbox_status() Gets the mailbox status
+ * register value. This API will be called by the Application
+ * to get the mailbox status register value
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_get_mailbox_status(struct device *dev,
+ can_emu_app_hndl *pstremuapphndl)

unused

.
SG - OK.

Quoted 11 lines

drivers/net/can/da8xx_pruss/pruss_can_api.c line 485

Quoted 9 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +8 -0
+/*
+ * pru_can_config_mode_set() Sets the timing value
+ * for data transfer. This API will be called by the Application
+ * to set timing valus for data transfer
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_config_mode_set(struct device *dev, bool bconfigmodeflag)

unused.

SG - OK.

Quoted 21 lines
Quoted 12 lines

+ u32offset = (PRU_CAN_TX_GLOBAL_CONTROL_REGISTER & 0xFFFF);
+ u32value = 0x00000000;
+ s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+ if (s16subrtnretval == -1) {
+ return -1;
+ }
+
+ u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+ u32value = 0x00000040;
+ s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+ if (s16subrtnretval == -1) {

Quoted 8 lines

+ return -1;
+ }
+ u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF);
+ u32value = 0x00000040;
+ s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1);
+ if (s16subrtnretval == -1) {
+ return -1;
+ }

Quoted 6 lines

<skipping 50 (!) more of these>

After the third time of writing the same code, you should have noticed that there is some duplication involved that can trivially be reduced. A good way to express the same would be a table with the contents:

SG - OK.
Thanks for taking the pain to review this. But, the good
part is that
everything works fine. All this got cluttered as
development &
design changes happened. Hopefully, the cleaned up code
will
also work as well :-)

Quoted 11 lines

static struct pru_can_register_init {
u16 offset;
u32 value;
} = {
{ PRU_CAN_TX_GLOBAL_CONTROL_REGISTER, 0, },
{ PRU_CAN_TX_GLOBAL_STATUS_REGISTER, 0x40, },
{ PRU_CAN_RX_GLOBAL_STATUS_REGISTER, 0x40, },
...
};

Quoted 16 lines
Quoted 13 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +13 -0
+
+
+/*
+ * pru_can_emu_open() Opens the can emu for
+ * application to use. This API will be called by the Application
+ * to Open the can emu for application to use.
+ *
+ * param pstremuapphndl Pointer to application handler
+ * structure
+ *
+ * return   SUCCESS or FAILURE
+ */
+s16 pru_can_emu_open(struct device *dev, can_emu_app_hndl 
Quoted 1 line

*pstremuapphndl)

unused.

SG - OK.

Quoted 15 lines
Quoted 13 lines

+/*
+ * brief pru_can_emu_close() Closes the can emu for other
+ * applications to use. This API will be called by the Application to Close
+ * the can emu for other applications to use
+ *
+ * param pstremuapphndl Pointer to application handler structure
+ *
+ * return SUCCESS or FAILURE
+ */
+s16 pru_can_emu_close(struct device *dev, can_emu_app_hndl *pstremuapphndl)

unused

SG - OK.

Quoted 7 lines

drivers/net/can/da8xx_pruss/pruss_can_api.c line 989

Quoted 5 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +4 -0
+s16 pru_can_emu_sreset(struct device *dev)
+{
+ return 0;
+}

pointless.

SG - OK.

Quoted 23 lines
Quoted 23 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +22 -0
+s16 pru_can_tx(struct device *dev, u8 u8mailboxnumber, u8 u8prunumber)
+{
+ u32 u32offset = 0;
+ u32 u32value = 0;
+ s16 s16subrtnretval = -1;
+
+ if (DA8XX_PRUCORE_1 == u8prunumber) {
+ switch (u8mailboxnumber) {
+ case 0:
+ u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF);
+ u32value = 0x00000080;
+ s16subrtnretval = pruss_writel(dev, u32offset,
+ (u32 *) &u32value, 1);
+ if (s16subrtnretval == -1) {
+ return -1;
+ }
+ break;
+ case 1:
+ u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF);
+ u32value = 0x00000080;
+ s16subrtnretval = pruss_writel(dev, u32offset,
+ (u32 *) &u32value, 1);
Quoted 8 lines
Quoted 6 lines

+ if (s16subrtnretval == -1) {
+ return -1;
+ }
+ break;
+ case 2:
+ u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF);

Another pointless switch statement.

SG - OK.

Quoted 12 lines

drivers/net/can/da8xx_pruss/pruss_can_api.c line 1203

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +9 -0
+s16 pru_can_mask_ints(struct device *dev, u32 int_mask)
+{
+ return 0;
+}
+
+int pru_can_get_error_cnt(struct device *dev, u8 u8prunumber)
+{
+ return 0;
+}

useless.

SG - OK.

Quoted 18 lines
Quoted 18 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +17 -0
+int pru_can_get_intc_status(struct device *dev)
+{
+ u32 u32offset = 0;
+ u32 u32getvalue = 0;
+ u32 u32clrvalue = 0;
+
+ u32offset = (PRUSS_INTC_STATCLRINT1 & 0xFFFF);
+ pruss_readl(dev, u32offset, (u32 *) &u32getvalue, 1);
+
+ if (u32getvalue & 4)
+ u32clrvalue = 34; /* CLR Event 34 */
+
+ if (u32getvalue & 2)
+ u32clrvalue = 33; /* CLR Event 33  */
+
+ if (u32clrvalue) {
+ u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF);
Quoted 9 lines
Quoted 3 lines

+ pruss_writel(dev, u32offset, &u32clrvalue, 1);
+ } else
+ return -1;

Could the controller signal both event 34 and 33 simultaneously? The only user of this function looks at the individual bits of the return value again, which looks wrong for all possible return values here.

SG - OK. The System Events are mapped to 10 host interrupts (PRU to ARM/DSP),
as I our case two system events can be mapped to a single
host interrupt.

Quoted 21 lines
Quoted 21 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +21 -0
+#ifndef _PRU_CAN_API_H_
+#define CAN_BIT_TIMINGS (0x273)
+
+/* Timer Clock is sourced from DDR freq (PLL1 SYS CLK 2) */
+#define TIMER_CLK_FREQ 132000000
+
+#define TIMER_SETUP_DELAY 14
+#define GPIO_SETUP_DELAY 150
+
+#define CAN_RX_PRU_0 PRUSS_NUM0
+#define CAN_TX_PRU_1 PRUSS_NUM1
+
+/* Number of Instruction in the Delay loop */
+#define DELAY_LOOP_LENGTH 2
+
+#define PRU1_BASE_ADDR 0x2000
+
+#define PRU_CAN_TX_GLOBAL_CONTROL_REGISTER (PRU1_BASE_ADDR)
+#define PRU_CAN_TX_GLOBAL_STATUS_REGISTER (PRU1_BASE_ADDR + 0x04)
+#define PRU_CAN_TX_INTERRUPT_MASK_REGISTER (PRU1_BASE_ADDR + 0x08)
+#define PRU_CAN_TX_INTERRUPT_STATUS_REGISTER (PRU1_BASE_ADDR + 0x0C)
Quoted 8 lines

drivers/net/can/da8xx_pruss/pruss_can_api.c

Quoted 2 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +2 -0
+#define PRU_CAN_TX_MAILBOX0_STATUS_REGISTER (PRU1_BASE_ADDR + 0x10)
+#define PRU_CAN_TX_MAILBOX1_STATUS_REGISTER (PRU1_BASE_ADDR + 0x14)

The header file should be used for interfaces between the two .c files, don't mix that with hardware specific definitions. Sometimes you may want to have register number lists in a header, if that list is going to be used in multiple places. In this case, there is just one user, so better move all those definitions over there.

SG - OK.

Quoted 28 lines
Quoted 13 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +12 -0
+typedef enum {
+ ecaninst0 = 0,
+ ecaninst1,
+ ecanmaxinst
+} can_instance_enum;
+
+typedef enum {
+ ecanmailbox0 = 0,
+ ecanmailbox1,
+ ecanmailbox2,
+ ecanmailbox3,
+ ecanmailbox4,

drivers/net/can/da8xx_pruss/pruss_can_api.c

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+ ecanmailbox5,
+ ecanmailbox6,
+ ecanmailbox7
+} can_mailbox_number;
+
+typedef enum {
+ ecandirectioninit = 0,
+ ecantransmit,
+ ecanreceive
+} can_transfer_direction;

The values are all unused, you only use the typedefs. IMHO it would be more sensible to just pass these as unsigned int or u32 values, but if you prefer, there is no reason to just do

Quoted 3 lines

typedef u32 can_mailbox_number;

etc.

SG - OK.

Quoted 27 lines
Quoted 5 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +4 -0
+typedef struct {
+ u16 u16extendedidentifier;
+ u16 u16baseidentifier;
+ u8 u8data7;

drivers/net/can/da8xx_pruss/pruss_can_api.c

Quoted 10 lines
drivers/net/can/da8xx_pruss/pruss_can_api.c new file +10 -0
+ u8 u8data6;
+ u8 u8data5;
+ u8 u8data4;
+ u8 u8data3;
+ u8 u8data2;
+ u8 u8data1;
+ u8 u8data0;
+ u16 u16datalength;
+ u16 u16crc;
+} can_mail_box_structure;

Please don't use typedef for complex data structures, and learn about better naming of identifiers. I would suggest writing this as

struct pru_can_mailbox {
u16 ext_id;
u16 base_id;
u8 data[8]; /* note: reverse order */
u16 len;
u16 crc;
};

Quoted 1 line

Sames rules apply to the other structures.

SG - OK.

Quoted 2 lines

Arnd

#53 lore raw

PATCH v2 10/13 mfd: pruss SUART private data.

In reply to Subhasish Ghosh

This patch adds the PRUSS SUART data.

---
 include/linux/mfd/pruss/da8xx_pru.h |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)
include/linux/mfd/pruss/da8xx_pru.h +5 -0
index 50f2ede..4230d10 100644
@@ -93,6 +93,11 @@ struct da8xx_pruss_can_data {
 	u32 version;
 };
 
+struct da850_evm_pruss_suart_data {
+	u32 version;
+	struct resource resource;
+};
+
 u32 pruss_get_clk_freq(struct device *dev);
 
 u32 pruss_enable(struct device *dev, u8 pruss_num);
Signature · 1 line
1.7.2.3
#54 lore raw

PATCH v2 11/13 da850: pruss SUART board specific additions.

In reply to Subhasish Ghosh

This patch adds the pruss SUART pin mux and registers the device with the pruss mfd driver.

---
 arch/arm/mach-davinci/board-da850-evm.c |   36 +++++++++++++++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/board-da850-evm.c +36 -0
index f9c38f8..3858516 100644
@@ -1060,6 +1060,25 @@ const short da850_evm_pruss_can_pins[] = {
 	-1
 };
 
+const short da850_evm_pruss_suart_pins[] = {
+	DA850_AHCLKX, DA850_ACLKX, DA850_AFSX,
+	DA850_AHCLKR, DA850_ACLKR, DA850_AFSR,
+	DA850_AXR_13, DA850_AXR_9, DA850_AXR_7,
+	DA850_AXR_14, DA850_AXR_10, DA850_AXR_8,
+	-1
+};
+
+static int __init da850_evm_setup_pruss_suart(void)
+{
+	int ret;
+
+	ret = davinci_cfg_reg_list(da850_evm_pruss_suart_pins);
+	if (ret)
+		pr_warning("%s: da850_evm_pruss_suart_pins "
+			"mux setup failed: %d\n", __func__, ret);
+	return ret;
+}
+
 static int __init da850_evm_setup_pruss_can(void)
 {
 	int ret, val = 0;
@@ -1085,6 +1104,17 @@ static int __init da850_evm_setup_pruss_can(void)
 	return ret;
 }
 
+static struct da850_evm_pruss_suart_data suart_data = {
+	.version	= 1,
+	.resource	= {
+			.name	= "da8xx_mcasp0_iomem",
+			.start	= DAVINCI_DA8XX_MCASP0_REG_BASE,
+			.end	= DAVINCI_DA8XX_MCASP0_REG_BASE +
+					(SZ_1K * 12) - 1,
+			.flags	= IORESOURCE_MEM,
+		},
+};
+
 static struct da8xx_pruss_can_data can_data = {
 	.version	= 1,
 };
@@ -1094,6 +1124,12 @@ static struct da8xx_pruss_devices pruss_devices[] = {
 		.dev_name	= "da8xx_pruss_can",
 		.pdata		= &can_data,
 		.pdata_size	= sizeof(can_data),
+		.setup		= da850_evm_setup_pruss_suart,
+	},
+	{
+		.dev_name	= "da8xx_pruss_uart",
+		.pdata		= &suart_data,
+		.pdata_size	= sizeof(suart_data),
 		.setup		= da850_evm_setup_pruss_can,
 	},
 	{
Signature · 1 line
1.7.2.3
#55 lore raw

Re: [PATCH v2 11/13] da850: pruss SUART board specific additions.

Hi Subhasish,

Quoted 14 lines · Subhasish Ghosh

This patch adds the pruss SUART pin mux and registers the device with the pruss mfd driver.

---
 arch/arm/mach-davinci/board-da850-evm.c |   36 +++++++++++++++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/board-da850-evm.c +0 -0
index f9c38f8..3858516 100644
@@ -1060,6 +1060,25 @@ const short da850_evm_pruss_can_pins[] = {
 	-1

arch/arm/mach-davinci/board-da850-evm.c const short da850_evm_pruss_can_pins[] = { · line 1070

Quoted 10 lines
arch/arm/mach-davinci/board-da850-evm.c +8 -0
 };
 
+const short da850_evm_pruss_suart_pins[] = {
+	DA850_AHCLKX, DA850_ACLKX, DA850_AFSX,
+	DA850_AHCLKR, DA850_ACLKR, DA850_AFSR,
+	DA850_AXR_13, DA850_AXR_9, DA850_AXR_7,
+	DA850_AXR_14, DA850_AXR_10, DA850_AXR_8,
+	-1
+};
+

Shouldn't this pins select PRU[0,1]_XXX type functions and not McASP functions? E.G.: PRU0_R31[17] instead of AHCLKX, PRU0_R31[18] instead of AHCLKR, etc.

Quoted 33 lines
arch/arm/mach-davinci/board-da850-evm.c +22 -0
+static int __init da850_evm_setup_pruss_suart(void)
+{
+	int ret;
+
+	ret = davinci_cfg_reg_list(da850_evm_pruss_suart_pins);
+	if (ret)
+		pr_warning("%s: da850_evm_pruss_suart_pins "
+			"mux setup failed: %d\n", __func__, ret);
+	return ret;
+}
+
 static int __init da850_evm_setup_pruss_can(void)
 {
 	int ret, val = 0;
@@ -1085,6 +1104,17 @@ static int __init da850_evm_setup_pruss_can(void)
 	return ret;
 }
 
+static struct da850_evm_pruss_suart_data suart_data = {
+	.version	= 1,
+	.resource	= {
+			.name	= "da8xx_mcasp0_iomem",
+			.start	= DAVINCI_DA8XX_MCASP0_REG_BASE,
+			.end	= DAVINCI_DA8XX_MCASP0_REG_BASE +
+					(SZ_1K * 12) - 1,
+			.flags	= IORESOURCE_MEM,
+		},
+};
+
 static struct da8xx_pruss_can_data can_data = {
 	.version	= 1,
 };

arch/arm/mach-davinci/board-da850-evm.c static struct da8xx_pruss_devices pruss_devices[] = { · line 1127

Quoted 4 lines
arch/arm/mach-davinci/board-da850-evm.c +1 -0
@@ -1094,6 +1124,12 @@ static struct da8xx_pruss_devices pruss_devices[] = {
 		.dev_name	= "da8xx_pruss_can",
 		.pdata		= &can_data,
 		.pdata_size	= sizeof(can_data),
+		.setup		= da850_evm_setup_pruss_suart,

Should this be da850_evm_setup_pruss_can instead?

arch/arm/mach-davinci/board-da850-evm.c static struct da8xx_pruss_devices pruss_devices[] = { · line 1133

Quoted 6 lines
arch/arm/mach-davinci/board-da850-evm.c +5 -0
+	},
+	{
+		.dev_name	= "da8xx_pruss_uart",
+		.pdata		= &suart_data,
+		.pdata_size	= sizeof(suart_data),
 		.setup		= da850_evm_setup_pruss_can,

Should this be da850_evm_setup_pruss_suart instead?

arch/arm/mach-davinci/board-da850-evm.c static struct da8xx_pruss_devices pruss_devices[] = { · line 1135

Quoted 2 lines
arch/arm/mach-davinci/board-da850-evm.c +0 -0
 	},
 	{

-Mike

#56 lore raw

Re: [PATCH v2 11/13] da850: pruss SUART board specific additions.

--------------------------------------------------
From: "Michael Williamson" <[email protected]>
Sent: Friday, February 11, 2011 8:56 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; "Russell King" <[email protected]>; "Kevin Hilman" <[email protected]>; "open list" <[email protected]>; <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 11/13] da850: pruss SUART board specific additions.

Quoted 21 lines

Hi Subhasish,

Quoted 17 lines · Subhasish Ghosh

This patch adds the pruss SUART pin mux and registers the device with the pruss mfd driver.

---
 arch/arm/mach-davinci/board-da850-evm.c |   36 
+++++++++++++++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/board-da850-evm.c +0 -0
index f9c38f8..3858516 100644
@@ -1060,6 +1060,25 @@ const short da850_evm_pruss_can_pins[] = {
 -1
 };
 
Quoted 13 lines

arch/arm/mach-davinci/board-da850-evm.c const short da850_evm_pruss_can_pins[] = { · line 1070

Quoted 8 lines
arch/arm/mach-davinci/board-da850-evm.c +8 -0
+const short da850_evm_pruss_suart_pins[] = {
+ DA850_AHCLKX, DA850_ACLKX, DA850_AFSX,
+ DA850_AHCLKR, DA850_ACLKR, DA850_AFSR,
+ DA850_AXR_13, DA850_AXR_9, DA850_AXR_7,
+ DA850_AXR_14, DA850_AXR_10, DA850_AXR_8,
+ -1
+};
+

Shouldn't this pins select PRU[0,1]_XXX type functions and not McASP functions? E.G.: PRU0_R31[17] instead of AHCLKX, PRU0_R31[18] instead of AHCLKR, etc.

SG - The Soft-UART implementation uses the McASP as shift registers to push out the data sequentially.
Hence, we configure the McASP PINS and not the PRU PINS.

Quoted 33 lines
Quoted 33 lines
arch/arm/mach-davinci/board-da850-evm.c +22 -0
+static int __init da850_evm_setup_pruss_suart(void)
+{
+ int ret;
+
+ ret = davinci_cfg_reg_list(da850_evm_pruss_suart_pins);
+ if (ret)
+ pr_warning("%s: da850_evm_pruss_suart_pins "
+ "mux setup failed: %d\n", __func__, ret);
+ return ret;
+}
+
 static int __init da850_evm_setup_pruss_can(void)
 {
 int ret, val = 0;
@@ -1085,6 +1104,17 @@ static int __init da850_evm_setup_pruss_can(void)
 return ret;
 }
 
+static struct da850_evm_pruss_suart_data suart_data = {
+ .version = 1,
+ .resource = {
+ .name = "da8xx_mcasp0_iomem",
+ .start = DAVINCI_DA8XX_MCASP0_REG_BASE,
+ .end = DAVINCI_DA8XX_MCASP0_REG_BASE +
+ (SZ_1K * 12) - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
 static struct da8xx_pruss_can_data can_data = {
 .version = 1,
 };
@@ -1094,6 +1124,12 @@ static struct da8xx_pruss_devices pruss_devices[] 
Quoted 8 lines

arch/arm/mach-davinci/board-da850-evm.c static struct da8xx_pruss_devices pruss_devices[] = { · line 1127

Quoted 5 lines
arch/arm/mach-davinci/board-da850-evm.c +1 -0
 = {
 .dev_name = "da8xx_pruss_can",
 .pdata = &can_data,
 .pdata_size = sizeof(can_data),
+ .setup = da850_evm_setup_pruss_suart,

Should this be da850_evm_setup_pruss_can instead?

SG - This just the way the patch is displayed. In the code the order is correct.

Quoted 10 lines

arch/arm/mach-davinci/board-da850-evm.c static struct da8xx_pruss_devices pruss_devices[] = { · line 1133

Quoted 7 lines
arch/arm/mach-davinci/board-da850-evm.c +5 -0
+ },
+ {
+ .dev_name = "da8xx_pruss_uart",
+ .pdata = &suart_data,
+ .pdata_size = sizeof(suart_data),
 .setup = da850_evm_setup_pruss_can,

Should this be da850_evm_setup_pruss_suart instead?

SG - Ditto

Quoted 7 lines
Quoted 3 lines
arch/arm/mach-davinci/board-da850-evm.c +0 -0
 },

{

-Mike

#57 lore raw

Re: [PATCH v2 11/13] da850: pruss SUART board specific additions.

In reply to Subhasish Ghosh
Quoted 12 lines · Subhasish Ghosh

This patch adds the pruss SUART pin mux and registers the device with the pruss mfd driver.

---
 arch/arm/mach-davinci/board-da850-evm.c |   36 +++++++++++++++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/board-da850-evm.c +0 -0
index f9c38f8..3858516 100644

[...]

Quoted 5 lines
arch/arm/mach-davinci/board-da850-evm.c +1 -0
@@ -1085,6 +1104,17 @@ static int __init da850_evm_setup_pruss_can(void)
 	return ret;
 }
 
+static struct da850_evm_pruss_suart_data suart_data = {

arch/arm/mach-davinci/board-da850-evm.c static int __init da850_evm_setup_pruss_can(void) · line 1117

Quoted 10 lines
arch/arm/mach-davinci/board-da850-evm.c +10 -0
+	.version	= 1,
+	.resource	= {
+			.name	= "da8xx_mcasp0_iomem",
+			.start	= DAVINCI_DA8XX_MCASP0_REG_BASE,
+			.end	= DAVINCI_DA8XX_MCASP0_REG_BASE +
+					(SZ_1K * 12) - 1,
+			.flags	= IORESOURCE_MEM,
+		},
+};
+

I don't think passing a resource thru platform data is good idea...
Resources should be bound to the platform device.

WBR, Sergei

#58 lore raw

Re: [PATCH v2 11/13] da850: pruss SUART board specific additions.

Hello,

Have modified this as shown below:
Please let me know if this looks ok.

(quoted diff) +0 -0

b/arch/arm/mach-davinci/board-da850-evm.c +static struct da850_evm_pruss_can_data can_data = {
.version = 1,
};

+static struct resource da850_evm_suart_resource[] =
------------------------->>> Added a separate resource structure for
suart.
+ {

+               .name   = "da8xx_mcasp0_iomem",
+               .start  = DAVINCI_DA8XX_MCASP0_REG_BASE,
+               .end    = DAVINCI_DA8XX_MCASP0_REG_BASE +

+ (SZ_1K * 12) - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
static struct da8xx_pruss_devices pruss_devices[] = {
{
.dev_name = "da8xx_pruss_can",
.pdata = &can_data,
.pdata_size = sizeof(can_data),
.setup = da850_evm_setup_pruss_can,

+               .num_resources  = 0,
+               .resources      = NULL,
        },
        {
                .dev_name       = "da8xx_pruss_uart",
                .pdata          = &suart_data,
                .pdata_size     = sizeof(suart_data),
                .setup          = da850_evm_setup_pruss_suart,
+               .num_resources  = ARRAY_SIZE(da850_evm_suart_resource),
+               .resources      = 
a850_evm_suart_resource,  ---------------------------- >>>>> Initialized it 
here
        },
        {
                .dev_name       = NULL,
drivers/mfd/da8xx_pru.c +0 -0
index f7868a4..140bf14 100644
@@ -332,6 +332,8 @@ static int pruss_mfd_add_devices(struct platform_device 

*pdev)

                cell.name               = (dev_data + count)->dev_name;
                cell.platform_data      = (dev_data + count)->pdata;
                cell.data_size          = (dev_data + count)->pdata_size;
+               cell.resources          = (dev_data + 

ount)->resources; ---------------------->>>Modified the MFD driver to add the resources.
+ cell.num_resources = (dev_data + count)->num_resources;

(quoted diff) +0 -0

b/drivers/tty/serial/da8xx_pruss/pruss_suart.c

+ res = platform_get_resource(pdev, IORESOURCE_MEM,
0); ------------------>> Used platform infrastructure to get the data.
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get resource");
+ return -ENOMEM;
+ }
+
+ if (!request_mem_region(res->start,
+ resource_size(res),

--------------------------------------------------
From: "Sergei Shtylyov" <[email protected]>
Sent: Saturday, February 12, 2011 12:20 AM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; "Russell King" <[email protected]>; "Kevin Hilman" <[email protected]>; <[email protected]>; "open list" <[email protected]>; <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 11/13] da850: pruss SUART board specific additions.

Quoted 36 lines
Quoted 14 lines · Subhasish Ghosh

This patch adds the pruss SUART pin mux and registers the device with the pruss mfd driver.

---
 arch/arm/mach-davinci/board-da850-evm.c |   36 
+++++++++++++++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/board-da850-evm.c +0 -0
index f9c38f8..3858516 100644

[...]

Quoted 4 lines
arch/arm/mach-davinci/board-da850-evm.c +0 -0
@@ -1085,6 +1104,17 @@ static int __init da850_evm_setup_pruss_can(void)
 return ret;
 }
 +static struct da850_evm_pruss_suart_data suart_data = {

arch/arm/mach-davinci/board-da850-evm.c static int __init da850_evm_setup_pruss_can(void) · line 1116

Quoted 10 lines
arch/arm/mach-davinci/board-da850-evm.c +10 -0
+ .version = 1,
+ .resource = {
+ .name = "da8xx_mcasp0_iomem",
+ .start = DAVINCI_DA8XX_MCASP0_REG_BASE,
+ .end = DAVINCI_DA8XX_MCASP0_REG_BASE +
+ (SZ_1K * 12) - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+

I don't think passing a resource thru platform data is good idea...
Resources should be bound to the platform device.

WBR, Sergei

#59 lore raw

PATCH v2 12/13 da850: pruss SUART platform specific additions.

In reply to Subhasish Ghosh

This patch adds the McASP clock alias.
The alias is used by the pruss suart driver
for enabling the McASP PSC.

---
 arch/arm/mach-davinci/devices-da8xx.c |   11 +++++++++++
 1 files changed, 11 insertions(+), 0 deletions(-)
arch/arm/mach-davinci/devices-da8xx.c +11 -0
index e15de72..f1cf605 100644
@@ -560,7 +560,18 @@ struct platform_device da8xx_pruss_dev = {
 
 int __init da8xx_register_pruss(struct da8xx_pruss_devices *pruss_device)
 {
+#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE
+	int ret;
+#endif
+
 	da8xx_pruss_dev.dev.platform_data = pruss_device;
+
+#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE
+	ret = clk_add_alias(NULL, "da8xx_pruss_uart.1",
+	NULL, &da850_mcasp_device.dev);
+	if (ret < 0)
+		return ret;
+#endif
 	return platform_device_register(&da8xx_pruss_dev);
 }
 
Signature · 1 line
1.7.2.3
#60 lore raw

Re: [PATCH v2 12/13] da850: pruss SUART platform specific additions.

Quoted 5 lines · Subhasish Ghosh

This patch adds the McASP clock alias.
The alias is used by the pruss suart driver
for enabling the McASP PSC.

[...]

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 563

Quoted 9 lines
arch/arm/mach-davinci/devices-da8xx.c +1 -0
index e15de72..f1cf605 100644
@@ -560,7 +560,18 @@ struct platform_device da8xx_pruss_dev = {
 
 int __init da8xx_register_pruss(struct da8xx_pruss_devices *pruss_device)
 {
+#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE
    #ifdef's in the function body are generally fromned upon.

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 566

Quoted 3 lines
arch/arm/mach-davinci/devices-da8xx.c +3 -0
+	int ret;
+#endif
+

This line should have been inside #ifdef...

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 569

Quoted 3 lines
arch/arm/mach-davinci/devices-da8xx.c +2 -0
 	da8xx_pruss_dev.dev.platform_data = pruss_device;
+
+#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE

Why not do it before assigning the platform data and avoid extra #ifdef?

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 571

Quoted 2 lines
arch/arm/mach-davinci/devices-da8xx.c +2 -0
+	ret = clk_add_alias(NULL, "da8xx_pruss_uart.1",
+	NULL, &da850_mcasp_device.dev);

This line should be indented more to the right.

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 574

Quoted 3 lines
arch/arm/mach-davinci/devices-da8xx.c +3 -0
+	if (ret < 0)
+		return ret;
+#endif

WBR, Sergei

#61 lore raw

Re: [PATCH v2 12/13] da850: pruss SUART platform specific additions.

Hello,

I could not follow your recommendations clearly, are you suggesting this:

int __init da8xx_register_pruss(struct da8xx_pruss_devices *pruss_device) {

#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE
        int ret;

        ret = clk_add_alias(NULL, "da8xx_pruss_uart.1",
                            NULL, &da850_mcasp_device.dev);
        if (ret < 0)
                return ret;
#endif
        da8xx_pruss_dev.dev.platform_data = pruss_device;
        return platform_device_register(&da8xx_pruss_dev);
}

--------------------------------------------------
From: "Sergei Shtylyov" <[email protected]>
Sent: Saturday, February 12, 2011 12:25 AM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; "Russell King" <[email protected]>; "Kevin Hilman" <[email protected]>; <[email protected]>; "open list" <[email protected]>; <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 12/13] da850: pruss SUART platform specific additions.

Quoted 45 lines
Quoted 5 lines · Subhasish Ghosh

This patch adds the McASP clock alias.
The alias is used by the pruss suart driver
for enabling the McASP PSC.

[...]

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 563

Quoted 10 lines
arch/arm/mach-davinci/devices-da8xx.c +1 -0
index e15de72..f1cf605 100644
@@ -560,7 +560,18 @@ struct platform_device da8xx_pruss_dev = {
 int __init da8xx_register_pruss(struct da8xx_pruss_devices 
 *pruss_device)
 {
+#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE
   #ifdef's in the function body are generally fromned upon.

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 566

Quoted 3 lines
arch/arm/mach-davinci/devices-da8xx.c +3 -0
+ int ret;
+#endif
+

This line should have been inside #ifdef...

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 569

Quoted 3 lines
arch/arm/mach-davinci/devices-da8xx.c +2 -0
 da8xx_pruss_dev.dev.platform_data = pruss_device;
+
+#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE

Why not do it before assigning the platform data and avoid extra
#ifdef?

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 571

Quoted 2 lines
arch/arm/mach-davinci/devices-da8xx.c +2 -0
+ ret = clk_add_alias(NULL, "da8xx_pruss_uart.1",
+ NULL, &da850_mcasp_device.dev);

This line should be indented more to the right.

arch/arm/mach-davinci/devices-da8xx.c struct platform_device da8xx_pruss_dev = { · line 574

Quoted 3 lines
arch/arm/mach-davinci/devices-da8xx.c +3 -0
+ if (ret < 0)
+ return ret;
+#endif

WBR, Sergei

#62 lore raw

Re: [PATCH v2 12/13] da850: pruss SUART platform specific additions.

Hello.

Quoted 7 lines · Subhasish Ghosh

I could not follow your recommendations clearly, are you suggesting this:

int __init da8xx_register_pruss(struct da8xx_pruss_devices *pruss_device) {
#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE
int ret;

Quoted 8 lines

ret = clk_add_alias(NULL, "da8xx_pruss_uart.1", NULL, &da850_mcasp_device.dev);
if (ret < 0)
return ret;
#endif
da8xx_pruss_dev.dev.platform_data = pruss_device; return platform_device_register(&da8xx_pruss_dev); }

Yes. But still better would be to wrap clk_add_alias() into a function of
its own (defining it empty if CONFIG_SERIAL_PRUSS_SUART_MODULE is not defined).

WBR, Sergei

#63 lore raw

Re: [PATCH v2 12/13] da850: pruss SUART platform specific additions.

Ok, will do.

--------------------------------------------------
From: "Sergei Shtylyov" <[email protected]>
Sent: Tuesday, February 22, 2011 4:50 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; "Russell King" <[email protected]>; "Kevin Hilman" <[email protected]>; <[email protected]>; "open list" <[email protected]>; <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 12/13] da850: pruss SUART platform specific additions.

Quoted 24 lines

Hello.

Quoted 7 lines · Subhasish Ghosh

I could not follow your recommendations clearly, are you suggesting this:

int __init da8xx_register_pruss(struct da8xx_pruss_devices *pruss_device) {
#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE
int ret;

Quoted 8 lines

ret = clk_add_alias(NULL, "da8xx_pruss_uart.1", NULL, &da850_mcasp_device.dev);
if (ret < 0)
return ret;
#endif
da8xx_pruss_dev.dev.platform_data = pruss_device; return platform_device_register(&da8xx_pruss_dev); }

Yes. But still better would be to wrap clk_add_alias() into a function
of its own (defining it empty if CONFIG_SERIAL_PRUSS_SUART_MODULE is not defined).

WBR, Sergei

#64 lore raw

PATCH v2 13/13 tty: pruss SUART driver

In reply to Subhasish Ghosh

This patch adds support for the TTY compliant
Soft-UART device emulated on PRUSS.

---
 drivers/tty/serial/Kconfig                         |    2 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/da8xx_pruss/Kconfig             |   19 +
 drivers/tty/serial/da8xx_pruss/Makefile            |    9 +
 drivers/tty/serial/da8xx_pruss/pruss_suart.c       | 1014 +++++++++
 drivers/tty/serial/da8xx_pruss/pruss_suart_api.c   | 2350 ++++++++++++++++++++
 drivers/tty/serial/da8xx_pruss/pruss_suart_api.h   |  345 +++
 drivers/tty/serial/da8xx_pruss/pruss_suart_board.h |   58 +
 drivers/tty/serial/da8xx_pruss/pruss_suart_err.h   |   48 +
 drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h |  526 +++++
 drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h  |  153 ++
 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c |  384 ++++
 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h |   63 +
 include/linux/serial_core.h                        |    2 +
 14 files changed, 4974 insertions(+), 0 deletions(-)
 create mode 100644 drivers/tty/serial/da8xx_pruss/Kconfig
 create mode 100644 drivers/tty/serial/da8xx_pruss/Makefile
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart.c
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_api.c
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_api.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_board.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_err.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c
 create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h
drivers/tty/serial/Kconfig +2 -0
index b1682d7..68c40c2 100644
@@ -1595,4 +1595,6 @@ config SERIAL_PCH_UART
 	  This driver is for PCH(Platform controller Hub) UART of Intel EG20T
 	  which is an IOH(Input/Output Hub) for x86 embedded processor.
 	  Enabling PCH_DMA, this PCH UART works as DMA mode.
+
+source "drivers/tty/serial/da8xx_pruss/Kconfig"
 endmenu
drivers/tty/serial/Makefile +1 -0
index 8ea92e9..51c89e9 100644
@@ -92,3 +92,4 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110)	+= mrst_max3110.o
 obj-$(CONFIG_SERIAL_MFD_HSU)	+= mfd.o
 obj-$(CONFIG_SERIAL_IFX6X60)  	+= ifx6x60.o
 obj-$(CONFIG_SERIAL_PCH_UART)	+= pch_uart.o
+obj-$(CONFIG_SERIAL_PRUSS_SUART)	+= da8xx_pruss/
drivers/tty/serial/da8xx_pruss/Kconfig new file +19 -0
new file mode 100644
index 0000000..23ac53f
@@ -0,0 +1,19 @@
+#
+# SUART Kernel Configuration
+#
+
+config SERIAL_PRUSS_SUART
+	depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850
+	select SERIAL_CORE
+	tristate "PRUSS based SoftUART emulation on DA8XX"
+	---help---
+	Enable this to emulate a UART controller on the PRUSS of DA8XX.
+	If not sure, mark N
+
+config PRUSS_SUART_MCASP
+    int "McASP number"
+    depends on ARCH_DAVINCI && ARCH_DAVINCI_DA830 && SERIAL_PRUSS_SUART
+    default "0"
+    ---help---
+    Enter the McASP number to use with SUART (0, 1 or 2).
+	You will need to recompile the kernel if this is changed.
drivers/tty/serial/da8xx_pruss/Makefile new file +9 -0
new file mode 100644
index 0000000..34a5b1f
@@ -0,0 +1,9 @@
+#
+# Makefile for SoftUART emulation
+#
+
+suart_emu-objs :=       pruss_suart.o \
+                        pruss_suart_api.o \
+                        pruss_suart_utils.o
+
+obj-$(CONFIG_SERIAL_PRUSS_SUART)        += suart_emu.o
drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +1014 -0

1,014 lines not on this page · raw message

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +2350 -0

1,948 lines not on this page · raw message

drivers/tty/serial/da8xx_pruss/pruss_suart_api.h new file +345 -0

345 lines not on this page · raw message

drivers/tty/serial/da8xx_pruss/pruss_suart_board.h new file +58 -0

58 lines not on this page · raw message

drivers/tty/serial/da8xx_pruss/pruss_suart_err.h new file +48 -0

48 lines not on this page · raw message

drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h new file +526 -0

526 lines not on this page · raw message

drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h new file +153 -0

153 lines not on this page · raw message

drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c new file +384 -0

384 lines not on this page · raw message

drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h new file +63 -0

63 lines not on this page · raw message

include/linux/serial_core.h +2 -0
index 758c5b0..eae37fe 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
1.7.2.3

Part of the diff is not shown here. View the raw message.

#65 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Don't see why it needs its own sub-directory

drivers/tty/serial/da8xx_pruss/pruss_suart.c line 57

Quoted 8 lines
drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +8 -0
+#ifdef __SUART_DEBUG
+#define __suart_debug(fmt, args...) \
+		printk(KERN_DEBUG "suart_debug: " fmt, ## args)
+#else
+#define __suart_debug(fmt, args...)
+#endif
+
+#define  __suart_err(fmt, args...) printk(KERN_ERR "suart_err: " fmt, ## args)

Use dev_dbg/dev_err/pr_debug/pr_err

drivers/tty/serial/da8xx_pruss/pruss_suart.c line 126

Quoted 11 lines
drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +11 -0
+static void omapl_pru_tx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no)
+{
+	struct circ_buf *xmit = &soft_uart->port[uart_no].state->xmit;
+	struct device *dev = soft_uart->dev;
+	s32 count = 0;
+
+	if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX))
+		return;
+
+	if (down_trylock(&soft_uart->port_sem[uart_no]))
+		return;

Please use a mutex not a semaphore. We are trying to get almost all the kernel using mutexes as it is needed for hard real time.

drivers/tty/serial/da8xx_pruss/pruss_suart.c line 214

Quoted 13 lines
drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +13 -0
+		pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no],
+				       suart_data, data_len + 1,
+				       &data_len_read);
+
+		for (i = 0; i <= data_len_read; i++) {
+			soft_uart->port[uart_no].icount.rx++;
+			/* check for sys rq */
+			if (uart_handle_sysrq_char
+			    (&soft_uart->port[uart_no], suart_data))
+				continue;
+		}
+		/* update the tty data structure */
+		tty_insert_flip_string(tty, suart_data, data_len_read);

You can avoid a copy here by using tty_prepare_flip_string()

Also please use the tty_port_tty_get/tty_kref_put interfaces so the tty refcounting is right

Quoted 9 lines
drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +9 -0
+static void pruss_suart_set_termios(struct uart_port *port,
+				  struct ktermios *termios,
+				  struct ktermios *old)
+{
+	struct omapl_pru_suart *soft_uart =
+	    container_of(port, struct omapl_pru_suart, port[port->line]);
+	struct device *dev = soft_uart->dev;
+	u8 cval = 0;
+	unsigned long flags = 0;

drivers/tty/serial/da8xx_pruss/pruss_suart.c line 360

Quoted 10 lines
drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +10 -0
+	u32 baud = 0;
+	u32 old_csize = old ? old->c_cflag & CSIZE : CS8;
+
+/*
+ * Do not allow unsupported configurations to be set
+ */
+	if (1) {
+		termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR | CSTOPB
+				      | PARENB | PARODD | CMSPAR);
+		termios->c_cflag |= CLOCAL;

No. CLOCAL and HUPCL are user side flags. Apps expect to able to set them even if in fact they have no effect, so leave those two alone. The rest is fine.

Quoted 2 lines

+/*
+ * Characteres to ignore

Typo

Quoted 21 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +15 -0
new file mode 100644
index 0000000..d809dd3
@@ -0,0 +1,2350 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Jitendra Kumar <[email protected]>
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/types.h>

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c line 25

Quoted 10 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +10 -0
+#include <linux/mfd/pruss/da8xx_pru.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include "pruss_suart_api.h"
+#include "pruss_suart_regs.h"
+#include "pruss_suart_board.h"
+#include "pruss_suart_utils.h"
+#include "pruss_suart_err.h"
+
+static u8 g_uart_statu_table[8];

Can you lose the g_, its a windows naming convention we dont use

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c line 441

Quoted 2 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +2 -0
+s16 pru_softuart_open(suart_handle h_suart)
+{

If you just used normal integers you could surely make this routine trivial and remove all the duplication in the switches

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c line 28

Quoted 1 line
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +1 -0
+	s16 status = PRU_SUART_SUCCESS;

And please stick to Linux error codes

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c line 583

Quoted 7 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +7 -0
+/* suart instance close routine */
+s16 pru_softuart_close(suart_handle h_uart)
+{
+	s16 status = SUART_SUCCESS;
+
+	if (h_uart == NULL) {
+		return PRU_SUART_ERR_HANDLE_INVALID;

Which is never checked. Far better to use WARN_ON and the like for such cases - or if like this one they don't appear to be possible to simply delete them

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c line 613

Quoted 4 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +4 -0
+	if ((tx_clk_divisor > 385) || (tx_clk_divisor == 0))
+		return SUART_INVALID_CLKDIVISOR;
+	if ((rx_clk_divisor > 385) || (rx_clk_divisor == 0))
+		return SUART_INVALID_CLKDIVISOR;

[minor] Lots of excess brackets

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c line 1923

Quoted 5 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +5 -0
+		u32offset = (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF);
+		u32value = PRU_INTC_CHAN_34_SYSEVT_36_39;
+		s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1);
+		if (-1 == s16retval)
+			return -1;

If you fixed the API here you'd be able to just write

		if (pruss_writel(dev, PRUSS_INTC_CHANMAP9 & 0xFFFF,
				PRU_INTC_BLAH)

or similar which would clean up a lot of messy code and shrink it dramatically. Given you have lots of series of writes some kind of

pruss_writel_multi(dev, array, len)

that took an array of addr/value pairs might also clean up a ton of code and turn it into trivial tables

#66 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Hello,

Regarding the semaphore to mutex migration.
We are using down_trylock in interrupt context,
mutex_trylock cannot be used in interrupt context, so we cannot use mutex in our driver.

--------------------------------------------------
From: "Alan Cox" <[email protected]>
Sent: Friday, February 11, 2011 9:58 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "Greg Kroah-Hartman" <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 172 lines

Don't see why it needs its own sub-directory

Quoted 9 lines
drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +8 -0
+#ifdef __SUART_DEBUG
+#define __suart_debug(fmt, args...) \
+ printk(KERN_DEBUG "suart_debug: " fmt, ## args)
+#else
+#define __suart_debug(fmt, args...)
+#endif
+
+#define  __suart_err(fmt, args...) printk(KERN_ERR "suart_err: " fmt, ## 

args)

Use dev_dbg/dev_err/pr_debug/pr_err

drivers/tty/serial/da8xx_pruss/pruss_suart.c line 126

Quoted 12 lines

+static void omapl_pru_tx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no)

drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +10 -0
+{
+ struct circ_buf *xmit = &soft_uart->port[uart_no].state->xmit;
+ struct device *dev = soft_uart->dev;
+ s32 count = 0;
+
+ if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX))
+ return;
+
+ if (down_trylock(&soft_uart->port_sem[uart_no]))
+ return;

Please use a mutex not a semaphore. We are trying to get almost all the kernel using mutexes as it is needed for hard real time.

drivers/tty/serial/da8xx_pruss/pruss_suart.c line 214

Quoted 13 lines

+ pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no],

drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +12 -0
+        suart_data, data_len + 1,
+        &data_len_read);
+
+ for (i = 0; i <= data_len_read; i++) {
+ soft_uart->port[uart_no].icount.rx++;
+ /* check for sys rq */
+ if (uart_handle_sysrq_char
+     (&soft_uart->port[uart_no], suart_data))
+ continue;
+ }
+ /* update the tty data structure */
+ tty_insert_flip_string(tty, suart_data, data_len_read);

You can avoid a copy here by using tty_prepare_flip_string()

Also please use the tty_port_tty_get/tty_kref_put interfaces so the tty refcounting is right

Quoted 9 lines
drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +9 -0
+static void pruss_suart_set_termios(struct uart_port *port,
+   struct ktermios *termios,
+   struct ktermios *old)
+{
+ struct omapl_pru_suart *soft_uart =
+     container_of(port, struct omapl_pru_suart, port[port->line]);
+ struct device *dev = soft_uart->dev;
+ u8 cval = 0;
+ unsigned long flags = 0;

drivers/tty/serial/da8xx_pruss/pruss_suart.c line 360

Quoted 10 lines
drivers/tty/serial/da8xx_pruss/pruss_suart.c new file +10 -0
+ u32 baud = 0;
+ u32 old_csize = old ? old->c_cflag & CSIZE : CS8;
+
+/*
+ * Do not allow unsupported configurations to be set
+ */
+ if (1) {
+ termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR | CSTOPB
+       | PARENB | PARODD | CMSPAR);
+ termios->c_cflag |= CLOCAL;

No. CLOCAL and HUPCL are user side flags. Apps expect to able to set them even if in fact they have no effect, so leave those two alone. The rest is fine.

Quoted 2 lines

+/*
+ * Characteres to ignore

Typo

Quoted 23 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +5 -0
new file mode 100644
index 0000000..d809dd3
@@ -0,0 +1,2350 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Jitendra Kumar <[email protected]>
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +2 -0
+
+#include <linux/types.h>

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c line 25

Quoted 10 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +10 -0
+#include <linux/mfd/pruss/da8xx_pru.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include "pruss_suart_api.h"
+#include "pruss_suart_regs.h"
+#include "pruss_suart_board.h"
+#include "pruss_suart_utils.h"
+#include "pruss_suart_err.h"
+
+static u8 g_uart_statu_table[8];

Can you lose the g_, its a windows naming convention we dont use

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c line 441

Quoted 2 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +2 -0
+s16 pru_softuart_open(suart_handle h_suart)
+{

If you just used normal integers you could surely make this routine trivial and remove all the duplication in the switches

Quoted 1 line

+ s16 status = PRU_SUART_SUCCESS;

And please stick to Linux error codes

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c line 583

Quoted 7 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +7 -0
+/* suart instance close routine */
+s16 pru_softuart_close(suart_handle h_uart)
+{
+ s16 status = SUART_SUCCESS;
+
+ if (h_uart == NULL) {
+ return PRU_SUART_ERR_HANDLE_INVALID;

Which is never checked. Far better to use WARN_ON and the like for such cases - or if like this one they don't appear to be possible to simply delete them

Quoted 4 lines

+ if ((tx_clk_divisor > 385) || (tx_clk_divisor == 0))
+ return SUART_INVALID_CLKDIVISOR;
+ if ((rx_clk_divisor > 385) || (rx_clk_divisor == 0))
+ return SUART_INVALID_CLKDIVISOR;

[minor] Lots of excess brackets

Quoted 5 lines

+ u32offset = (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF);
+ u32value = PRU_INTC_CHAN_34_SYSEVT_36_39;
+ s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1);
+ if (-1 == s16retval)
+ return -1;

If you fixed the API here you'd be able to just write

if (pruss_writel(dev, PRUSS_INTC_CHANMAP9 & 0xFFFF, PRU_INTC_BLAH)

or similar which would clean up a lot of messy code and shrink it dramatically. Given you have lots of series of writes some kind of

pruss_writel_multi(dev, array, len)

that took an array of addr/value pairs might also clean up a ton of code and turn it into trivial tables

#67 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 6 lines · On Fri, 18 Feb 2011 19:17:38 +0530 "Subhasish Ghosh"…

Hello,

Regarding the semaphore to mutex migration.
We are using down_trylock in interrupt context,
mutex_trylock cannot be used in interrupt context, so we cannot use mutex in our driver.

Then you probably need to rework your locking. Best bet might be to fix all the other stuff and report the driver, and people can think about the locking problem.

Alan

#68 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 5 lines · Alan Cox
Quoted 2 lines · On Fri, 18 Feb 2011 19:17:38 +0530 "Subhasish Ghosh"…

Hello,

Quoted 8 lines
Quoted 4 lines

Regarding the semaphore to mutex migration.
We are using down_trylock in interrupt context,
mutex_trylock cannot be used in interrupt context, so we cannot use mutex in our driver.

Then you probably need to rework your locking. Best bet might be to fix all the other stuff and report the driver, and people can think about the locking problem.

That semaphore is utterly useless to begin with. There are more serious locking problems than this one. Non serialized calls to suart_intr_clrmask/suart_intr_setmask are the most obvious ones.

Aside of that the code is complete unreadable.

Thanks,

tglx

#69 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 16 lines · On Friday 18 February 2011 19:23:49 Thomas Gleixner wrote:
Quoted 5 lines · Alan Cox
Quoted 2 lines · On Fri, 18 Feb 2011 19:17:38 +0530 "Subhasish Ghosh"…

Hello,

Quoted 8 lines
Quoted 4 lines

Regarding the semaphore to mutex migration.
We are using down_trylock in interrupt context,
mutex_trylock cannot be used in interrupt context, so we cannot use mutex in our driver.

Then you probably need to rework your locking. Best bet might be to fix all the other stuff and report the driver, and people can think about the locking problem.

Quoted 5 lines

That semaphore is utterly useless to begin with. There are more serious locking problems than this one. Non serialized calls to suart_intr_clrmask/suart_intr_setmask are the most obvious ones.

Aside of that the code is complete unreadable.

I think it mostly suffers from the same problem as the CAN driver I commented on earlier: One of the files (pruss_suart_api.c) was clearly not written with Linux as the target, and the other files try to work around this by wrapping a Linux driver around it.

The suart_api HAL stuff clearly needs to go away, so that the rest can be rewritten into a proper device driver.

Arnd

#70 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Hello,

I had kept separate files to affirm the modularity and ease of portability of the system.

There are three different interfaces,
1. The Linux driver interface
2. The PRU control interface
3. The McASP serializer interface.

To maintain modularity, I had classified the files respectively as :

1.  pruss_suart.c
2.  pruss_suart_api.c
3.  pruss_suart_utils.c

This is not a single device which can be expressed as a single file, but functionally different devices logically cascaded together to work in unison.

We use the PRU for packet processing, but the actual data is transmitted/received through the McASP, which we use as a serializer.

I feel to combine these disparate functionalities into a single file will not

1. Help better understanding the device. I mean, why should a TTY UART driver be aware of the McASP or the PRU.
2. In case of a bug in the API layer or McASP, the driver need not be touched, thus improve maintainability.
3. If we need to port it to another Linux version, just editing the driver file should suffice, this will reduce bugs while porting.

To me, combining all of these into a single file only creates a mess. This is the reason I had separated them into different files!! I don't understand why should it be better to have all of these into a single file.

--------------------------------------------------
From: "Arnd Bergmann" <[email protected]>
Sent: Saturday, February 19, 2011 12:21 AM
To: <[email protected]>
Cc: "Thomas Gleixner" <[email protected]>; "Alan Cox" <[email protected]>; <[email protected]>; <[email protected]>; "Subhasish Ghosh" <[email protected]>; "Greg Kroah-Hartman" <[email protected]>; <[email protected]>; "open list" <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 32 lines · On Friday 18 February 2011 19:23:49 Thomas Gleixner wrote:
Quoted 16 lines · Alan Cox
Quoted 6 lines
Quoted 2 lines · On Fri, 18 Feb 2011 19:17:38 +0530 "Subhasish Ghosh"…

Hello,

Quoted 8 lines
Quoted 4 lines

Regarding the semaphore to mutex migration.
We are using down_trylock in interrupt context,
mutex_trylock cannot be used in interrupt context, so we cannot use mutex in our driver.

Then you probably need to rework your locking. Best bet might be to fix all the other stuff and report the driver, and people can think about the locking problem.

Quoted 5 lines

That semaphore is utterly useless to begin with. There are more serious locking problems than this one. Non serialized calls to suart_intr_clrmask/suart_intr_setmask are the most obvious ones.

Aside of that the code is complete unreadable.

I think it mostly suffers from the same problem as the CAN driver I commented on earlier: One of the files (pruss_suart_api.c) was clearly not written with Linux as the target, and the other files try to work around this by wrapping a Linux driver around it.

The suart_api HAL stuff clearly needs to go away, so that the rest can be rewritten into a proper device driver.

Arnd

#71 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 26 lines · Subhasish Ghosh

Hello,

I had kept separate files to affirm the modularity and ease of portability of the system.

There are three different interfaces,
1. The Linux driver interface
2. The PRU control interface
3. The McASP serializer interface.

To maintain modularity, I had classified the files respectively as :

1.  pruss_suart.c
2.  pruss_suart_api.c
3.  pruss_suart_utils.c

This is not a single device which can be expressed as a single file, but functionally different devices logically cascaded together to work in unison.

We use the PRU for packet processing, but the actual data is transmitted/received through the
McASP, which we use as a serializer.

I feel to combine these disparate functionalities into a single file will not

Quoted 6 lines

1. Help better understanding the device. I mean, why should a TTY UART driver be aware of the McASP or the PRU.
2. In case of a bug in the API layer or McASP, the driver need not be touched, thus improve maintainability.
3. If we need to port it to another Linux version, just editing the driver file should suffice, this will reduce bugs while porting.

If your code is in the kernel tree, you do not need to ever port it to a new version, as it will happen automatically as new kernels are released, so this really isn't anything to worry about.

Quoted 4 lines

To me, combining all of these into a single file only creates a mess. This is the reason I had separated them into different files!! I don't understand why should it be better to have all of these into a single file.

As Alan stated, just use 3 files in the directory with the other drivers, you don't need a subdir for something small like this.

thanks,

greg k-h

#72 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

I could not follow the recommendations clearly.
This is just to clarify.

Currently, I have the following files for the suart implementation:

drivers/tty/serial/da8xx_pruss/pruss_suart_api.h drivers/tty/serial/da8xx_pruss/pruss_suart_err.h drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h drivers/tty/serial/da8xx_pruss/pruss_suart_board.h drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c drivers/tty/serial/da8xx_pruss/pruss_suart.c drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c

Of these, I will be removing pruss_suart_err.h as part of the Linux error code cleanup. But, I need to keep at least pruss_suart_board.h as a separate file, as this defines configurations which will be often modified by users, I don't want to mix it with other files.

Should I combine rest of the headers into a single file ? and keep the other three .c files under "drivers/tty/serial/" and remove the da8xx_pruss directory altogether.

--------------------------------------------------
From: "Greg KH" <[email protected]>
Sent: Tuesday, February 22, 2011 8:07 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: "Arnd Bergmann" <[email protected]>; <[email protected]>; "Thomas Gleixner" <[email protected]>; "Alan Cox" <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "open list" <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 49 lines · Subhasish Ghosh
Quoted 26 lines

Hello,

I had kept separate files to affirm the modularity and ease of portability of the system.

There are three different interfaces,
1. The Linux driver interface
2. The PRU control interface
3. The McASP serializer interface.

To maintain modularity, I had classified the files respectively as :

1.  pruss_suart.c
2.  pruss_suart_api.c
3.  pruss_suart_utils.c

This is not a single device which can be expressed as a single file, but functionally different devices logically cascaded together to work in unison.

We use the PRU for packet processing, but the actual data is transmitted/received through the
McASP, which we use as a serializer.

I feel to combine these disparate functionalities into a single file will not

Quoted 6 lines

1. Help better understanding the device. I mean, why should a TTY UART driver be aware of the McASP or the PRU.
2. In case of a bug in the API layer or McASP, the driver need not be touched, thus improve maintainability.
3. If we need to port it to another Linux version, just editing the driver file should suffice, this will reduce bugs while porting.

If your code is in the kernel tree, you do not need to ever port it to a new version, as it will happen automatically as new kernels are released, so this really isn't anything to worry about.

Quoted 4 lines

To me, combining all of these into a single file only creates a mess. This is the reason I had separated them into different files!! I don't understand why should it be better to have all of these into a single file.

As Alan stated, just use 3 files in the directory with the other drivers, you don't need a subdir for something small like this.

thanks,

greg k-h

#73 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 16 lines · Subhasish Ghosh

I could not follow the recommendations clearly.
This is just to clarify.

Currently, I have the following files for the suart implementation:

drivers/tty/serial/da8xx_pruss/pruss_suart_api.h drivers/tty/serial/da8xx_pruss/pruss_suart_err.h drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h drivers/tty/serial/da8xx_pruss/pruss_suart_board.h drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c drivers/tty/serial/da8xx_pruss/pruss_suart.c drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c

Quoted 6 lines

Of these, I will be removing pruss_suart_err.h as part of the Linux error code cleanup.
But, I need to keep at least pruss_suart_board.h as a separate file, as this defines
configurations which will be often modified by users, I don't want to mix it with other files.

Why would a .h file ever need to be "modified by users"? That sounds wrong to me.

Quoted 1 line

Should I combine rest of the headers into a single file ?

Yes, why would they need to be separate?

Quoted 2 lines

and keep the other three .c files under "drivers/tty/serial/" and remove the da8xx_pruss directory altogether.

Yes.

thanks,

greg k-h

#74 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

In reply to Arnd Bergmann

I agree with the Codingstyle which needs to be fixed and would appreciate more feedback on it.

I have also gotten rid of the semaphore completely, please let me know what you feel of this implementation:
I have tested this without any problem.
What I am basically doing below is that, I am getting the data from the circ buff and then using the interrupt handler to pump out the data. As the circ buff empties, I am accepting another request from the TTY.
Are you noticing any problems with this:

drivers/tty/serial/da8xx_pruss/pruss_suart.c +0 -0
index d222e2e..edc3863 100644
@@ -72,7 +72,7 @@ struct suart_fifo {
struct omapl_pru_suart {
       struct uart_port port[NR_SUART];
       struct device *dev; /* pdev->dev */
-       struct semaphore port_sem[NR_SUART];
+       bool tx_empty[NR_SUART];
       struct clk *clk_mcasp;
       struct suart_fifo suart_fifo_addr[NR_SUART];
       const struct firmware *fw;

@@ -122,13 +122,10 @@ static void omapl_pru_tx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no)
if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX))
return;

- if (down_trylock(&soft_uart->port_sem[uart_no]))
- return;
-

       if (uart_circ_empty(xmit) ||
                       uart_tx_stopped(&soft_uart->port[uart_no])) {
               pruss_suart_stop_tx(&soft_uart->port[uart_no]);
-               up(&soft_uart->port_sem[uart_no]);
+               soft_uart->tx_empty[uart_no] = true;
               return;
       }

@@ -259,7 +256,6 @@ static irqreturn_t pruss_suart_interrupt(s32 irq, void *dev_id)

                       pru_intr_clr_isrstatus(dev, uart_num, PRU_TX_INTR);
                       pru_softuart_clr_tx_status(dev, 
&soft_uart->suart_hdl
                                                [port->line]);
-                       up(&soft_uart->port_sem[port->line]);
                       omapl_pru_tx_chars(soft_uart, port->line);
               }
       } while (txrx_flag & (PRU_RX_INTR | PRU_TX_INTR));

@@ -294,7 +290,10 @@ static void pruss_suart_start_tx(struct uart_port *port)

       suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num,
                          PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT);

- omapl_pru_tx_chars(soft_uart, port->line);
+ if (soft_uart->tx_empty[port->line] == true) {
+ soft_uart->tx_empty[port->line] = false;
+ omapl_pru_tx_chars(soft_uart, port->line);
+ }
}

--------------------------------------------------
From: "Arnd Bergmann" <[email protected]>
Sent: Saturday, February 19, 2011 12:21 AM
To: <[email protected]>
Cc: "Thomas Gleixner" <[email protected]>; "Alan Cox" <[email protected]>; <[email protected]>; <[email protected]>; "Subhasish Ghosh" <[email protected]>; "Greg Kroah-Hartman" <[email protected]>; <[email protected]>; "open list" <[email protected]>; <[email protected]>
Subject: Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 32 lines · On Friday 18 February 2011 19:23:49 Thomas Gleixner wrote:
Quoted 16 lines · Alan Cox
Quoted 6 lines
Quoted 2 lines · On Fri, 18 Feb 2011 19:17:38 +0530 "Subhasish Ghosh"…

Hello,

Quoted 8 lines
Quoted 4 lines

Regarding the semaphore to mutex migration.
We are using down_trylock in interrupt context,
mutex_trylock cannot be used in interrupt context, so we cannot use mutex in our driver.

Then you probably need to rework your locking. Best bet might be to fix all the other stuff and report the driver, and people can think about the locking problem.

Quoted 5 lines

That semaphore is utterly useless to begin with. There are more serious locking problems than this one. Non serialized calls to suart_intr_clrmask/suart_intr_setmask are the most obvious ones.

Aside of that the code is complete unreadable.

I think it mostly suffers from the same problem as the CAN driver I commented on earlier: One of the files (pruss_suart_api.c) was clearly not written with Linux as the target, and the other files try to work around this by wrapping a Linux driver around it.

The suart_api HAL stuff clearly needs to go away, so that the rest can be rewritten into a proper device driver.

Arnd

#75 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 26 lines · Subhasish Ghosh
(quoted diff) +1 -5
@@ -122,13 +122,10 @@ static void omapl_pru_tx_chars(struct omapl_pru_suart 
 *soft_uart, u32 uart_no)
        if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX))
                return;
 
-       if (down_trylock(&soft_uart->port_sem[uart_no]))
-               return;
-
        if (uart_circ_empty(xmit) ||
                        uart_tx_stopped(&soft_uart->port[uart_no])) {
                pruss_suart_stop_tx(&soft_uart->port[uart_no]);
-               up(&soft_uart->port_sem[uart_no]);
+               soft_uart->tx_empty[uart_no] = true;
                return;
        }
@@ -259,7 +256,6 @@ static irqreturn_t pruss_suart_interrupt(s32 irq, void 
 *dev_id)
                        pru_intr_clr_isrstatus(dev, uart_num, PRU_TX_INTR);
                        pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl
                                                 [port->line]);
-                       up(&soft_uart->port_sem[port->line]);
                        omapl_pru_tx_chars(soft_uart, port->line);
                }
        } while (txrx_flag & (PRU_RX_INTR | PRU_TX_INTR));

Review static void pruss_suart_start_tx(struct uart_port · line 298

Quoted 10 lines
(quoted diff) +4 -1
@@ -294,7 +290,10 @@ static void pruss_suart_start_tx(struct uart_port 
 *port)
 
        suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num,
                           PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT);
-       omapl_pru_tx_chars(soft_uart, port->line);
+       if (soft_uart->tx_empty[port->line] == true) {
+               soft_uart->tx_empty[port->line] = false;
+               omapl_pru_tx_chars(soft_uart, port->line);
+       }
 }

This looks racy, and I think you at least need to take the spinlock in pruss_suart_start_tx(), but I don't fully understand the intention of the code.

I guess you could also use a bitmask for tx_empty and use test_and_clear_bit() on that to guarantee atomicity.

Arnd

#76 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Hello,

Ok, have implemented the test_and_clear_bit.

Quoted 52 lines · Subhasish Ghosh
Quoted 32 lines
(quoted diff) +0 -0
@@ -122,13 +122,10 @@ static void omapl_pru_tx_chars(struct 

omapl_pru_suart
*soft_uart, u32 uart_no)
if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX))
return;

(quoted diff) +1 -4
-       if (down_trylock(&soft_uart->port_sem[uart_no]))
-               return;
-
        if (uart_circ_empty(xmit) ||
                        uart_tx_stopped(&soft_uart->port[uart_no])) {
                pruss_suart_stop_tx(&soft_uart->port[uart_no]);
-               up(&soft_uart->port_sem[uart_no]);
+               soft_uart->tx_empty[uart_no] = true;
                return;
        }
@@ -259,7 +256,6 @@ static irqreturn_t pruss_suart_interrupt(s32 irq, 

void
*dev_id)
pru_intr_clr_isrstatus(dev, uart_num,
PRU_TX_INTR);
pru_softuart_clr_tx_status(dev,
&soft_uart->suart_hdl

                                                 [port->line]);
(quoted diff) +0 -1
-                       up(&soft_uart->port_sem[port->line]);
                        omapl_pru_tx_chars(soft_uart, port->line);
                }
        } while (txrx_flag & (PRU_RX_INTR | PRU_TX_INTR));
@@ -294,7 +290,10 @@ static void pruss_suart_start_tx(struct uart_port
 *port)

Review static void pruss_suart_start_tx(struct uart_port · line 299

Quoted 10 lines
(quoted diff) +4 -1
 
        suart_intr_setmask(dev, 
 soft_uart->suart_hdl[port->line].uart_num,
                           PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT);
-       omapl_pru_tx_chars(soft_uart, port->line);
+       if (soft_uart->tx_empty[port->line] == true) {
+               soft_uart->tx_empty[port->line] = false;
+               omapl_pru_tx_chars(soft_uart, port->line);
+       }
 }

This looks racy, and I think you at least need to take the spinlock in pruss_suart_start_tx(), but I don't fully understand the intention of the code.

I guess you could also use a bitmask for tx_empty and use test_and_clear_bit() on that to guarantee atomicity.

Arnd

author
#77 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

In reply to Alan Cox

--------------------------------------------------
From: "Alan Cox" <[email protected]>
Sent: Friday, February 11, 2011 9:58 PM
To: "Subhasish Ghosh" <[email protected]>
Cc: <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "Greg Kroah-Hartman" <[email protected]>; "open list" <[email protected]>
Subject: Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 1 line

Don't see why it needs its own sub-directory

SG - We have two different layers of implementation. One is the driver layer, which interacts with the TTY sub-system and the other is the API layer,
which interacts with the PRUSS. To maintain this (also explained in the other mail),
we used separate files and hence we decided to keep the code in a separate directory so that the related files can be identified easily.

Quoted 14 lines
Quoted 11 lines
(quoted diff) +8 -0
+#ifdef __SUART_DEBUG
+#define __suart_debug(fmt, args...) \
+ printk(KERN_DEBUG "suart_debug: " fmt, ## args)
+#else
+#define __suart_debug(fmt, args...)
+#endif
+
+#define  __suart_err(fmt, args...) printk(KERN_ERR "suart_err: " fmt, ##
Quoted 1 line

args)

Use dev_dbg/dev_err/pr_debug/pr_err

SG - did you mean replace the printks above with dev_dgb/err or the suart_dbg/err.

Quoted 17 lines
Quoted 4 lines

+static void omapl_pru_tx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no)

Review

Quoted 10 lines
(quoted diff) +10 -0
+{
+ struct circ_buf *xmit = &soft_uart->port[uart_no].state->xmit;
+ struct device *dev = soft_uart->dev;
+ s32 count = 0;
+
+ if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX))
+ return;
+
+ if (down_trylock(&soft_uart->port_sem[uart_no]))
+ return;

Please use a mutex not a semaphore. We are trying to get almost all the kernel using mutexes as it is needed for hard real time.

SG - Have removed, need your feedback.

Quoted 16 lines
Quoted 5 lines

+ pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no],

(quoted diff) +2 -0
+        suart_data, data_len + 1,
+        &data_len_read);

Review

Quoted 10 lines
(quoted diff) +10 -0
+
+ for (i = 0; i <= data_len_read; i++) {
+ soft_uart->port[uart_no].icount.rx++;
+ /* check for sys rq */
+ if (uart_handle_sysrq_char
+     (&soft_uart->port[uart_no], suart_data))
+ continue;
+ }
+ /* update the tty data structure */
+ tty_insert_flip_string(tty, suart_data, data_len_read);
Quoted 1 line

You can avoid a copy here by using tty_prepare_flip_string()

SG - Ok, Thus this look ok ?
==================================================================================

drivers/tty/serial/da8xx_pruss/pruss_suart.c +0 -0
@@ -170,8 +170,9 @@ static void omapl_pru_rx_chars(struct omapl_pru_suart 

*soft_uart, u32 uart_no)

        s8 flags = TTY_NORMAL;
        u16 rx_status, data_len = SUART_FIFO_LEN;
        u32 data_len_read;
-       u8 suart_data[SUART_FIFO_LEN + 1];
+       u8 *suart_data = NULL;
        s32 i = 0;

+ s32 alloc_len = 0;

if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_RX))
return;
@@ -199,9 +200,10 @@ static void omapl_pru_rx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no)

                soft_uart->port[uart_no].sysrq = 0;
 #endif
        } else {
+               alloc_len = tty_prepare_flip_string(tty, &suart_data, 
data_len + 1);
                pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no],

- suart_data, data_len + 1,
- &data_len_read);
+ suart_data, alloc_len,
+ &data_len_read);

                for (i = 0; i <= data_len_read; i++) {
                        soft_uart->port[uart_no].icount.rx++;
@@ -210,8 +212,6 @@ static void omapl_pru_rx_chars(struct omapl_pru_suart 
*soft_uart, u32 uart_no)
                            (&soft_uart->port[uart_no], suart_data))
                                continue;
                }
-               /* update the tty data structure */
-               tty_insert_flip_string(tty, suart_data, data_len_read);
        }

============================================================================================

Quoted 2 lines

Also please use the tty_port_tty_get/tty_kref_put interfaces so the tty refcounting is right

SG - Ok, Will do.

Quoted 17 lines
Quoted 17 lines
(quoted diff) +15 -0
+static void pruss_suart_set_termios(struct uart_port *port,
+   struct ktermios *termios,
+   struct ktermios *old)
+{
+ struct omapl_pru_suart *soft_uart =
+     container_of(port, struct omapl_pru_suart, port[port->line]);
+ struct device *dev = soft_uart->dev;
+ u8 cval = 0;
+ unsigned long flags = 0;
+ u32 baud = 0;
+ u32 old_csize = old ? old->c_cflag & CSIZE : CS8;
+
+/*
+ * Do not allow unsupported configurations to be set
+ */
Quoted 8 lines
Quoted 4 lines

+ if (1) {
+ termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR | CSTOPB
+ | PARENB | PARODD | CMSPAR);
+ termios->c_cflag |= CLOCAL;

No. CLOCAL and HUPCL are user side flags. Apps expect to able to set them even if in fact they have no effect, so leave those two alone. The rest is fine.

SG -Ok, will do.

Quoted 7 lines
Quoted 4 lines

+/*
+ * Characteres to ignore

Typo

SG - ok.

Quoted 29 lines
Quoted 29 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +5 -0
new file mode 100644
index 0000000..d809dd3
@@ -0,0 +1,2350 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Jitendra Kumar <[email protected]>
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +5 -0
+
+#include <linux/types.h>
+#include <linux/mfd/pruss/da8xx_pru.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
Quoted 8 lines

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c

Quoted 7 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +7 -0
+#include "pruss_suart_api.h"
+#include "pruss_suart_regs.h"
+#include "pruss_suart_board.h"
+#include "pruss_suart_utils.h"
+#include "pruss_suart_err.h"
+
+static u8 g_uart_statu_table[8];

Can you lose the g_, its a windows naming convention we dont use

SG -- Ok, I can also see the Hungarian style like u32Offset, will get rid of these as well.
Would really appreciate if you may please point me out more such
problems.

Quoted 7 lines

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c

Quoted 4 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +2 -0
+s16 pru_softuart_open(suart_handle h_suart)
+{

If you just used normal integers you could surely make this routine trivial and remove all the duplication in the switches

SG -- Ok, will do.

Quoted 4 lines
Quoted 2 lines

+ s16 status = PRU_SUART_SUCCESS;

And please stick to Linux error codes

SG - Ok, Will do

Quoted 13 lines

drivers/tty/serial/da8xx_pruss/pruss_suart_api.c

Quoted 9 lines
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file +7 -0
+/* suart instance close routine */
+s16 pru_softuart_close(suart_handle h_uart)
+{
+ s16 status = SUART_SUCCESS;
+
+ if (h_uart == NULL) {
+ return PRU_SUART_ERR_HANDLE_INVALID;

Which is never checked. Far better to use WARN_ON and the like for such cases - or if like this one they don't appear to be possible to simply delete them

SG -- OK, does this look ok ?
=================================
if (h_uart == NULL) {
+WARN_ON(1);
- return PRU_SUART_ERR_HANDLE_INVALID;
+return -EINVAL;
}
=================================

Quoted 7 lines
Quoted 5 lines

+ if ((tx_clk_divisor > 385) || (tx_clk_divisor == 0))
+ return SUART_INVALID_CLKDIVISOR;
+ if ((rx_clk_divisor > 385) || (rx_clk_divisor == 0))
+ return SUART_INVALID_CLKDIVISOR;

[minor] Lots of excess brackets

Ok - Will do.

Quoted 13 lines
Quoted 7 lines

+ u32offset = (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF);
+ u32value = PRU_INTC_CHAN_34_SYSEVT_36_39;
+ s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1);
+ if (-1 == s16retval)
+ return -1;

If you fixed the API here you'd be able to just write

if (pruss_writel(dev, PRUSS_INTC_CHANMAP9 & 0xFFFF, PRU_INTC_BLAH)

Quoted 7 lines

or similar which would clean up a lot of messy code and shrink it dramatically. Given you have lots of series of writes some kind of

pruss_writel_multi(dev, array, len)

that took an array of addr/value pairs might also clean up a ton of code and turn it into trivial tables

SG -- Will shrink this function.

Quoted 1 line

Please do not print this email unless it is absolutely necessary. Spread environmental awareness.

-------------------------------------------------------DISCLAIMER------------------------------------------------------
The information transmitted herewith is confidential and proprietary information intended only for use by the individual or entity to which it is addressed. If the reader of this message is not the intended recipient, you are hereby notified that any review, retransmission, dissemination, distribution, copying or other use of, or taking of any action in reliance upon this information is strictly prohibited. If you have received this communication in error, please contact the sender and delete the material from your computer.
---------------------------------------------------------------------------------------------------------------------------

#78 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 2 lines

we used separate files and hence we decided to keep the code in a separate directory so that the related files can be identified easily.

Fair enough but I would have thought you could drop the two files in the serial directory if they have obviously related names- trivial item/

Quoted 16 lines
Quoted 4 lines
Quoted 11 lines
Quoted 9 lines
(quoted diff) +8 -0
+#ifdef __SUART_DEBUG
+#define __suart_debug(fmt, args...) \
+ printk(KERN_DEBUG "suart_debug: " fmt, ## args)
+#else
+#define __suart_debug(fmt, args...)
+#endif
+
+#define  __suart_err(fmt, args...) printk(KERN_ERR "suart_err: " fmt, ##

args)

Use dev_dbg/dev_err/pr_debug/pr_err

Quoted 2 lines

SG - did you mean replace the printks above with dev_dgb/err or the suart_dbg/err.

Ideally all the messages shopuld use dev_dbg/dev_err etc. That allows you to configure debug levels and the like nicely as well as producing clearer printk info. In some cases with tty code you may not know the device so have to use pr_err/pr_debug etc.

Ok

Quoted 11 lines
Quoted 3 lines

Which is never checked. Far better to use WARN_ON and the like for such cases - or if like this one they don't appear to be possible to simply delete them

SG -- OK, does this look ok ?
=================================
if (h_uart == NULL) {

(quoted diff) +2 -1
+WARN_ON(1);
- return PRU_SUART_ERR_HANDLE_INVALID;
+return -EINVAL;

}

Yep - the user will now get a backtrace, and in addition kerneloops.org can capture it if that is set up in the distro in use.

Alan

#79 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Hello,

I tried using the tty_prepare_flip_string as shown below, but this is causing some latency issues.

Problem is, we do not have any flow control, so we must copy the FIFO data before the next data is available. As we are using the tty_prepare_flip_string just before the read API, the FIFO is getting overwritten and we are ending up missing chunks (FIFO sized) of data.

I tried using a tasklet for the TX part, but that did not help.
Another way is to prepare the buffer for the next read and read the data immediately. Something like this:

1. Call tty_prepare_flip_string while startup.
2. When the read interrupt arrives, read the data immediately.
3. Call tty_prepare_flip_string for the next read.

Again, the problem here is that we need to use global variables to store the pre-allocated buffers and at the last read, we allocated the buffer but never used it. I think this will cause a memory leak. Or we can de-allocate it while driver close, not sure how to.

The best way is if we can keep the current implementation, one extra copy is not hurting us as we do it after the read_data API.

==================================================================================

drivers/tty/serial/da8xx_pruss/pruss_suart.c +0 -0
@@ -170,8 +170,9 @@ static void omapl_pru_rx_chars(struct omapl_pru_suart

*soft_uart, u32 uart_no)

       s8 flags = TTY_NORMAL;
       u16 rx_status, data_len = SUART_FIFO_LEN;
       u32 data_len_read;
-       u8 suart_data[SUART_FIFO_LEN + 1];
+       u8 *suart_data = NULL;
       s32 i = 0;

+ s32 alloc_len = 0;

if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_RX))
return;
@@ -199,9 +200,10 @@ static void omapl_pru_rx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no)

               soft_uart->port[uart_no].sysrq = 0;
#endif
       } else {
+               alloc_len = tty_prepare_flip_string(tty, &suart_data,
data_len + 1);
               pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no],

- suart_data, data_len + 1,
- &data_len_read);
+ suart_data, alloc_len,
+ &data_len_read);

               for (i = 0; i <= data_len_read; i++) {
                       soft_uart->port[uart_no].icount.rx++;
@@ -210,8 +212,6 @@ static void omapl_pru_rx_chars(struct omapl_pru_suart
*soft_uart, u32 uart_no)
                           (&soft_uart->port[uart_no], suart_data))
                               continue;
               }
-               /* update the tty data structure */
-               tty_insert_flip_string(tty, suart_data, data_len_read);
       }

============================================================================================

--------------------------------------------------
From: "Alan Cox" <[email protected]>
Sent: Tuesday, February 22, 2011 4:41 PM
To: "Subhasish" <[email protected]>
Cc: <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>; "Greg Kroah-Hartman" <[email protected]>; "open list" <[email protected]>; "Stalin Srinivasan" <[email protected]>
Subject: Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 48 lines
Quoted 2 lines

we used separate files and hence we decided to keep the code in a separate directory so that the related files can be identified easily.

Fair enough but I would have thought you could drop the two files in the serial directory if they have obviously related names- trivial item/

Quoted 17 lines
Quoted 4 lines
Quoted 12 lines
Quoted 10 lines
(quoted diff) +8 -0
+#ifdef __SUART_DEBUG
+#define __suart_debug(fmt, args...) \
+ printk(KERN_DEBUG "suart_debug: " fmt, ## args)
+#else
+#define __suart_debug(fmt, args...)
+#endif
+
+#define  __suart_err(fmt, args...) printk(KERN_ERR "suart_err: " fmt, 

##
args)

Use dev_dbg/dev_err/pr_debug/pr_err

Quoted 2 lines

SG - did you mean replace the printks above with dev_dgb/err or the suart_dbg/err.

Ideally all the messages shopuld use dev_dbg/dev_err etc. That allows you to configure debug levels and the like nicely as well as producing clearer printk info. In some cases with tty code you may not know the device so have to use pr_err/pr_debug etc.

Ok

Quoted 11 lines
Quoted 3 lines

Which is never checked. Far better to use WARN_ON and the like for such cases - or if like this one they don't appear to be possible to simply delete them

SG -- OK, does this look ok ?
=================================
if (h_uart == NULL) {

(quoted diff) +2 -1
+WARN_ON(1);
- return PRU_SUART_ERR_HANDLE_INVALID;
+return -EINVAL;

}

Yep - the user will now get a backtrace, and in addition kerneloops.org can capture it if that is set up in the distro in use.

Alan

#80 lore raw

Re: [PATCH v2 13/13] tty: pruss SUART driver

Quoted 8 lines

I tried using a tasklet for the TX part, but that did not help. Another way is to prepare the buffer for the next read and read the data immediately.
Something like this:

1. Call tty_prepare_flip_string while startup.
2. When the read interrupt arrives, read the data immediately.
3. Call tty_prepare_flip_string for the next read.

Only you then don't know the size of space required/

Quoted 2 lines

Again, the problem here is that we need to use global variables to store the pre-allocated buffers

You don't. You can store them per port in the existing objects !

Quoted 3 lines

The best way is if we can keep the current implementation, one extra copy is not hurting us
as we do it after the read_data API.

No problem with that at all.