diff options
author | AnilKumar Ch <anilkumar@ti.com> | 2012-08-20 07:20:54 -0400 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2012-09-21 17:58:47 -0400 |
commit | 4cdd34b26826e89972c03043987b83f76e7ad510 (patch) | |
tree | 634e385dc93e91b97b12f8e71aec1baaa6db4fdb /drivers/net/can | |
parent | 2469627d175c1d6d7812a1395dd3ef053a0e65b3 (diff) |
can: c_can: Add runtime PM support to Bosch C_CAN/D_CAN controller
Add Runtime PM support to C_CAN/D_CAN controller. The runtime PM
APIs control clocks for C_CAN/D_CAN IP and prevent access to the
register of C_CAN/D_CAN IP when clock is turned off.
Signed-off-by: AnilKumar Ch <anilkumar@ti.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Diffstat (limited to 'drivers/net/can')
-rw-r--r-- | drivers/net/can/c_can/c_can.c | 49 | ||||
-rw-r--r-- | drivers/net/can/c_can/c_can.h | 1 | ||||
-rw-r--r-- | drivers/net/can/c_can/c_can_platform.c | 1 |
3 files changed, 49 insertions, 2 deletions
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index 4c538e388655..768bb481b077 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/if_ether.h> | 34 | #include <linux/if_ether.h> |
35 | #include <linux/list.h> | 35 | #include <linux/list.h> |
36 | #include <linux/io.h> | 36 | #include <linux/io.h> |
37 | #include <linux/pm_runtime.h> | ||
37 | 38 | ||
38 | #include <linux/can.h> | 39 | #include <linux/can.h> |
39 | #include <linux/can/dev.h> | 40 | #include <linux/can/dev.h> |
@@ -201,6 +202,30 @@ static const struct can_bittiming_const c_can_bittiming_const = { | |||
201 | .brp_inc = 1, | 202 | .brp_inc = 1, |
202 | }; | 203 | }; |
203 | 204 | ||
205 | static inline void c_can_pm_runtime_enable(const struct c_can_priv *priv) | ||
206 | { | ||
207 | if (priv->device) | ||
208 | pm_runtime_enable(priv->device); | ||
209 | } | ||
210 | |||
211 | static inline void c_can_pm_runtime_disable(const struct c_can_priv *priv) | ||
212 | { | ||
213 | if (priv->device) | ||
214 | pm_runtime_disable(priv->device); | ||
215 | } | ||
216 | |||
217 | static inline void c_can_pm_runtime_get_sync(const struct c_can_priv *priv) | ||
218 | { | ||
219 | if (priv->device) | ||
220 | pm_runtime_get_sync(priv->device); | ||
221 | } | ||
222 | |||
223 | static inline void c_can_pm_runtime_put_sync(const struct c_can_priv *priv) | ||
224 | { | ||
225 | if (priv->device) | ||
226 | pm_runtime_put_sync(priv->device); | ||
227 | } | ||
228 | |||
204 | static inline int get_tx_next_msg_obj(const struct c_can_priv *priv) | 229 | static inline int get_tx_next_msg_obj(const struct c_can_priv *priv) |
205 | { | 230 | { |
206 | return (priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) + | 231 | return (priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) + |
@@ -673,11 +698,15 @@ static int c_can_get_berr_counter(const struct net_device *dev, | |||
673 | unsigned int reg_err_counter; | 698 | unsigned int reg_err_counter; |
674 | struct c_can_priv *priv = netdev_priv(dev); | 699 | struct c_can_priv *priv = netdev_priv(dev); |
675 | 700 | ||
701 | c_can_pm_runtime_get_sync(priv); | ||
702 | |||
676 | reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); | 703 | reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); |
677 | bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> | 704 | bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> |
678 | ERR_CNT_REC_SHIFT; | 705 | ERR_CNT_REC_SHIFT; |
679 | bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK; | 706 | bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK; |
680 | 707 | ||
708 | c_can_pm_runtime_put_sync(priv); | ||
709 | |||
681 | return 0; | 710 | return 0; |
682 | } | 711 | } |
683 | 712 | ||
@@ -1053,11 +1082,13 @@ static int c_can_open(struct net_device *dev) | |||
1053 | int err; | 1082 | int err; |
1054 | struct c_can_priv *priv = netdev_priv(dev); | 1083 | struct c_can_priv *priv = netdev_priv(dev); |
1055 | 1084 | ||
1085 | c_can_pm_runtime_get_sync(priv); | ||
1086 | |||
1056 | /* open the can device */ | 1087 | /* open the can device */ |
1057 | err = open_candev(dev); | 1088 | err = open_candev(dev); |
1058 | if (err) { | 1089 | if (err) { |
1059 | netdev_err(dev, "failed to open can device\n"); | 1090 | netdev_err(dev, "failed to open can device\n"); |
1060 | return err; | 1091 | goto exit_open_fail; |
1061 | } | 1092 | } |
1062 | 1093 | ||
1063 | /* register interrupt handler */ | 1094 | /* register interrupt handler */ |
@@ -1079,6 +1110,8 @@ static int c_can_open(struct net_device *dev) | |||
1079 | 1110 | ||
1080 | exit_irq_fail: | 1111 | exit_irq_fail: |
1081 | close_candev(dev); | 1112 | close_candev(dev); |
1113 | exit_open_fail: | ||
1114 | c_can_pm_runtime_put_sync(priv); | ||
1082 | return err; | 1115 | return err; |
1083 | } | 1116 | } |
1084 | 1117 | ||
@@ -1091,6 +1124,7 @@ static int c_can_close(struct net_device *dev) | |||
1091 | c_can_stop(dev); | 1124 | c_can_stop(dev); |
1092 | free_irq(dev->irq, dev); | 1125 | free_irq(dev->irq, dev); |
1093 | close_candev(dev); | 1126 | close_candev(dev); |
1127 | c_can_pm_runtime_put_sync(priv); | ||
1094 | 1128 | ||
1095 | return 0; | 1129 | return 0; |
1096 | } | 1130 | } |
@@ -1133,10 +1167,19 @@ static const struct net_device_ops c_can_netdev_ops = { | |||
1133 | 1167 | ||
1134 | int register_c_can_dev(struct net_device *dev) | 1168 | int register_c_can_dev(struct net_device *dev) |
1135 | { | 1169 | { |
1170 | struct c_can_priv *priv = netdev_priv(dev); | ||
1171 | int err; | ||
1172 | |||
1173 | c_can_pm_runtime_enable(priv); | ||
1174 | |||
1136 | dev->flags |= IFF_ECHO; /* we support local echo */ | 1175 | dev->flags |= IFF_ECHO; /* we support local echo */ |
1137 | dev->netdev_ops = &c_can_netdev_ops; | 1176 | dev->netdev_ops = &c_can_netdev_ops; |
1138 | 1177 | ||
1139 | return register_candev(dev); | 1178 | err = register_candev(dev); |
1179 | if (err) | ||
1180 | c_can_pm_runtime_disable(priv); | ||
1181 | |||
1182 | return err; | ||
1140 | } | 1183 | } |
1141 | EXPORT_SYMBOL_GPL(register_c_can_dev); | 1184 | EXPORT_SYMBOL_GPL(register_c_can_dev); |
1142 | 1185 | ||
@@ -1148,6 +1191,8 @@ void unregister_c_can_dev(struct net_device *dev) | |||
1148 | c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); | 1191 | c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); |
1149 | 1192 | ||
1150 | unregister_candev(dev); | 1193 | unregister_candev(dev); |
1194 | |||
1195 | c_can_pm_runtime_disable(priv); | ||
1151 | } | 1196 | } |
1152 | EXPORT_SYMBOL_GPL(unregister_c_can_dev); | 1197 | EXPORT_SYMBOL_GPL(unregister_c_can_dev); |
1153 | 1198 | ||
diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h index 4e56baa9c2da..1437a6dbdca5 100644 --- a/drivers/net/can/c_can/c_can.h +++ b/drivers/net/can/c_can/c_can.h | |||
@@ -153,6 +153,7 @@ struct c_can_priv { | |||
153 | struct can_priv can; /* must be the first member */ | 153 | struct can_priv can; /* must be the first member */ |
154 | struct napi_struct napi; | 154 | struct napi_struct napi; |
155 | struct net_device *dev; | 155 | struct net_device *dev; |
156 | struct device *device; | ||
156 | int tx_object; | 157 | int tx_object; |
157 | int current_status; | 158 | int current_status; |
158 | int last_status; | 159 | int last_status; |
diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index d0a66cf298ba..7b8b7a485d72 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c | |||
@@ -179,6 +179,7 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) | |||
179 | 179 | ||
180 | dev->irq = irq; | 180 | dev->irq = irq; |
181 | priv->base = addr; | 181 | priv->base = addr; |
182 | priv->device = &pdev->dev; | ||
182 | priv->can.clock.freq = clk_get_rate(clk); | 183 | priv->can.clock.freq = clk_get_rate(clk); |
183 | priv->priv = clk; | 184 | priv->priv = clk; |
184 | 185 | ||