Merge branch 'for-davem' of git://gitorious.org/linux-can/linux-can-next
authorDavid S. Miller <davem@davemloft.net>
Wed, 4 Sep 2013 01:54:02 +0000 (21:54 -0400)
committerDavid S. Miller <davem@davemloft.net>
Wed, 4 Sep 2013 01:54:02 +0000 (21:54 -0400)
Marc Kleine-Budde says:

====================
this is a pull request for net-next. There are two patches from Gerhard
Sittig, which improves the clock handling on mpc5121. Oliver Hartkopp
provides a patch that adds a per rule limitation of frame hops.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/can/mscan/mpc5xxx_can.c
drivers/net/can/mscan/mscan.c
drivers/net/can/mscan/mscan.h
include/uapi/linux/can/gw.h
net/can/gw.c

index 5b0ee8ef5885e54a610030b9318445bae8aaf321..e59b3a392af62d8cfcb0e9f40ad3b974027c3299 100644 (file)
@@ -40,6 +40,7 @@ struct mpc5xxx_can_data {
        unsigned int type;
        u32 (*get_clock)(struct platform_device *ofdev, const char *clock_name,
                         int *mscan_clksrc);
+       void (*put_clock)(struct platform_device *ofdev);
 };
 
 #ifdef CONFIG_PPC_MPC52xx
@@ -148,7 +149,10 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev,
                goto exit_put;
        }
 
-       /* Determine the MSCAN device index from the physical address */
+       /* Determine the MSCAN device index from the peripheral's
+        * physical address. Register address offsets against the
+        * IMMR base are:  0x1300, 0x1380, 0x2300, 0x2380
+        */
        pval = of_get_property(ofdev->dev.of_node, "reg", &plen);
        BUG_ON(!pval || plen < sizeof(*pval));
        clockidx = (*pval & 0x80) ? 1 : 0;
@@ -177,7 +181,7 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev,
                        clockdiv = 1;
 
                if (!clock_name || !strcmp(clock_name, "sys")) {
-                       sys_clk = clk_get(&ofdev->dev, "sys_clk");
+                       sys_clk = devm_clk_get(&ofdev->dev, "sys_clk");
                        if (IS_ERR(sys_clk)) {
                                dev_err(&ofdev->dev, "couldn't get sys_clk\n");
                                goto exit_unmap;
@@ -200,7 +204,7 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev,
                }
 
                if (clocksrc < 0) {
-                       ref_clk = clk_get(&ofdev->dev, "ref_clk");
+                       ref_clk = devm_clk_get(&ofdev->dev, "ref_clk");
                        if (IS_ERR(ref_clk)) {
                                dev_err(&ofdev->dev, "couldn't get ref_clk\n");
                                goto exit_unmap;
@@ -277,6 +281,8 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev)
        dev = alloc_mscandev();
        if (!dev)
                goto exit_dispose_irq;
+       platform_set_drvdata(ofdev, dev);
+       SET_NETDEV_DEV(dev, &ofdev->dev);
 
        priv = netdev_priv(dev);
        priv->reg_base = base;
@@ -293,8 +299,6 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev)
                goto exit_free_mscan;
        }
 
-       SET_NETDEV_DEV(dev, &ofdev->dev);
-
        err = register_mscandev(dev, mscan_clksrc);
        if (err) {
                dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
@@ -302,8 +306,6 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev)
                goto exit_free_mscan;
        }
 
-       platform_set_drvdata(ofdev, dev);
-
        dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n",
                 priv->reg_base, dev->irq, priv->can.clock.freq);
 
@@ -321,10 +323,17 @@ exit_unmap_mem:
 
 static int mpc5xxx_can_remove(struct platform_device *ofdev)
 {
+       const struct of_device_id *match;
+       const struct mpc5xxx_can_data *data;
        struct net_device *dev = platform_get_drvdata(ofdev);
        struct mscan_priv *priv = netdev_priv(dev);
 
+       match = of_match_device(mpc5xxx_can_table, &ofdev->dev);
+       data = match ? match->data : NULL;
+
        unregister_mscandev(dev);
+       if (data && data->put_clock)
+               data->put_clock(ofdev);
        iounmap(priv->reg_base);
        irq_dispose_mapping(dev->irq);
        free_candev(dev);
index e6b40954e204d77bfb6a465a8ecc607db7231f98..a955ec8c4b9712915796e419584080c93cba6f13 100644 (file)
@@ -573,10 +573,21 @@ static int mscan_open(struct net_device *dev)
        struct mscan_priv *priv = netdev_priv(dev);
        struct mscan_regs __iomem *regs = priv->reg_base;
 
+       if (priv->clk_ipg) {
+               ret = clk_prepare_enable(priv->clk_ipg);
+               if (ret)
+                       goto exit_retcode;
+       }
+       if (priv->clk_can) {
+               ret = clk_prepare_enable(priv->clk_can);
+               if (ret)
+                       goto exit_dis_ipg_clock;
+       }
+
        /* common open */
        ret = open_candev(dev);
        if (ret)
-               return ret;
+               goto exit_dis_can_clock;
 
        napi_enable(&priv->napi);
 
@@ -604,6 +615,13 @@ exit_free_irq:
 exit_napi_disable:
        napi_disable(&priv->napi);
        close_candev(dev);
+exit_dis_can_clock:
+       if (priv->clk_can)
+               clk_disable_unprepare(priv->clk_can);
+exit_dis_ipg_clock:
+       if (priv->clk_ipg)
+               clk_disable_unprepare(priv->clk_ipg);
+exit_retcode:
        return ret;
 }
 
@@ -621,6 +639,11 @@ static int mscan_close(struct net_device *dev)
        close_candev(dev);
        free_irq(dev->irq, dev);
 
+       if (priv->clk_can)
+               clk_disable_unprepare(priv->clk_can);
+       if (priv->clk_ipg)
+               clk_disable_unprepare(priv->clk_ipg);
+
        return 0;
 }
 
index af2ed8baf0a3b30b9361c0376f65ec0672d7b1b7..9c24d60a23b1dbf459c75d14f7e3ac340350b9d0 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef __MSCAN_H__
 #define __MSCAN_H__
 
+#include <linux/clk.h>
 #include <linux/types.h>
 
 /* MSCAN control register 0 (CANCTL0) bits */
@@ -283,6 +284,8 @@ struct mscan_priv {
        unsigned int type;      /* MSCAN type variants */
        unsigned long flags;
        void __iomem *reg_base; /* ioremap'ed address to registers */
+       struct clk *clk_ipg;    /* clock for registers */
+       struct clk *clk_can;    /* clock for bitrates */
        u8 shadow_statflg;
        u8 shadow_canrier;
        u8 cur_pri;
index ae07bec74f4bfb6d772c5d596ac08a06a1c28d35..4e27c82b564a13a6a4b55860c3cb78b51f86dc7e 100644 (file)
@@ -45,6 +45,7 @@ enum {
        CGW_DST_IF,     /* ifindex of destination network interface */
        CGW_FILTER,     /* specify struct can_filter on source CAN device */
        CGW_DELETED,    /* number of deleted CAN frames (see max_hops param) */
+       CGW_LIM_HOPS,   /* limit the number of hops of this specific rule */
        __CGW_MAX
 };
 
@@ -116,13 +117,19 @@ enum {
  * Sets a CAN receive filter for the gateway job specified by the
  * struct can_filter described in include/linux/can.h
  *
- * CGW_MOD_XXX (length 17 bytes):
+ * CGW_MOD_(AND|OR|XOR|SET) (length 17 bytes):
  * Specifies a modification that's done to a received CAN frame before it is
  * send out to the destination interface.
  *
  * <struct can_frame> data used as operator
  * <u8> affected CAN frame elements
  *
+ * CGW_LIM_HOPS (length 1 byte):
+ * Limit the number of hops of this specific rule. Usually the received CAN
+ * frame can be processed as much as 'max_hops' times (which is given at module
+ * load time of the can-gw module). This value is used to reduce the number of
+ * possible hops for this gateway rule to a value smaller then max_hops.
+ *
  * CGW_CS_XOR (length 4 bytes):
  * Set a simple XOR checksum starting with an initial value into
  * data[result-idx] using data[start-idx] .. data[end-idx]
index 2f291f961a170018f1464fb50f4faa272887a50d..3f9b0f3a281876e7d8ec7de0366ed98712759aad 100644 (file)
@@ -146,6 +146,7 @@ struct cgw_job {
                /* tbc */
        };
        u8 gwtype;
+       u8 limit_hops;
        u16 flags;
 };
 
@@ -402,6 +403,11 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
 
        /* put the incremented hop counter in the cloned skb */
        cgw_hops(nskb) = cgw_hops(skb) + 1;
+
+       /* first processing of this CAN frame -> adjust to private hop limit */
+       if (gwj->limit_hops && cgw_hops(nskb) == 1)
+               cgw_hops(nskb) = max_hops - gwj->limit_hops + 1;
+
        nskb->dev = gwj->dst.dev;
 
        /* pointer to modifiable CAN frame */
@@ -509,6 +515,11 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
 
        /* check non default settings of attributes */
 
+       if (gwj->limit_hops) {
+               if (nla_put_u8(skb, CGW_LIM_HOPS, gwj->limit_hops) < 0)
+                       goto cancel;
+       }
+
        if (gwj->mod.modtype.and) {
                memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
                mb.modtype = gwj->mod.modtype.and;
@@ -606,11 +617,12 @@ static const struct nla_policy cgw_policy[CGW_MAX+1] = {
        [CGW_SRC_IF]    = { .type = NLA_U32 },
        [CGW_DST_IF]    = { .type = NLA_U32 },
        [CGW_FILTER]    = { .len = sizeof(struct can_filter) },
+       [CGW_LIM_HOPS]  = { .type = NLA_U8 },
 };
 
 /* check for common and gwtype specific attributes */
 static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
-                         u8 gwtype, void *gwtypeattr)
+                         u8 gwtype, void *gwtypeattr, u8 *limhops)
 {
        struct nlattr *tb[CGW_MAX+1];
        struct cgw_frame_mod mb;
@@ -625,6 +637,13 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
        if (err < 0)
                return err;
 
+       if (tb[CGW_LIM_HOPS]) {
+               *limhops = nla_get_u8(tb[CGW_LIM_HOPS]);
+
+               if (*limhops < 1 || *limhops > max_hops)
+                       return -EINVAL;
+       }
+
        /* check for AND/OR/XOR/SET modifications */
 
        if (tb[CGW_MOD_AND]) {
@@ -782,6 +801,7 @@ static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh)
 {
        struct rtcanmsg *r;
        struct cgw_job *gwj;
+       u8 limhops = 0;
        int err = 0;
 
        if (!capable(CAP_NET_ADMIN))
@@ -808,7 +828,8 @@ static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh)
        gwj->flags = r->flags;
        gwj->gwtype = r->gwtype;
 
-       err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw);
+       err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw,
+                            &limhops);
        if (err < 0)
                goto out;
 
@@ -836,6 +857,8 @@ static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh)
        if (gwj->dst.dev->type != ARPHRD_CAN || gwj->dst.dev->header_ops)
                goto put_src_dst_out;
 
+       gwj->limit_hops = limhops;
+
        ASSERT_RTNL();
 
        err = cgw_register_filter(gwj);
@@ -867,13 +890,14 @@ static void cgw_remove_all_jobs(void)
        }
 }
 
-static int cgw_remove_job(struct sk_buff *skb,  struct nlmsghdr *nlh)
+static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct cgw_job *gwj = NULL;
        struct hlist_node *nx;
        struct rtcanmsg *r;
        struct cf_mod mod;
        struct can_can_gw ccgw;
+       u8 limhops = 0;
        int err = 0;
 
        if (!capable(CAP_NET_ADMIN))
@@ -890,7 +914,7 @@ static int cgw_remove_job(struct sk_buff *skb,  struct nlmsghdr *nlh)
        if (r->gwtype != CGW_TYPE_CAN_CAN)
                return -EINVAL;
 
-       err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw);
+       err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
        if (err < 0)
                return err;
 
@@ -910,6 +934,9 @@ static int cgw_remove_job(struct sk_buff *skb,  struct nlmsghdr *nlh)
                if (gwj->flags != r->flags)
                        continue;
 
+               if (gwj->limit_hops != limhops)
+                       continue;
+
                if (memcmp(&gwj->mod, &mod, sizeof(mod)))
                        continue;