can: rcar_can: support all input clocks
[firefly-linux-kernel-4.4.55.git] / drivers / net / can / rcar_can.c
index 5268d216ecfae7b5a447abbd1327d7a08a632b46..c02fcf369afd0991fa22a824781de72bc3cfc4b2 100644 (file)
@@ -87,6 +87,7 @@ struct rcar_can_priv {
        struct napi_struct napi;
        struct rcar_can_regs __iomem *regs;
        struct clk *clk;
+       struct clk *can_clk;
        u8 tx_dlc[RCAR_CAN_FIFO_DEPTH];
        u32 tx_head;
        u32 tx_tail;
@@ -505,14 +506,20 @@ static int rcar_can_open(struct net_device *ndev)
 
        err = clk_prepare_enable(priv->clk);
        if (err) {
-               netdev_err(ndev, "clk_prepare_enable() failed, error %d\n",
+               netdev_err(ndev, "failed to enable periperal clock, error %d\n",
                           err);
                goto out;
        }
+       err = clk_prepare_enable(priv->can_clk);
+       if (err) {
+               netdev_err(ndev, "failed to enable CAN clock, error %d\n",
+                          err);
+               goto out_clock;
+       }
        err = open_candev(ndev);
        if (err) {
                netdev_err(ndev, "open_candev() failed, error %d\n", err);
-               goto out_clock;
+               goto out_can_clock;
        }
        napi_enable(&priv->napi);
        err = request_irq(ndev->irq, rcar_can_interrupt, 0, ndev->name, ndev);
@@ -527,6 +534,8 @@ static int rcar_can_open(struct net_device *ndev)
 out_close:
        napi_disable(&priv->napi);
        close_candev(ndev);
+out_can_clock:
+       clk_disable_unprepare(priv->can_clk);
 out_clock:
        clk_disable_unprepare(priv->clk);
 out:
@@ -565,6 +574,7 @@ static int rcar_can_close(struct net_device *ndev)
        rcar_can_stop(ndev);
        free_irq(ndev->irq, ndev);
        napi_disable(&priv->napi);
+       clk_disable_unprepare(priv->can_clk);
        clk_disable_unprepare(priv->clk);
        close_candev(ndev);
        can_led_event(ndev, CAN_LED_EVENT_STOP);
@@ -715,6 +725,12 @@ static int rcar_can_get_berr_counter(const struct net_device *dev,
        return 0;
 }
 
+static const char * const clock_names[] = {
+       [CLKR_CLKP1]    = "clkp1",
+       [CLKR_CLKP2]    = "clkp2",
+       [CLKR_CLKEXT]   = "can_clk",
+};
+
 static int rcar_can_probe(struct platform_device *pdev)
 {
        struct rcar_can_platform_data *pdata;
@@ -722,6 +738,7 @@ static int rcar_can_probe(struct platform_device *pdev)
        struct net_device *ndev;
        struct resource *mem;
        void __iomem *addr;
+       u32 clock_select;
        int err = -ENODEV;
        int irq;
 
@@ -730,6 +747,7 @@ static int rcar_can_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "No platform data provided!\n");
                goto fail;
        }
+       clock_select = pdata->clock_select;
 
        irq = platform_get_irq(pdev, 0);
        if (!irq) {
@@ -753,10 +771,22 @@ static int rcar_can_probe(struct platform_device *pdev)
 
        priv = netdev_priv(ndev);
 
-       priv->clk = devm_clk_get(&pdev->dev, NULL);
+       priv->clk = devm_clk_get(&pdev->dev, "clkp1");
        if (IS_ERR(priv->clk)) {
                err = PTR_ERR(priv->clk);
-               dev_err(&pdev->dev, "cannot get clock: %d\n", err);
+               dev_err(&pdev->dev, "cannot get peripheral clock: %d\n", err);
+               goto fail_clk;
+       }
+
+       if (clock_select >= ARRAY_SIZE(clock_names)) {
+               err = -EINVAL;
+               dev_err(&pdev->dev, "invalid CAN clock selected\n");
+               goto fail_clk;
+       }
+       priv->can_clk = devm_clk_get(&pdev->dev, clock_names[clock_select]);
+       if (IS_ERR(priv->can_clk)) {
+               err = PTR_ERR(priv->can_clk);
+               dev_err(&pdev->dev, "cannot get CAN clock: %d\n", err);
                goto fail_clk;
        }
 
@@ -765,8 +795,8 @@ static int rcar_can_probe(struct platform_device *pdev)
        ndev->flags |= IFF_ECHO;
        priv->ndev = ndev;
        priv->regs = addr;
-       priv->clock_select = pdata->clock_select;
-       priv->can.clock.freq = clk_get_rate(priv->clk);
+       priv->clock_select = clock_select;
+       priv->can.clock.freq = clk_get_rate(priv->can_clk);
        priv->can.bittiming_const = &rcar_can_bittiming_const;
        priv->can.do_set_mode = rcar_can_do_set_mode;
        priv->can.do_get_berr_counter = rcar_can_get_berr_counter;