diff options
| author | Alexander Shiyan <shc_work@mail.ru> | 2013-12-21 00:01:41 -0500 |
|---|---|---|
| committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2013-12-21 09:04:24 -0500 |
| commit | 66606aafd8cd6cffbefa463b27e1f16e793e40a9 (patch) | |
| tree | d4b9c8e4d0fdfe8fb99306a73747d287f0e34985 | |
| parent | c0ebbdd6b57949775d428f00b8696a4078f86ac4 (diff) | |
can: mcp251x: Add device tree support
This patch adds Device Tree support to the Microchip MCP251X driver.
Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
| -rw-r--r-- | Documentation/devicetree/bindings/net/can/microchip,mcp251x.txt | 25 | ||||
| -rw-r--r-- | drivers/net/can/mcp251x.c | 99 |
2 files changed, 99 insertions, 25 deletions
diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp251x.txt b/Documentation/devicetree/bindings/net/can/microchip,mcp251x.txt new file mode 100644 index 000000000000..ee3723beb701 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/microchip,mcp251x.txt | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | * Microchip MCP251X stand-alone CAN controller device tree bindings | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: Should be one of the following: | ||
| 5 | - "microchip,mcp2510" for MCP2510. | ||
| 6 | - "microchip,mcp2515" for MCP2515. | ||
| 7 | - reg: SPI chip select. | ||
| 8 | - clocks: The clock feeding the CAN controller. | ||
| 9 | - interrupt-parent: The parent interrupt controller. | ||
| 10 | - interrupts: Should contain IRQ line for the CAN controller. | ||
| 11 | |||
| 12 | Optional properties: | ||
| 13 | - vdd-supply: Regulator that powers the CAN controller. | ||
| 14 | - xceiver-supply: Regulator that powers the CAN transceiver. | ||
| 15 | |||
| 16 | Example: | ||
| 17 | can0: can@1 { | ||
| 18 | compatible = "microchip,mcp2515"; | ||
| 19 | reg = <1>; | ||
| 20 | clocks = <&clk24m>; | ||
| 21 | interrupt-parent = <&gpio4>; | ||
| 22 | interrupts = <13 0x2>; | ||
| 23 | vdd-supply = <®5v0>; | ||
| 24 | xceiver-supply = <®5v0>; | ||
| 25 | }; | ||
diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index 88d3877b6277..cdb9808d12db 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c | |||
| @@ -58,6 +58,7 @@ | |||
| 58 | #include <linux/can/dev.h> | 58 | #include <linux/can/dev.h> |
| 59 | #include <linux/can/led.h> | 59 | #include <linux/can/led.h> |
| 60 | #include <linux/can/platform/mcp251x.h> | 60 | #include <linux/can/platform/mcp251x.h> |
| 61 | #include <linux/clk.h> | ||
| 61 | #include <linux/completion.h> | 62 | #include <linux/completion.h> |
| 62 | #include <linux/delay.h> | 63 | #include <linux/delay.h> |
| 63 | #include <linux/device.h> | 64 | #include <linux/device.h> |
| @@ -68,6 +69,8 @@ | |||
| 68 | #include <linux/kernel.h> | 69 | #include <linux/kernel.h> |
| 69 | #include <linux/module.h> | 70 | #include <linux/module.h> |
| 70 | #include <linux/netdevice.h> | 71 | #include <linux/netdevice.h> |
| 72 | #include <linux/of.h> | ||
| 73 | #include <linux/of_device.h> | ||
| 71 | #include <linux/platform_device.h> | 74 | #include <linux/platform_device.h> |
| 72 | #include <linux/slab.h> | 75 | #include <linux/slab.h> |
| 73 | #include <linux/spi/spi.h> | 76 | #include <linux/spi/spi.h> |
| @@ -263,6 +266,7 @@ struct mcp251x_priv { | |||
| 263 | int restart_tx; | 266 | int restart_tx; |
| 264 | struct regulator *power; | 267 | struct regulator *power; |
| 265 | struct regulator *transceiver; | 268 | struct regulator *transceiver; |
| 269 | struct clk *clk; | ||
| 266 | }; | 270 | }; |
| 267 | 271 | ||
| 268 | #define MCP251X_IS(_model) \ | 272 | #define MCP251X_IS(_model) \ |
| @@ -994,22 +998,65 @@ static const struct net_device_ops mcp251x_netdev_ops = { | |||
| 994 | .ndo_start_xmit = mcp251x_hard_start_xmit, | 998 | .ndo_start_xmit = mcp251x_hard_start_xmit, |
| 995 | }; | 999 | }; |
| 996 | 1000 | ||
| 1001 | static const struct of_device_id mcp251x_of_match[] = { | ||
| 1002 | { | ||
| 1003 | .compatible = "microchip,mcp2510", | ||
| 1004 | .data = (void *)CAN_MCP251X_MCP2510, | ||
| 1005 | }, | ||
| 1006 | { | ||
| 1007 | .compatible = "microchip,mcp2515", | ||
| 1008 | .data = (void *)CAN_MCP251X_MCP2515, | ||
| 1009 | }, | ||
| 1010 | { } | ||
| 1011 | }; | ||
| 1012 | MODULE_DEVICE_TABLE(of, mcp251x_of_match); | ||
| 1013 | |||
| 1014 | static const struct spi_device_id mcp251x_id_table[] = { | ||
| 1015 | { | ||
| 1016 | .name = "mcp2510", | ||
| 1017 | .driver_data = (kernel_ulong_t)CAN_MCP251X_MCP2510, | ||
| 1018 | }, | ||
| 1019 | { | ||
| 1020 | .name = "mcp2515", | ||
| 1021 | .driver_data = (kernel_ulong_t)CAN_MCP251X_MCP2515, | ||
| 1022 | }, | ||
| 1023 | { } | ||
| 1024 | }; | ||
| 1025 | MODULE_DEVICE_TABLE(spi, mcp251x_id_table); | ||
| 1026 | |||
| 997 | static int mcp251x_can_probe(struct spi_device *spi) | 1027 | static int mcp251x_can_probe(struct spi_device *spi) |
| 998 | { | 1028 | { |
| 1029 | const struct of_device_id *of_id = of_match_device(mcp251x_of_match, | ||
| 1030 | &spi->dev); | ||
| 1031 | struct mcp251x_platform_data *pdata = dev_get_platdata(&spi->dev); | ||
| 999 | struct net_device *net; | 1032 | struct net_device *net; |
| 1000 | struct mcp251x_priv *priv; | 1033 | struct mcp251x_priv *priv; |
| 1001 | struct mcp251x_platform_data *pdata = dev_get_platdata(&spi->dev); | 1034 | int freq, ret = -ENODEV; |
| 1002 | int ret = -ENODEV; | 1035 | struct clk *clk; |
| 1036 | |||
| 1037 | clk = devm_clk_get(&spi->dev, NULL); | ||
| 1038 | if (IS_ERR(clk)) { | ||
| 1039 | if (pdata) | ||
| 1040 | freq = pdata->oscillator_frequency; | ||
| 1041 | else | ||
| 1042 | return PTR_ERR(clk); | ||
| 1043 | } else { | ||
| 1044 | freq = clk_get_rate(clk); | ||
| 1045 | } | ||
| 1003 | 1046 | ||
| 1004 | if (!pdata) | 1047 | /* Sanity check */ |
| 1005 | /* Platform data is required for osc freq */ | 1048 | if (freq < 1000000 || freq > 25000000) |
| 1006 | goto error_out; | 1049 | return -ERANGE; |
| 1007 | 1050 | ||
| 1008 | /* Allocate can/net device */ | 1051 | /* Allocate can/net device */ |
| 1009 | net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX); | 1052 | net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX); |
| 1010 | if (!net) { | 1053 | if (!net) |
| 1011 | ret = -ENOMEM; | 1054 | return -ENOMEM; |
| 1012 | goto error_alloc; | 1055 | |
| 1056 | if (!IS_ERR(clk)) { | ||
| 1057 | ret = clk_prepare_enable(clk); | ||
| 1058 | if (ret) | ||
| 1059 | goto out_free; | ||
| 1013 | } | 1060 | } |
| 1014 | 1061 | ||
| 1015 | net->netdev_ops = &mcp251x_netdev_ops; | 1062 | net->netdev_ops = &mcp251x_netdev_ops; |
| @@ -1018,23 +1065,27 @@ static int mcp251x_can_probe(struct spi_device *spi) | |||
| 1018 | priv = netdev_priv(net); | 1065 | priv = netdev_priv(net); |
| 1019 | priv->can.bittiming_const = &mcp251x_bittiming_const; | 1066 | priv->can.bittiming_const = &mcp251x_bittiming_const; |
| 1020 | priv->can.do_set_mode = mcp251x_do_set_mode; | 1067 | priv->can.do_set_mode = mcp251x_do_set_mode; |
| 1021 | priv->can.clock.freq = pdata->oscillator_frequency / 2; | 1068 | priv->can.clock.freq = freq / 2; |
| 1022 | priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | | 1069 | priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | |
| 1023 | CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; | 1070 | CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; |
| 1024 | priv->model = spi_get_device_id(spi)->driver_data; | 1071 | if (of_id) |
| 1072 | priv->model = (enum mcp251x_model)of_id->data; | ||
| 1073 | else | ||
| 1074 | priv->model = spi_get_device_id(spi)->driver_data; | ||
| 1025 | priv->net = net; | 1075 | priv->net = net; |
| 1076 | priv->clk = clk; | ||
| 1026 | 1077 | ||
| 1027 | priv->power = devm_regulator_get(&spi->dev, "vdd"); | 1078 | priv->power = devm_regulator_get(&spi->dev, "vdd"); |
| 1028 | priv->transceiver = devm_regulator_get(&spi->dev, "xceiver"); | 1079 | priv->transceiver = devm_regulator_get(&spi->dev, "xceiver"); |
| 1029 | if ((PTR_ERR(priv->power) == -EPROBE_DEFER) || | 1080 | if ((PTR_ERR(priv->power) == -EPROBE_DEFER) || |
| 1030 | (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) { | 1081 | (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) { |
| 1031 | ret = -EPROBE_DEFER; | 1082 | ret = -EPROBE_DEFER; |
| 1032 | goto error_power; | 1083 | goto out_clk; |
| 1033 | } | 1084 | } |
| 1034 | 1085 | ||
| 1035 | ret = mcp251x_power_enable(priv->power, 1); | 1086 | ret = mcp251x_power_enable(priv->power, 1); |
| 1036 | if (ret) | 1087 | if (ret) |
| 1037 | goto error_power; | 1088 | goto out_clk; |
| 1038 | 1089 | ||
| 1039 | spi_set_drvdata(spi, priv); | 1090 | spi_set_drvdata(spi, priv); |
| 1040 | 1091 | ||
| @@ -1113,11 +1164,14 @@ error_probe: | |||
| 1113 | dma_free_coherent(&spi->dev, PAGE_SIZE, | 1164 | dma_free_coherent(&spi->dev, PAGE_SIZE, |
| 1114 | priv->spi_tx_buf, priv->spi_tx_dma); | 1165 | priv->spi_tx_buf, priv->spi_tx_dma); |
| 1115 | mcp251x_power_enable(priv->power, 0); | 1166 | mcp251x_power_enable(priv->power, 0); |
| 1116 | error_power: | 1167 | |
| 1168 | out_clk: | ||
