[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Mon, 9 Sep 2013 09:24:58 +0200
From: Benedikt Spranger <b.spranger@...utronix.de>
To: netdev@...r.kernel.org
Cc: Alexander Frank <Alexander.Frank@...rspaecher.com>,
Sebastian Andrzej Siewior <bigeasy@...utronix.de>,
Holger Dengler <dengler@...utronix.de>,
Benedikt Spranger <b.spranger@...utronix.de>
Subject: [PATCH 01/16] c_can_platform: add FlexCard D-CAN support
FlexCard supports up to 8 D-CAN devices with a shared DMA-capable receive
function. Add support for FlexCard D-CAN.
Signed-off-by: Benedikt Spranger <b.spranger@...utronix.de>
---
drivers/net/can/c_can/c_can.h | 1 +
drivers/net/can/c_can/c_can_platform.c | 149 ++++++++++++++++++++++++++++++++-
2 files changed, 148 insertions(+), 2 deletions(-)
diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h
index d2e1c21..d2e2d20 100644
--- a/drivers/net/can/c_can/c_can.h
+++ b/drivers/net/can/c_can/c_can.h
@@ -148,6 +148,7 @@ enum c_can_dev_id {
BOSCH_C_CAN_PLATFORM,
BOSCH_C_CAN,
BOSCH_D_CAN,
+ BOSCH_D_CAN_FLEXCARD,
};
/* c_can private data structure */
diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c
index c6f838d..43a3e3f 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/of.h>
#include <linux/of_device.h>
+#include <linux/flexcard.h>
#include <linux/can/dev.h>
@@ -81,6 +82,112 @@ static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
writel(val, priv->raminit_ctrlreg);
}
+static int c_can_rx_pkt(void *p, void *data, size_t len)
+{
+ struct net_device *dev = p;
+ struct net_device_stats *stats = &dev->stats;
+ struct fc_packet_buf *pb = data;
+ union fc_packet_types *pt = &pb->packet;
+ struct can_frame *frame;
+ struct sk_buff *skb;
+ u32 flags, id, state, type;
+
+ switch (le32_to_cpu(pb->header.type)) {
+ case fc_packet_type_can:
+ skb = alloc_can_skb(dev, &frame);
+ if (!skb) {
+ stats->rx_dropped++;
+ return -ENOMEM;
+ }
+
+ id = le32_to_cpu(pt->can_packet.id);
+ flags = le32_to_cpu(pt->can_packet.flags);
+
+ if (id & BIT(29))
+ frame->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ frame->can_id = id & CAN_SFF_MASK;
+
+ if (flags & BIT(13))
+ frame->can_id |= CAN_RTR_FLAG;
+ frame->can_dlc = (flags >> 8) & 0xf;
+ memcpy(frame->data, pt->can_packet.data, frame->can_dlc);
+ netif_receive_skb(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+ break;
+
+ case fc_packet_type_can_error:
+ stats->rx_errors++;
+
+ skb = alloc_can_err_skb(dev, &frame);
+ if (!skb)
+ return -ENOMEM;
+
+ type = le32_to_cpu(pt->can_error_packet.type);
+ state = le32_to_cpu(pt->can_error_packet.state);
+
+ switch (state) {
+ case fc_can_state_warning:
+ frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ frame->can_id |= CAN_ERR_CRTL;
+ break;
+ case fc_can_state_error_passive:
+ frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+ frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+ frame->can_id |= CAN_ERR_CRTL;
+ break;
+ case fc_can_state_bus_off:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ break;
+ }
+
+ switch (type) {
+ case fc_can_error_stuff:
+ frame->data[2] |= CAN_ERR_PROT_STUFF;
+ frame->can_id |= CAN_ERR_PROT;
+ break;
+ case fc_can_error_form:
+ frame->data[2] |= CAN_ERR_PROT_FORM;
+ frame->can_id |= CAN_ERR_PROT;
+ break;
+ case fc_can_error_acknowledge:
+ frame->can_id |= CAN_ERR_ACK;
+ break;
+ case fc_can_error_bit1:
+ frame->data[2] |= CAN_ERR_PROT_BIT1;
+ frame->can_id |= CAN_ERR_PROT;
+ break;
+ case fc_can_error_bit0:
+ frame->data[2] |= CAN_ERR_PROT_BIT0;
+ frame->can_id |= CAN_ERR_PROT;
+ break;
+ case fc_can_error_crc:
+ frame->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+ frame->can_id |= CAN_ERR_PROT;
+ break;
+ case fc_can_error_parity:
+ frame->data[1] |= CAN_ERR_PROT_UNSPEC;
+ frame->can_id |= CAN_ERR_CRTL;
+ break;
+ }
+ frame->data[5] = pt->can_error_packet.rx_error_counter;
+ frame->data[6] = pt->can_error_packet.tx_error_counter;
+
+ stats->rx_bytes += frame->can_dlc;
+ stats->rx_packets++;
+
+ netif_receive_skb(skb);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static struct platform_device_id c_can_id_table[] = {
[BOSCH_C_CAN_PLATFORM] = {
.name = KBUILD_MODNAME,
@@ -93,6 +200,10 @@ static struct platform_device_id c_can_id_table[] = {
[BOSCH_D_CAN] = {
.name = "d_can",
.driver_data = BOSCH_D_CAN,
+ },
+ [BOSCH_D_CAN_FLEXCARD] = {
+ .name = "flexcard-d_can",
+ .driver_data = BOSCH_D_CAN_FLEXCARD,
}, {
}
};
@@ -108,7 +219,7 @@ MODULE_DEVICE_TABLE(of, c_can_of_table);
static int c_can_plat_probe(struct platform_device *pdev)
{
int ret;
- void __iomem *addr;
+ void __iomem *addr, *reset_reg;
struct net_device *dev;
struct c_can_priv *priv;
const struct of_device_id *match;
@@ -167,6 +278,8 @@ static int c_can_plat_probe(struct platform_device *pdev)
}
priv = netdev_priv(dev);
+ priv->can.clock.freq = clk_get_rate(clk);
+
switch (id->driver_data) {
case BOSCH_C_CAN:
priv->regs = reg_map_c_can;
@@ -199,7 +312,26 @@ static int c_can_plat_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "control memory is not used for raminit\n");
else
priv->raminit = c_can_hw_raminit;
+
break;
+ case BOSCH_D_CAN_FLEXCARD:
+ priv->regs = reg_map_d_can;
+ priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
+ priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
+ priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
+ priv->instance = pdev->id;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ reset_reg = ioremap(res->start, resource_size(res));
+ if (!reset_reg || priv->instance < 0) {
+ dev_info(&pdev->dev, "Can't reset device.\n");
+ } else {
+ writel(1 << (4 + priv->instance), addr);
+ udelay(100);
+ }
+ iounmap(reset_reg);
+ priv->can.clock.freq = 16000000;
+
default:
ret = -EINVAL;
goto exit_free_device;
@@ -208,7 +340,6 @@ static int c_can_plat_probe(struct platform_device *pdev)
dev->irq = irq;
priv->base = addr;
priv->device = &pdev->dev;
- priv->can.clock.freq = clk_get_rate(clk);
priv->priv = clk;
priv->type = id->driver_data;
@@ -222,10 +353,22 @@ static int c_can_plat_probe(struct platform_device *pdev)
goto exit_free_device;
}
+ if (id->driver_data == BOSCH_D_CAN_FLEXCARD) {
+ ret = fc_register_rx_pkt(FC_CANIF_OFFSET + pdev->id,
+ dev, c_can_rx_pkt);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "registering RX-CB failed %d\n", ret);
+ goto exit_unregister_device;
+ }
+ }
+
dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n",
KBUILD_MODNAME, priv->base, dev->irq);
return 0;
+exit_unregister_device:
+ unregister_c_can_dev(dev);
exit_free_device:
free_c_can_dev(dev);
exit_iounmap:
@@ -246,6 +389,8 @@ static int c_can_plat_remove(struct platform_device *pdev)
struct c_can_priv *priv = netdev_priv(dev);
struct resource *mem;
+ if (priv->type == BOSCH_D_CAN_FLEXCARD)
+ fc_unregister_rx_pkt(FC_CANIF_OFFSET + priv->instance);
unregister_c_can_dev(dev);
free_c_can_dev(dev);
--
1.8.4.rc3
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists