From: AnilKumar Ch <anilkumar@ti.com>
To: wg@grandegger.com, mkl@pengutronix.de, linux-can@vger.kernel.org
Cc: anantgole@ti.com, nsekhar@ti.com, AnilKumar Ch <anilkumar@ti.com>
Subject: [PATCH 3/3] can: c_can: Add support for Bosch D_CAN controller
Date: Fri, 20 Apr 2012 15:28:22 +0530 [thread overview]
Message-ID: <1334915902-30253-1-git-send-email-anilkumar@ti.com> (raw)
This patch adds the support for D_CAN controller driver to the existing
C_CAN driver.
Bosch D_CAN controller is a full-CAN implementation which is compliant
to CAN protocol version 2.0 part A and B. Bosch D_CAN user manual can be
obtained from: http://www.semiconductors.bosch.de/media/en/pdf/
ipmodules_1/can/d_can_users_manual_111.pdf
The following are the design choices made while adding D_CAN to C_CAN
driver:
A new overlay structure is added for d_can controller and care is taken
to make sure its member names match with equavalent c_can structure
members (even if the d_can specification calls them slightly differently).
Note that d_can controller has more registers, so structure members of
d_can are more than those in c_can.
A new set if read/write macros are used to access the registers common
between c_can and d_can. To get the basic d_can functionality working
it is sufficient to access just these registers.
Current D_CAN implementation has following limitations, this is done
to avoid large changes to the C_CAN driver.
1. Message objects are limited to 32, 16 for RX and 16 for TX. C_CAN IP
supports upto 32 message objects but in case of D_CAN we can configure
upto 128 message objects.
2. Using two 16bit reads/writes for accessing the 32bit D_CAN registers.
3. These patches have been tested on little endian machine, there might
be some hidden endian-related issues due to the nature of the accesses
(32-bit registers accessed as 2 16-bit registers). However, I do not
have a big-endian D_CAN implementation to confirm.
Signed-off-by: AnilKumar Ch <anilkumar@ti.com>
---
drivers/net/can/c_can/c_can.c | 139 ++++++++++++++++----------------
drivers/net/can/c_can/c_can.h | 93 +++++++++++++++++++++-
drivers/net/can/c_can/c_can_platform.c | 47 +++++++++--
include/linux/can/platform/c_can.h | 42 ++++++++++
4 files changed, 245 insertions(+), 76 deletions(-)
create mode 100644 include/linux/can/platform/c_can.h
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c
index 09fcc50..4abb09d 100644
--- a/drivers/net/can/c_can/c_can.c
+++ b/drivers/net/can/c_can/c_can.c
@@ -38,6 +38,7 @@
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
+#include <linux/can/platform/c_can.h>
#include "c_can.h"
@@ -219,24 +220,24 @@ static u32 c_can_read_reg32(struct c_can_priv *priv, void *reg)
static void c_can_enable_all_interrupts(struct c_can_priv *priv,
int enable)
{
- unsigned int cntrl_save = priv->read_reg(priv,
- &priv->regs->control);
+ unsigned int cntrl_save;
+ C_CAN_READ(cntrl_save, priv, regs->control);
if (enable)
cntrl_save |= (CONTROL_SIE | CONTROL_EIE | CONTROL_IE);
else
cntrl_save &= ~(CONTROL_EIE | CONTROL_IE | CONTROL_SIE);
- priv->write_reg(priv, &priv->regs->control, cntrl_save);
+ C_CAN_WRITE(priv, regs->control, cntrl_save);
}
static inline int c_can_msg_obj_is_busy(struct c_can_priv *priv, int iface)
{
int count = MIN_TIMEOUT_VALUE;
+ u32 com_req;
- while (count && priv->read_reg(priv,
- &priv->regs->ifregs[iface].com_req) &
- IF_COMR_BUSY) {
+ C_CAN_READ(com_req, priv, regs->ifregs[iface].com_req);
+ while (count && com_req & IF_COMR_BUSY) {
count--;
udelay(1);
}
@@ -258,9 +259,9 @@ static inline void c_can_object_get(struct net_device *dev,
* register and message RAM must be complete in 6 CAN-CLK
* period.
*/
- priv->write_reg(priv, &priv->regs->ifregs[iface].com_mask,
+ C_CAN_WRITE(priv, regs->ifregs[iface].com_mask,
IFX_WRITE_LOW_16BIT(mask));
- priv->write_reg(priv, &priv->regs->ifregs[iface].com_req,
+ C_CAN_WRITE(priv, regs->ifregs[iface].com_req,
IFX_WRITE_LOW_16BIT(objno));
if (c_can_msg_obj_is_busy(priv, iface))
@@ -278,9 +279,9 @@ static inline void c_can_object_put(struct net_device *dev,
* register and message RAM must be complete in 6 CAN-CLK
* period.
*/
- priv->write_reg(priv, &priv->regs->ifregs[iface].com_mask,
+ C_CAN_WRITE(priv, regs->ifregs[iface].com_mask,
(IF_COMM_WR | IFX_WRITE_LOW_16BIT(mask)));
- priv->write_reg(priv, &priv->regs->ifregs[iface].com_req,
+ C_CAN_WRITE(priv, regs->ifregs[iface].com_req,
IFX_WRITE_LOW_16BIT(objno));
if (c_can_msg_obj_is_busy(priv, iface))
@@ -306,18 +307,18 @@ static void c_can_write_msg_object(struct net_device *dev,
flags |= IF_ARB_MSGVAL;
- priv->write_reg(priv, &priv->regs->ifregs[iface].arb1,
+ C_CAN_WRITE(priv, regs->ifregs[iface].arb1,
IFX_WRITE_LOW_16BIT(id));
- priv->write_reg(priv, &priv->regs->ifregs[iface].arb2, flags |
+ C_CAN_WRITE(priv, regs->ifregs[iface].arb2, flags |
IFX_WRITE_HIGH_16BIT(id));
for (i = 0; i < frame->can_dlc; i += 2) {
- priv->write_reg(priv, &priv->regs->ifregs[iface].data[i / 2],
+ C_CAN_WRITE(priv, regs->ifregs[iface].data[i / 2],
frame->data[i] | (frame->data[i + 1] << 8));
}
/* enable interrupt for this message object */
- priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl,
+ C_CAN_WRITE(priv, regs->ifregs[iface].msg_cntrl,
IF_MCONT_TXIE | IF_MCONT_TXRQST | IF_MCONT_EOB |
frame->can_dlc);
c_can_object_put(dev, iface, objno, IF_COMM_ALL);
@@ -329,7 +330,7 @@ static inline void c_can_mark_rx_msg_obj(struct net_device *dev,
{
struct c_can_priv *priv = netdev_priv(dev);
- priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl,
+ C_CAN_WRITE(priv, regs->ifregs[iface].msg_cntrl,
ctrl_mask & ~(IF_MCONT_MSGLST | IF_MCONT_INTPND));
c_can_object_put(dev, iface, obj, IF_COMM_CONTROL);
@@ -343,7 +344,7 @@ static inline void c_can_activate_all_lower_rx_msg_obj(struct net_device *dev,
struct c_can_priv *priv = netdev_priv(dev);
for (i = C_CAN_MSG_OBJ_RX_FIRST; i <= C_CAN_MSG_RX_LOW_LAST; i++) {
- priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl,
+ C_CAN_WRITE(priv, regs->ifregs[iface].msg_cntrl,
ctrl_mask & ~(IF_MCONT_MSGLST |
IF_MCONT_INTPND | IF_MCONT_NEWDAT));
c_can_object_put(dev, iface, i, IF_COMM_CONTROL);
@@ -356,7 +357,7 @@ static inline void c_can_activate_rx_msg_obj(struct net_device *dev,
{
struct c_can_priv *priv = netdev_priv(dev);
- priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl,
+ C_CAN_WRITE(priv, regs->ifregs[iface].msg_cntrl,
ctrl_mask & ~(IF_MCONT_MSGLST |
IF_MCONT_INTPND | IF_MCONT_NEWDAT));
c_can_object_put(dev, iface, obj, IF_COMM_CONTROL);
@@ -374,7 +375,7 @@ static void c_can_handle_lost_msg_obj(struct net_device *dev,
c_can_object_get(dev, iface, objno, IF_COMM_ALL & ~IF_COMM_TXRQST);
- priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl,
+ C_CAN_WRITE(priv, regs->ifregs[iface].msg_cntrl,
IF_MCONT_CLR_MSGLST);
c_can_object_put(dev, 0, objno, IF_COMM_CONTROL);
@@ -410,9 +411,9 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, int ctrl)
frame->can_dlc = get_can_dlc(ctrl & 0x0F);
- flags = priv->read_reg(priv, &priv->regs->ifregs[iface].arb2);
- val = priv->read_reg(priv, &priv->regs->ifregs[iface].arb1) |
- (flags << 16);
+ C_CAN_READ(flags, priv, regs->ifregs[iface].arb2);
+ C_CAN_READ(val, priv, regs->ifregs[iface].arb1);
+ val |= (flags << 16);
if (flags & IF_ARB_MSGXTD)
frame->can_id = (val & CAN_EFF_MASK) | CAN_EFF_FLAG;
@@ -423,8 +424,8 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, int ctrl)
frame->can_id |= CAN_RTR_FLAG;
else {
for (i = 0; i < frame->can_dlc; i += 2) {
- data = priv->read_reg(priv,
- &priv->regs->ifregs[iface].data[i / 2]);
+ C_CAN_READ(data, priv,
+ regs->ifregs[iface].data[i / 2]);
frame->data[i] = data;
frame->data[i + 1] = data >> 8;
}
@@ -443,41 +444,45 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface,
unsigned int id, unsigned int mcont)
{
struct c_can_priv *priv = netdev_priv(dev);
+ u32 msgval1;
- priv->write_reg(priv, &priv->regs->ifregs[iface].mask1,
+ C_CAN_WRITE(priv, regs->ifregs[iface].mask1,
IFX_WRITE_LOW_16BIT(mask));
- priv->write_reg(priv, &priv->regs->ifregs[iface].mask2,
+ C_CAN_WRITE(priv, regs->ifregs[iface].mask2,
IFX_WRITE_HIGH_16BIT(mask));
- priv->write_reg(priv, &priv->regs->ifregs[iface].arb1,
+ C_CAN_WRITE(priv, regs->ifregs[iface].arb1,
IFX_WRITE_LOW_16BIT(id));
- priv->write_reg(priv, &priv->regs->ifregs[iface].arb2,
+ C_CAN_WRITE(priv, regs->ifregs[iface].arb2,
(IF_ARB_MSGVAL | IFX_WRITE_HIGH_16BIT(id)));
- priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, mcont);
+ C_CAN_WRITE(priv, regs->ifregs[iface].msg_cntrl, mcont);
c_can_object_put(dev, iface, objno, IF_COMM_ALL & ~IF_COMM_TXRQST);
- netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno,
- c_can_read_reg32(priv, &priv->regs->msgval1));
+ C_CAN_READ_REG32(msgval1, priv, regs->msgval1);
+ netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, msgval1);
}
static void c_can_inval_msg_object(struct net_device *dev, int iface, int objno)
{
struct c_can_priv *priv = netdev_priv(dev);
+ u32 msgval1;
- priv->write_reg(priv, &priv->regs->ifregs[iface].arb1, 0);
- priv->write_reg(priv, &priv->regs->ifregs[iface].arb2, 0);
- priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, 0);
+ C_CAN_WRITE(priv, regs->ifregs[iface].arb1, 0);
+ C_CAN_WRITE(priv, regs->ifregs[iface].arb2, 0);
+ C_CAN_WRITE(priv, regs->ifregs[iface].msg_cntrl, 0);
c_can_object_put(dev, iface, objno, IF_COMM_ARB | IF_COMM_CONTROL);
+ C_CAN_READ_REG32(msgval1, priv, regs->msgval1);
- netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno,
- c_can_read_reg32(priv, &priv->regs->msgval1));
+ netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, msgval1);
}
static inline int c_can_is_next_tx_obj_busy(struct c_can_priv *priv, int objno)
{
- int val = c_can_read_reg32(priv, &priv->regs->txrqst1);
+ int val;
+
+ C_CAN_READ_REG32(val, priv, regs->txrqst1);
/*
* as transmission request register's bit n-1 corresponds to
@@ -540,12 +545,12 @@ static int c_can_set_bittiming(struct net_device *dev)
netdev_info(dev,
"setting BTR=%04x BRPE=%04x\n", reg_btr, reg_brpe);
- ctrl_save = priv->read_reg(priv, &priv->regs->control);
- priv->write_reg(priv, &priv->regs->control,
+ C_CAN_READ(ctrl_save, priv, regs->control);
+ C_CAN_WRITE(priv, regs->control,
ctrl_save | CONTROL_CCE | CONTROL_INIT);
- priv->write_reg(priv, &priv->regs->btr, reg_btr);
- priv->write_reg(priv, &priv->regs->brp_ext, reg_brpe);
- priv->write_reg(priv, &priv->regs->control, ctrl_save);
+ C_CAN_WRITE(priv, regs->btr, reg_btr);
+ C_CAN_WRITE(priv, regs->brp_ext, reg_brpe);
+ C_CAN_WRITE(priv, regs->control, ctrl_save);
return 0;
}
@@ -587,36 +592,35 @@ static void c_can_chip_config(struct net_device *dev)
struct c_can_priv *priv = netdev_priv(dev);
/* enable automatic retransmission */
- priv->write_reg(priv, &priv->regs->control,
- CONTROL_ENABLE_AR);
+ C_CAN_WRITE(priv, regs->control, CONTROL_ENABLE_AR);
if (priv->can.ctrlmode & (CAN_CTRLMODE_LISTENONLY &
CAN_CTRLMODE_LOOPBACK)) {
/* loopback + silent mode : useful for hot self-test */
- priv->write_reg(priv, &priv->regs->control, CONTROL_EIE |
+ C_CAN_WRITE(priv, regs->control, CONTROL_EIE |
CONTROL_SIE | CONTROL_IE | CONTROL_TEST);
- priv->write_reg(priv, &priv->regs->test,
+ C_CAN_WRITE(priv, regs->test,
TEST_LBACK | TEST_SILENT);
} else if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
/* loopback mode : useful for self-test function */
- priv->write_reg(priv, &priv->regs->control, CONTROL_EIE |
+ C_CAN_WRITE(priv, regs->control, CONTROL_EIE |
CONTROL_SIE | CONTROL_IE | CONTROL_TEST);
- priv->write_reg(priv, &priv->regs->test, TEST_LBACK);
+ C_CAN_WRITE(priv, regs->test, TEST_LBACK);
} else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
/* silent mode : bus-monitoring mode */
- priv->write_reg(priv, &priv->regs->control, CONTROL_EIE |
+ C_CAN_WRITE(priv, regs->control, CONTROL_EIE |
CONTROL_SIE | CONTROL_IE | CONTROL_TEST);
- priv->write_reg(priv, &priv->regs->test, TEST_SILENT);
+ C_CAN_WRITE(priv, regs->test, TEST_SILENT);
} else
/* normal mode*/
- priv->write_reg(priv, &priv->regs->control,
+ C_CAN_WRITE(priv, regs->control,
CONTROL_EIE | CONTROL_SIE | CONTROL_IE);
/* configure message objects */
c_can_configure_msg_objects(dev);
/* set a `lec` value so that we can check for updates later */
- priv->write_reg(priv, &priv->regs->status, LEC_UNUSED);
+ C_CAN_WRITE(priv, regs->status, LEC_UNUSED);
/* set bittiming params */
c_can_set_bittiming(dev);
@@ -669,7 +673,7 @@ static int c_can_get_berr_counter(const struct net_device *dev,
unsigned int reg_err_counter;
struct c_can_priv *priv = netdev_priv(dev);
- reg_err_counter = priv->read_reg(priv, &priv->regs->err_cnt);
+ C_CAN_READ(reg_err_counter, priv, regs->err_cnt);
bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >>
ERR_CNT_REC_SHIFT;
bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK;
@@ -690,20 +694,19 @@ static int c_can_get_berr_counter(const struct net_device *dev,
*/
static void c_can_do_tx(struct net_device *dev)
{
- u32 val;
+ u32 val, msg_cntrl;
u32 msg_obj_no;
struct c_can_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
for (/* nix */; (priv->tx_next - priv->tx_echo) > 0; priv->tx_echo++) {
msg_obj_no = get_tx_echo_msg_obj(priv);
- val = c_can_read_reg32(priv, &priv->regs->txrqst1);
+ C_CAN_READ_REG32(val, priv, regs->txrqst1);
if (!(val & (1 << (msg_obj_no - 1)))) {
can_get_echo_skb(dev,
msg_obj_no - C_CAN_MSG_OBJ_TX_FIRST);
- stats->tx_bytes += priv->read_reg(priv,
- &priv->regs->ifregs[0].msg_cntrl)
- & IF_MCONT_DLC_MASK;
+ C_CAN_READ(msg_cntrl, priv, regs->ifregs[0].msg_cntrl);
+ stats->tx_bytes += msg_cntrl & IF_MCONT_DLC_MASK;
stats->tx_packets++;
c_can_inval_msg_object(dev, 0, msg_obj_no);
} else {
@@ -744,11 +747,11 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
u32 num_rx_pkts = 0;
unsigned int msg_obj, msg_ctrl_save;
struct c_can_priv *priv = netdev_priv(dev);
- u32 val = c_can_read_reg32(priv, &priv->regs->intpnd1);
+ u32 val;
+ C_CAN_READ_REG32(val, priv, regs->intpnd1);
for (msg_obj = C_CAN_MSG_OBJ_RX_FIRST;
msg_obj <= C_CAN_MSG_OBJ_RX_LAST && quota > 0;
- val = c_can_read_reg32(priv, &priv->regs->intpnd1),
msg_obj++) {
/*
* as interrupt pending register's bit n-1 corresponds to
@@ -757,8 +760,8 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
if (val & (1 << (msg_obj - 1))) {
c_can_object_get(dev, 0, msg_obj, IF_COMM_ALL &
~IF_COMM_TXRQST);
- msg_ctrl_save = priv->read_reg(priv,
- &priv->regs->ifregs[0].msg_cntrl);
+ C_CAN_READ(msg_ctrl_save, priv,
+ regs->ifregs[0].msg_cntrl);
if (msg_ctrl_save & IF_MCONT_EOB)
return num_rx_pkts;
@@ -791,6 +794,7 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
num_rx_pkts++;
quota--;
}
+ C_CAN_READ_REG32(val, priv, regs->intpnd1);
}
return num_rx_pkts;
@@ -819,7 +823,7 @@ static int c_can_handle_state_change(struct net_device *dev,
return 0;
c_can_get_berr_counter(dev, &bec);
- reg_err_counter = priv->read_reg(priv, &priv->regs->err_cnt);
+ C_CAN_READ(reg_err_counter, priv, regs->err_cnt);
rx_err_passive = (reg_err_counter & ERR_CNT_RP_MASK) >>
ERR_CNT_RP_SHIFT;
@@ -935,7 +939,7 @@ static int c_can_handle_bus_err(struct net_device *dev,
}
/* set a `lec` value so that we can check for updates later */
- priv->write_reg(priv, &priv->regs->status, LEC_UNUSED);
+ C_CAN_WRITE(priv, regs->status, LEC_UNUSED);
netif_receive_skb(skb);
stats->rx_packets++;
@@ -956,16 +960,15 @@ static int c_can_poll(struct napi_struct *napi, int quota)
/* status events have the highest priority */
if (priv->irqstatus == STATUS_INTERRUPT) {
- priv->current_status = priv->read_reg(priv,
- &priv->regs->status);
+ C_CAN_READ(priv->current_status, priv, regs->status);
/* handle Tx/Rx events */
if (priv->current_status & STATUS_TXOK)
- priv->write_reg(priv, &priv->regs->status,
+ C_CAN_WRITE(priv, regs->status,
priv->current_status & ~STATUS_TXOK);
if (priv->current_status & STATUS_RXOK)
- priv->write_reg(priv, &priv->regs->status,
+ C_CAN_WRITE(priv, regs->status,
priv->current_status & ~STATUS_RXOK);
/* handle state changes */
@@ -1031,7 +1034,7 @@ static irqreturn_t c_can_isr(int irq, void *dev_id)
struct net_device *dev = (struct net_device *)dev_id;
struct c_can_priv *priv = netdev_priv(dev);
- priv->irqstatus = priv->read_reg(priv, &priv->regs->interrupt);
+ C_CAN_READ(priv->irqstatus, priv, regs->interrupt);
if (!priv->irqstatus)
return IRQ_NONE;
diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h
index 5f32d34..7084e4c 100644
--- a/drivers/net/can/c_can/c_can.h
+++ b/drivers/net/can/c_can/c_can.h
@@ -61,6 +61,95 @@ struct c_can_regs {
u16 _reserved6[6];
};
+/* c_can IF registers */
+struct d_can_if_regs {
+ u16 com_req;
+ u16 com_mask;
+ u16 mask1;
+ u16 mask2;
+ u16 arb1;
+ u16 arb2;
+ u16 msg_cntrl;
+ u16 msg_cntrl1;
+ u16 data[4];
+};
+
+/* d_can hardware registers */
+struct d_can_regs {
+ u16 control;
+ u16 control1;
+ u16 status;
+ u16 status1;
+ u16 err_cnt;
+ u16 err_cnt1;
+ u16 btr;
+ u16 brp_ext;
+ u16 interrupt;
+ u16 interrupt1;
+ u16 test;
+ u16 test1;
+ u32 _reserved1;
+ u16 parity_err;
+ u16 parity_err1;
+ u32 _reserved2[24];
+ u16 auto_bus_on;
+ u16 auto_bus_on1;
+ u16 txrq_x;
+ u16 txrq_x1;
+ u16 txrqst1;
+ u16 txrqst2;
+ u32 txrq[3];
+ u16 new_dat_x;
+ u16 new_dat_x1;
+ u16 newdat1;
+ u16 newdat2;
+ u32 new_dat[3];
+ u16 intpnd_x;
+ u16 intpnd_x1;
+ u16 intpnd1;
+ u16 intpnd2;
+ u32 intpnd[3];
+ u16 msg_val_x;
+ u16 msg_val_x1;
+ u16 msgval1;
+ u16 msgval2;
+ u32 msg_val[3];
+ u32 _reserved3;
+ u32 intmux[4];
+ u32 _reserved4[6];
+ struct d_can_if_regs ifregs[3];
+ u32 _reserved5[2];
+ u32 if3_update[4];
+ u16 tioc;
+ u16 tioc1;
+ u16 rioc;
+ u16 rioc1;
+};
+
+#define C_CAN_READ_REG32(ret, priv, reg) \
+ do { \
+ if (priv->dev_type == DEV_TYPE_D_CAN) \
+ ret = c_can_read_reg32(priv, &priv->d##reg); \
+ else \
+ ret = c_can_read_reg32(priv, &priv->c##reg); \
+ } while (0) \
+
+#define C_CAN_READ(ret, priv, reg) \
+ do { \
+ if (priv->dev_type == DEV_TYPE_D_CAN) \
+ ret = priv->read_reg(priv, &priv->d##reg); \
+ else \
+ ret = priv->read_reg(priv, &priv->c##reg); \
+ } while (0) \
+
+#define C_CAN_WRITE(priv, reg, val) \
+ do { \
+ if (priv->dev_type == DEV_TYPE_D_CAN) \
+ priv->write_reg(priv, &priv->d##reg, val); \
+ else \
+ priv->write_reg(priv, &priv->c##reg, val); \
+ } while (0) \
+
/* c_can private data structure */
struct c_can_priv {
struct can_priv can; /* must be the first member */
@@ -71,12 +160,14 @@ struct c_can_priv {
int last_status;
u16 (*read_reg) (struct c_can_priv *priv, void *reg);
void (*write_reg) (struct c_can_priv *priv, void *reg, u16 val);
- struct c_can_regs __iomem *regs;
+ struct d_can_regs __iomem *dregs;
+ struct c_can_regs __iomem *cregs;
unsigned long irq_flags; /* for request_irq() */
unsigned int tx_next;
unsigned int tx_echo;
void *priv; /* for board-specific data */
u16 irqstatus;
+ unsigned int dev_type;
};
struct net_device *alloc_c_can_dev(void);
diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c
index 5e1a5ff..56de2dc 100644
--- a/drivers/net/can/c_can/c_can_platform.c
+++ b/drivers/net/can/c_can/c_can_platform.c
@@ -32,6 +32,7 @@
#include <linux/clk.h>
#include <linux/can/dev.h>
+#include <linux/can/platform/c_can.h>
#include "c_can.h"
@@ -56,13 +57,25 @@ static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv,
static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv,
void *reg)
{
- return readw(reg + (long)reg - (long)priv->regs);
+ return readw(reg + (long)reg - (long)priv->cregs);
}
static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv,
void *reg, u16 val)
{
- writew(val, reg + (long)reg - (long)priv->regs);
+ writew(val, reg + (long)reg - (long)priv->cregs);
+}
+
+static u16 d_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv,
+ void *reg)
+{
+ return readw(reg + (long)reg - (long)priv->dregs);
+}
+
+static void d_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv,
+ void *reg, u16 val)
+{
+ writew(val, reg + (long)reg - (long)priv->dregs);
}
static int __devinit c_can_plat_probe(struct platform_device *pdev)
@@ -71,6 +84,7 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
void __iomem *addr;
struct net_device *dev;
struct c_can_priv *priv;
+ struct c_can_platform_data *pdata = pdev->dev.platform_data;
struct resource *mem;
int irq;
#ifdef CONFIG_HAVE_CLK
@@ -115,9 +129,19 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
}
priv = netdev_priv(dev);
+ if (pdata) {
+ if (pdata->dev_type == DEV_TYPE_D_CAN) {
+ priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
+ priv->dev_type = DEV_TYPE_D_CAN;
+ priv->dregs = (struct d_can_regs __iomem *)addr;
+ } else {
+ priv->cregs = (struct c_can_regs __iomem *)addr;
+ priv->dev_type = DEV_TYPE_C_CAN;
+ }
+ }
dev->irq = irq;
- priv->regs = addr;
+
#ifdef CONFIG_HAVE_CLK
priv->can.clock.freq = clk_get_rate(clk);
priv->priv = clk;
@@ -125,8 +149,13 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
case IORESOURCE_MEM_32BIT:
- priv->read_reg = c_can_plat_read_reg_aligned_to_32bit;
- priv->write_reg = c_can_plat_write_reg_aligned_to_32bit;
+ if (pdata->dev_type == DEV_TYPE_D_CAN) {
+ priv->read_reg = d_can_plat_read_reg_aligned_to_32bit;
+ priv->write_reg = d_can_plat_write_reg_aligned_to_32bit;
+ } else {
+ priv->read_reg = c_can_plat_read_reg_aligned_to_32bit;
+ priv->write_reg = c_can_plat_write_reg_aligned_to_32bit;
+ }
break;
case IORESOURCE_MEM_16BIT:
default:
@@ -146,7 +175,7 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n",
- KBUILD_MODNAME, priv->regs, dev->irq);
+ KBUILD_MODNAME, addr, dev->irq);
return 0;
exit_free_device:
@@ -176,7 +205,11 @@ static int __devexit c_can_plat_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
free_c_can_dev(dev);
- iounmap(priv->regs);
+
+ if (priv->dev_type == DEV_TYPE_D_CAN)
+ iounmap(priv->dregs);
+ else
+ iounmap(priv->cregs);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, resource_size(mem));
diff --git a/include/linux/can/platform/c_can.h b/include/linux/can/platform/c_can.h
new file mode 100644
index 0000000..e95201a
--- /dev/null
+++ b/include/linux/can/platform/c_can.h
@@ -0,0 +1,42 @@
+/*
+ * C_CAN controller driver platform header
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Bosch C_CAN/D_CAN controller is compliant to CAN protocol version 2.0
+ * part A and B.
+ * Bosch C_CAN user manual can be obtained from:
+ * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/
+ * users_manual_c_can.pdf
+ * Bosch D_CAN user manual can be obtained from:
+ * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/can/
+ * d_can_users_manual_111.pdf
+ *
+ * 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 __CAN_PLATFORM_TI_C_CAN_H__
+#define __CAN_PLATFORM_TI_C_CAN_H__
+
+#define DEV_TYPE_C_CAN 0
+#define DEV_TYPE_D_CAN 1
+
+/**
+ * struct c_can_platform_data - CCAN Platform Data
+ *
+ * @dev_type: Device type C_CAN/D_CAN
+ *
+ * Platform data structure to get all platform specific settings.
+ */
+
+struct c_can_platform_data {
+ unsigned int dev_type;
+};
+#endif
--
1.7.0.4
next reply other threads:[~2012-04-20 9:58 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-04-20 9:58 AnilKumar Ch [this message]
2012-04-24 8:10 ` [PATCH 3/3] can: c_can: Add support for Bosch D_CAN controller Marc Kleine-Budde
2012-04-24 8:31 ` Wolfgang Grandegger
2012-04-24 8:36 ` Marc Kleine-Budde
2012-04-24 9:14 ` Wolfgang Grandegger
2012-04-24 11:42 ` AnilKumar, Chimata
2012-04-24 12:25 ` Wolfgang Grandegger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1334915902-30253-1-git-send-email-anilkumar@ti.com \
--to=anilkumar@ti.com \
--cc=anantgole@ti.com \
--cc=linux-can@vger.kernel.org \
--cc=mkl@pengutronix.de \
--cc=nsekhar@ti.com \
--cc=wg@grandegger.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox