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>
This commit is contained in:
		
					parent
					
						
							
								2469627d17
							
						
					
				
			
			
				commit
				
					
						4cdd34b268
					
				
			
		
					 3 changed files with 49 additions and 2 deletions
				
			
		|  | @ -34,6 +34,7 @@ | ||||||
| #include <linux/if_ether.h> | #include <linux/if_ether.h> | ||||||
| #include <linux/list.h> | #include <linux/list.h> | ||||||
| #include <linux/io.h> | #include <linux/io.h> | ||||||
|  | #include <linux/pm_runtime.h> | ||||||
| 
 | 
 | ||||||
| #include <linux/can.h> | #include <linux/can.h> | ||||||
| #include <linux/can/dev.h> | #include <linux/can/dev.h> | ||||||
|  | @ -201,6 +202,30 @@ static const struct can_bittiming_const c_can_bittiming_const = { | ||||||
| 	.brp_inc = 1, | 	.brp_inc = 1, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static inline void c_can_pm_runtime_enable(const struct c_can_priv *priv) | ||||||
|  | { | ||||||
|  | 	if (priv->device) | ||||||
|  | 		pm_runtime_enable(priv->device); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void c_can_pm_runtime_disable(const struct c_can_priv *priv) | ||||||
|  | { | ||||||
|  | 	if (priv->device) | ||||||
|  | 		pm_runtime_disable(priv->device); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void c_can_pm_runtime_get_sync(const struct c_can_priv *priv) | ||||||
|  | { | ||||||
|  | 	if (priv->device) | ||||||
|  | 		pm_runtime_get_sync(priv->device); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void c_can_pm_runtime_put_sync(const struct c_can_priv *priv) | ||||||
|  | { | ||||||
|  | 	if (priv->device) | ||||||
|  | 		pm_runtime_put_sync(priv->device); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline int get_tx_next_msg_obj(const struct c_can_priv *priv) | static inline int get_tx_next_msg_obj(const struct c_can_priv *priv) | ||||||
| { | { | ||||||
| 	return (priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) + | 	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, | ||||||
| 	unsigned int reg_err_counter; | 	unsigned int reg_err_counter; | ||||||
| 	struct c_can_priv *priv = netdev_priv(dev); | 	struct c_can_priv *priv = netdev_priv(dev); | ||||||
| 
 | 
 | ||||||
|  | 	c_can_pm_runtime_get_sync(priv); | ||||||
|  | 
 | ||||||
| 	reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); | 	reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); | ||||||
| 	bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> | 	bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> | ||||||
| 				ERR_CNT_REC_SHIFT; | 				ERR_CNT_REC_SHIFT; | ||||||
| 	bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK; | 	bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK; | ||||||
| 
 | 
 | ||||||
|  | 	c_can_pm_runtime_put_sync(priv); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1053,11 +1082,13 @@ static int c_can_open(struct net_device *dev) | ||||||
| 	int err; | 	int err; | ||||||
| 	struct c_can_priv *priv = netdev_priv(dev); | 	struct c_can_priv *priv = netdev_priv(dev); | ||||||
| 
 | 
 | ||||||
|  | 	c_can_pm_runtime_get_sync(priv); | ||||||
|  | 
 | ||||||
| 	/* open the can device */ | 	/* open the can device */ | ||||||
| 	err = open_candev(dev); | 	err = open_candev(dev); | ||||||
| 	if (err) { | 	if (err) { | ||||||
| 		netdev_err(dev, "failed to open can device\n"); | 		netdev_err(dev, "failed to open can device\n"); | ||||||
| 		return err; | 		goto exit_open_fail; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* register interrupt handler */ | 	/* register interrupt handler */ | ||||||
|  | @ -1079,6 +1110,8 @@ static int c_can_open(struct net_device *dev) | ||||||
| 
 | 
 | ||||||
| exit_irq_fail: | exit_irq_fail: | ||||||
| 	close_candev(dev); | 	close_candev(dev); | ||||||
|  | exit_open_fail: | ||||||
|  | 	c_can_pm_runtime_put_sync(priv); | ||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1091,6 +1124,7 @@ static int c_can_close(struct net_device *dev) | ||||||
| 	c_can_stop(dev); | 	c_can_stop(dev); | ||||||
| 	free_irq(dev->irq, dev); | 	free_irq(dev->irq, dev); | ||||||
| 	close_candev(dev); | 	close_candev(dev); | ||||||
|  | 	c_can_pm_runtime_put_sync(priv); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | @ -1133,10 +1167,19 @@ static const struct net_device_ops c_can_netdev_ops = { | ||||||
| 
 | 
 | ||||||
| int register_c_can_dev(struct net_device *dev) | int register_c_can_dev(struct net_device *dev) | ||||||
| { | { | ||||||
|  | 	struct c_can_priv *priv = netdev_priv(dev); | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	c_can_pm_runtime_enable(priv); | ||||||
|  | 
 | ||||||
| 	dev->flags |= IFF_ECHO;	/* we support local echo */ | 	dev->flags |= IFF_ECHO;	/* we support local echo */ | ||||||
| 	dev->netdev_ops = &c_can_netdev_ops; | 	dev->netdev_ops = &c_can_netdev_ops; | ||||||
| 
 | 
 | ||||||
| 	return register_candev(dev); | 	err = register_candev(dev); | ||||||
|  | 	if (err) | ||||||
|  | 		c_can_pm_runtime_disable(priv); | ||||||
|  | 
 | ||||||
|  | 	return err; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(register_c_can_dev); | EXPORT_SYMBOL_GPL(register_c_can_dev); | ||||||
| 
 | 
 | ||||||
|  | @ -1148,6 +1191,8 @@ void unregister_c_can_dev(struct net_device *dev) | ||||||
| 	c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); | 	c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); | ||||||
| 
 | 
 | ||||||
| 	unregister_candev(dev); | 	unregister_candev(dev); | ||||||
|  | 
 | ||||||
|  | 	c_can_pm_runtime_disable(priv); | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(unregister_c_can_dev); | EXPORT_SYMBOL_GPL(unregister_c_can_dev); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -153,6 +153,7 @@ struct c_can_priv { | ||||||
| 	struct can_priv can;	/* must be the first member */ | 	struct can_priv can;	/* must be the first member */ | ||||||
| 	struct napi_struct napi; | 	struct napi_struct napi; | ||||||
| 	struct net_device *dev; | 	struct net_device *dev; | ||||||
|  | 	struct device *device; | ||||||
| 	int tx_object; | 	int tx_object; | ||||||
| 	int current_status; | 	int current_status; | ||||||
| 	int last_status; | 	int last_status; | ||||||
|  |  | ||||||
|  | @ -179,6 +179,7 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) | ||||||
| 
 | 
 | ||||||
| 	dev->irq = irq; | 	dev->irq = irq; | ||||||
| 	priv->base = addr; | 	priv->base = addr; | ||||||
|  | 	priv->device = &pdev->dev; | ||||||
| 	priv->can.clock.freq = clk_get_rate(clk); | 	priv->can.clock.freq = clk_get_rate(clk); | ||||||
| 	priv->priv = clk; | 	priv->priv = clk; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 AnilKumar Ch
				AnilKumar Ch