summaryrefslogtreecommitdiffstats
path: root/drivers/net/can/flexcan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/can/flexcan.c')
-rw-r--r--drivers/net/can/flexcan.c154
1 files changed, 120 insertions, 34 deletions
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 38c0690df5c8..c5f143165f80 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -34,6 +34,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
@@ -165,10 +166,21 @@ struct flexcan_regs {
u32 imask1; /* 0x28 */
u32 iflag2; /* 0x2c */
u32 iflag1; /* 0x30 */
- u32 _reserved2[19];
+ u32 crl2; /* 0x34 */
+ u32 esr2; /* 0x38 */
+ u32 imeur; /* 0x3c */
+ u32 lrfr; /* 0x40 */
+ u32 crcr; /* 0x44 */
+ u32 rxfgmask; /* 0x48 */
+ u32 rxfir; /* 0x4c */
+ u32 _reserved3[12];
struct flexcan_mb cantxfg[64];
};
+struct flexcan_devtype_data {
+ u32 hw_ver; /* hardware controller version */
+};
+
struct flexcan_priv {
struct can_priv can;
struct net_device *dev;
@@ -178,11 +190,21 @@ struct flexcan_priv {
u32 reg_esr;
u32 reg_ctrl_default;
- struct clk *clk;
+ struct clk *clk_ipg;
+ struct clk *clk_per;
struct flexcan_platform_data *pdata;
+ const struct flexcan_devtype_data *devtype_data;
+};
+
+static struct flexcan_devtype_data fsl_p1010_devtype_data = {
+ .hw_ver = 3,
+};
+
+static struct flexcan_devtype_data fsl_imx6q_devtype_data = {
+ .hw_ver = 10,
};
-static struct can_bittiming_const flexcan_bittiming_const = {
+static const struct can_bittiming_const flexcan_bittiming_const = {
.name = DRV_NAME,
.tseg1_min = 4,
.tseg1_max = 16,
@@ -750,6 +772,9 @@ static int flexcan_chip_start(struct net_device *dev)
flexcan_write(0x0, &regs->rx14mask);
flexcan_write(0x0, &regs->rx15mask);
+ if (priv->devtype_data->hw_ver >= 10)
+ flexcan_write(0x0, &regs->rxfgmask);
+
flexcan_transceiver_switch(priv, 1);
/* synchronize with the can bus */
@@ -804,7 +829,8 @@ static int flexcan_open(struct net_device *dev)
struct flexcan_priv *priv = netdev_priv(dev);
int err;
- clk_prepare_enable(priv->clk);
+ clk_prepare_enable(priv->clk_ipg);
+ clk_prepare_enable(priv->clk_per);
err = open_candev(dev);
if (err)
@@ -826,7 +852,8 @@ static int flexcan_open(struct net_device *dev)
out_close:
close_candev(dev);
out:
- clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk_per);
+ clk_disable_unprepare(priv->clk_ipg);
return err;
}
@@ -840,7 +867,8 @@ static int flexcan_close(struct net_device *dev)
flexcan_chip_stop(dev);
free_irq(dev->irq, dev);
- clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk_per);
+ clk_disable_unprepare(priv->clk_ipg);
close_candev(dev);
@@ -879,7 +907,8 @@ static int __devinit register_flexcandev(struct net_device *dev)
struct flexcan_regs __iomem *regs = priv->base;
u32 reg, err;
- clk_prepare_enable(priv->clk);
+ clk_prepare_enable(priv->clk_ipg);
+ clk_prepare_enable(priv->clk_per);
/* select "bus clock", chip must be disabled */
flexcan_chip_disable(priv);
@@ -912,7 +941,8 @@ static int __devinit register_flexcandev(struct net_device *dev)
out:
/* disable core and turn off clocks */
flexcan_chip_disable(priv);
- clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk_per);
+ clk_disable_unprepare(priv->clk_ipg);
return err;
}
@@ -922,12 +952,25 @@ static void __devexit unregister_flexcandev(struct net_device *dev)
unregister_candev(dev);
}
+static const struct of_device_id flexcan_of_match[] = {
+ { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
+ { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
+ { /* sentinel */ },
+};
+
+static const struct platform_device_id flexcan_id_table[] = {
+ { .name = "flexcan", .driver_data = (kernel_ulong_t)&fsl_p1010_devtype_data, },
+ { /* sentinel */ },
+};
+
static int __devinit flexcan_probe(struct platform_device *pdev)
{
+ const struct of_device_id *of_id;
+ const struct flexcan_devtype_data *devtype_data;
struct net_device *dev;
struct flexcan_priv *priv;
struct resource *mem;
- struct clk *clk = NULL;
+ struct clk *clk_ipg = NULL, *clk_per = NULL;
struct pinctrl *pinctrl;
void __iomem *base;
resource_size_t mem_size;
@@ -938,23 +981,25 @@ static int __devinit flexcan_probe(struct platform_device *pdev)
if (IS_ERR(pinctrl))
return PTR_ERR(pinctrl);
- if (pdev->dev.of_node) {
- const u32 *clock_freq_p;
-
- clock_freq_p = of_get_property(pdev->dev.of_node,
- "clock-frequency", NULL);
- if (clock_freq_p)
- clock_freq = *clock_freq_p;
- }
+ if (pdev->dev.of_node)
+ of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &clock_freq);
if (!clock_freq) {
- clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "no clock defined\n");
- err = PTR_ERR(clk);
+ clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(clk_ipg)) {
+ dev_err(&pdev->dev, "no ipg clock defined\n");
+ err = PTR_ERR(clk_ipg);
+ goto failed_clock;
+ }
+ clock_freq = clk_get_rate(clk_ipg);
+
+ clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(clk_per)) {
+ dev_err(&pdev->dev, "no per clock defined\n");
+ err = PTR_ERR(clk_per);
goto failed_clock;
}
- clock_freq = clk_get_rate(clk);
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -982,6 +1027,17 @@ static int __devinit flexcan_probe(struct platform_device *pdev)
goto failed_alloc;
}
+ of_id = of_match_device(flexcan_of_match, &pdev->dev);
+ if (of_id) {
+ devtype_data = of_id->data;
+ } else if (pdev->id_entry->driver_data) {
+ devtype_data = (struct flexcan_devtype_data *)
+ pdev->id_entry->driver_data;
+ } else {
+ err = -ENODEV;
+ goto failed_devtype;
+ }
+
dev->netdev_ops = &flexcan_netdev_ops;
dev->irq = irq;
dev->flags |= IFF_ECHO;
@@ -996,8 +1052,10 @@ static int __devinit flexcan_probe(struct platform_device *pdev)
CAN_CTRLMODE_BERR_REPORTING;
priv->base = base;
priv->dev = dev;
- priv->clk = clk;
+ priv->clk_ipg = clk_ipg;
+ priv->clk_per = clk_per;
priv->pdata = pdev->dev.platform_data;
+ priv->devtype_data = devtype_data;
netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);
@@ -1016,14 +1074,13 @@ static int __devinit flexcan_probe(struct platform_device *pdev)
return 0;
failed_register:
+ failed_devtype:
free_candev(dev);
failed_alloc:
iounmap(base);
failed_map:
release_mem_region(mem->start, mem_size);
failed_get:
- if (clk)
- clk_put(clk);
failed_clock:
return err;
}
@@ -1041,20 +1098,46 @@ static int __devexit flexcan_remove(struct platform_device *pdev)
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, resource_size(mem));
- if (priv->clk)
- clk_put(priv->clk);
-
free_candev(dev);
return 0;
}
-static struct of_device_id flexcan_of_match[] = {
- {
- .compatible = "fsl,p1010-flexcan",
- },
- {},
-};
+#ifdef CONFIG_PM
+static int flexcan_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct flexcan_priv *priv = netdev_priv(dev);
+
+ flexcan_chip_disable(priv);
+
+ if (netif_running(dev)) {
+ netif_stop_queue(dev);
+ netif_device_detach(dev);
+ }
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ return 0;
+}
+
+static int flexcan_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct flexcan_priv *priv = netdev_priv(dev);
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ if (netif_running(dev)) {
+ netif_device_attach(dev);
+ netif_start_queue(dev);
+ }
+ flexcan_chip_enable(priv);
+
+ return 0;
+}
+#else
+#define flexcan_suspend NULL
+#define flexcan_resume NULL
+#endif
static struct platform_driver flexcan_driver = {
.driver = {
@@ -1064,6 +1147,9 @@ static struct platform_driver flexcan_driver = {
},
.probe = flexcan_probe,
.remove = __devexit_p(flexcan_remove),
+ .suspend = flexcan_suspend,
+ .resume = flexcan_resume,
+ .id_table = flexcan_id_table,
};
module_platform_driver(flexcan_driver);