Commit c12a2242 authored by David S. Miller's avatar David S. Miller

Merge branch 'for-davem' of git://gitorious.org/linux-can/linux-can-next

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: default avatarDavid S. Miller <davem@davemloft.net>
parents e7abfe40 391ac128
...@@ -40,6 +40,7 @@ struct mpc5xxx_can_data { ...@@ -40,6 +40,7 @@ struct mpc5xxx_can_data {
unsigned int type; unsigned int type;
u32 (*get_clock)(struct platform_device *ofdev, const char *clock_name, u32 (*get_clock)(struct platform_device *ofdev, const char *clock_name,
int *mscan_clksrc); int *mscan_clksrc);
void (*put_clock)(struct platform_device *ofdev);
}; };
#ifdef CONFIG_PPC_MPC52xx #ifdef CONFIG_PPC_MPC52xx
...@@ -148,7 +149,10 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev, ...@@ -148,7 +149,10 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev,
goto exit_put; 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); pval = of_get_property(ofdev->dev.of_node, "reg", &plen);
BUG_ON(!pval || plen < sizeof(*pval)); BUG_ON(!pval || plen < sizeof(*pval));
clockidx = (*pval & 0x80) ? 1 : 0; clockidx = (*pval & 0x80) ? 1 : 0;
...@@ -177,7 +181,7 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev, ...@@ -177,7 +181,7 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev,
clockdiv = 1; clockdiv = 1;
if (!clock_name || !strcmp(clock_name, "sys")) { 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)) { if (IS_ERR(sys_clk)) {
dev_err(&ofdev->dev, "couldn't get sys_clk\n"); dev_err(&ofdev->dev, "couldn't get sys_clk\n");
goto exit_unmap; goto exit_unmap;
...@@ -200,7 +204,7 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev, ...@@ -200,7 +204,7 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev,
} }
if (clocksrc < 0) { 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)) { if (IS_ERR(ref_clk)) {
dev_err(&ofdev->dev, "couldn't get ref_clk\n"); dev_err(&ofdev->dev, "couldn't get ref_clk\n");
goto exit_unmap; goto exit_unmap;
...@@ -277,6 +281,8 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev) ...@@ -277,6 +281,8 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev)
dev = alloc_mscandev(); dev = alloc_mscandev();
if (!dev) if (!dev)
goto exit_dispose_irq; goto exit_dispose_irq;
platform_set_drvdata(ofdev, dev);
SET_NETDEV_DEV(dev, &ofdev->dev);
priv = netdev_priv(dev); priv = netdev_priv(dev);
priv->reg_base = base; priv->reg_base = base;
...@@ -293,8 +299,6 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev) ...@@ -293,8 +299,6 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev)
goto exit_free_mscan; goto exit_free_mscan;
} }
SET_NETDEV_DEV(dev, &ofdev->dev);
err = register_mscandev(dev, mscan_clksrc); err = register_mscandev(dev, mscan_clksrc);
if (err) { if (err) {
dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
...@@ -302,8 +306,6 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev) ...@@ -302,8 +306,6 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev)
goto exit_free_mscan; goto exit_free_mscan;
} }
platform_set_drvdata(ofdev, dev);
dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n", dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n",
priv->reg_base, dev->irq, priv->can.clock.freq); priv->reg_base, dev->irq, priv->can.clock.freq);
...@@ -321,10 +323,17 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev) ...@@ -321,10 +323,17 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev)
static int mpc5xxx_can_remove(struct platform_device *ofdev) 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 net_device *dev = platform_get_drvdata(ofdev);
struct mscan_priv *priv = netdev_priv(dev); struct mscan_priv *priv = netdev_priv(dev);
match = of_match_device(mpc5xxx_can_table, &ofdev->dev);
data = match ? match->data : NULL;
unregister_mscandev(dev); unregister_mscandev(dev);
if (data && data->put_clock)
data->put_clock(ofdev);
iounmap(priv->reg_base); iounmap(priv->reg_base);
irq_dispose_mapping(dev->irq); irq_dispose_mapping(dev->irq);
free_candev(dev); free_candev(dev);
......
...@@ -573,10 +573,21 @@ static int mscan_open(struct net_device *dev) ...@@ -573,10 +573,21 @@ static int mscan_open(struct net_device *dev)
struct mscan_priv *priv = netdev_priv(dev); struct mscan_priv *priv = netdev_priv(dev);
struct mscan_regs __iomem *regs = priv->reg_base; 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 */ /* common open */
ret = open_candev(dev); ret = open_candev(dev);
if (ret) if (ret)
return ret; goto exit_dis_can_clock;
napi_enable(&priv->napi); napi_enable(&priv->napi);
...@@ -604,6 +615,13 @@ static int mscan_open(struct net_device *dev) ...@@ -604,6 +615,13 @@ static int mscan_open(struct net_device *dev)
exit_napi_disable: exit_napi_disable:
napi_disable(&priv->napi); napi_disable(&priv->napi);
close_candev(dev); 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; return ret;
} }
...@@ -621,6 +639,11 @@ static int mscan_close(struct net_device *dev) ...@@ -621,6 +639,11 @@ static int mscan_close(struct net_device *dev)
close_candev(dev); close_candev(dev);
free_irq(dev->irq, 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; return 0;
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#ifndef __MSCAN_H__ #ifndef __MSCAN_H__
#define __MSCAN_H__ #define __MSCAN_H__
#include <linux/clk.h>
#include <linux/types.h> #include <linux/types.h>
/* MSCAN control register 0 (CANCTL0) bits */ /* MSCAN control register 0 (CANCTL0) bits */
...@@ -283,6 +284,8 @@ struct mscan_priv { ...@@ -283,6 +284,8 @@ struct mscan_priv {
unsigned int type; /* MSCAN type variants */ unsigned int type; /* MSCAN type variants */
unsigned long flags; unsigned long flags;
void __iomem *reg_base; /* ioremap'ed address to registers */ 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_statflg;
u8 shadow_canrier; u8 shadow_canrier;
u8 cur_pri; u8 cur_pri;
......
...@@ -45,6 +45,7 @@ enum { ...@@ -45,6 +45,7 @@ enum {
CGW_DST_IF, /* ifindex of destination network interface */ CGW_DST_IF, /* ifindex of destination network interface */
CGW_FILTER, /* specify struct can_filter on source CAN device */ CGW_FILTER, /* specify struct can_filter on source CAN device */
CGW_DELETED, /* number of deleted CAN frames (see max_hops param) */ 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 __CGW_MAX
}; };
...@@ -116,13 +117,19 @@ enum { ...@@ -116,13 +117,19 @@ enum {
* Sets a CAN receive filter for the gateway job specified by the * Sets a CAN receive filter for the gateway job specified by the
* struct can_filter described in include/linux/can.h * 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 * Specifies a modification that's done to a received CAN frame before it is
* send out to the destination interface. * send out to the destination interface.
* *
* <struct can_frame> data used as operator * <struct can_frame> data used as operator
* <u8> affected CAN frame elements * <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): * CGW_CS_XOR (length 4 bytes):
* Set a simple XOR checksum starting with an initial value into * Set a simple XOR checksum starting with an initial value into
* data[result-idx] using data[start-idx] .. data[end-idx] * data[result-idx] using data[start-idx] .. data[end-idx]
......
...@@ -146,6 +146,7 @@ struct cgw_job { ...@@ -146,6 +146,7 @@ struct cgw_job {
/* tbc */ /* tbc */
}; };
u8 gwtype; u8 gwtype;
u8 limit_hops;
u16 flags; u16 flags;
}; };
...@@ -402,6 +403,11 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) ...@@ -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 */ /* put the incremented hop counter in the cloned skb */
cgw_hops(nskb) = cgw_hops(skb) + 1; 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; nskb->dev = gwj->dst.dev;
/* pointer to modifiable CAN frame */ /* pointer to modifiable CAN frame */
...@@ -509,6 +515,11 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type, ...@@ -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 */ /* 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) { if (gwj->mod.modtype.and) {
memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf)); memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
mb.modtype = gwj->mod.modtype.and; mb.modtype = gwj->mod.modtype.and;
...@@ -606,11 +617,12 @@ static const struct nla_policy cgw_policy[CGW_MAX+1] = { ...@@ -606,11 +617,12 @@ static const struct nla_policy cgw_policy[CGW_MAX+1] = {
[CGW_SRC_IF] = { .type = NLA_U32 }, [CGW_SRC_IF] = { .type = NLA_U32 },
[CGW_DST_IF] = { .type = NLA_U32 }, [CGW_DST_IF] = { .type = NLA_U32 },
[CGW_FILTER] = { .len = sizeof(struct can_filter) }, [CGW_FILTER] = { .len = sizeof(struct can_filter) },
[CGW_LIM_HOPS] = { .type = NLA_U8 },
}; };
/* check for common and gwtype specific attributes */ /* check for common and gwtype specific attributes */
static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, 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 nlattr *tb[CGW_MAX+1];
struct cgw_frame_mod mb; struct cgw_frame_mod mb;
...@@ -625,6 +637,13 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, ...@@ -625,6 +637,13 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
if (err < 0) if (err < 0)
return err; 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 */ /* check for AND/OR/XOR/SET modifications */
if (tb[CGW_MOD_AND]) { if (tb[CGW_MOD_AND]) {
...@@ -782,6 +801,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -782,6 +801,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
{ {
struct rtcanmsg *r; struct rtcanmsg *r;
struct cgw_job *gwj; struct cgw_job *gwj;
u8 limhops = 0;
int err = 0; int err = 0;
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
...@@ -808,7 +828,8 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -808,7 +828,8 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
gwj->flags = r->flags; gwj->flags = r->flags;
gwj->gwtype = r->gwtype; 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) if (err < 0)
goto out; goto out;
...@@ -836,6 +857,8 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -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) if (gwj->dst.dev->type != ARPHRD_CAN || gwj->dst.dev->header_ops)
goto put_src_dst_out; goto put_src_dst_out;
gwj->limit_hops = limhops;
ASSERT_RTNL(); ASSERT_RTNL();
err = cgw_register_filter(gwj); err = cgw_register_filter(gwj);
...@@ -867,13 +890,14 @@ static void cgw_remove_all_jobs(void) ...@@ -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 cgw_job *gwj = NULL;
struct hlist_node *nx; struct hlist_node *nx;
struct rtcanmsg *r; struct rtcanmsg *r;
struct cf_mod mod; struct cf_mod mod;
struct can_can_gw ccgw; struct can_can_gw ccgw;
u8 limhops = 0;
int err = 0; int err = 0;
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
...@@ -890,7 +914,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -890,7 +914,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)
if (r->gwtype != CGW_TYPE_CAN_CAN) if (r->gwtype != CGW_TYPE_CAN_CAN)
return -EINVAL; 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) if (err < 0)
return err; return err;
...@@ -910,6 +934,9 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -910,6 +934,9 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)
if (gwj->flags != r->flags) if (gwj->flags != r->flags)
continue; continue;
if (gwj->limit_hops != limhops)
continue;
if (memcmp(&gwj->mod, &mod, sizeof(mod))) if (memcmp(&gwj->mod, &mod, sizeof(mod)))
continue; continue;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment