Skip to content

Commit

Permalink
can: c_can: Add d_can suspend resume support
Browse files Browse the repository at this point in the history
Adds suspend resume support to DCAN driver which enables
DCAN power down mode bit (PDR). Then DCAN will ack the local
power-down mode by setting PDA bit in STATUS register.

Signed-off-by: AnilKumar Ch <anilkumar@ti.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
  • Loading branch information
AnilKumar Ch authored and Marc Kleine-Budde committed Sep 21, 2012
1 parent 4cdd34b commit 8212003
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 0 deletions.
78 changes: 78 additions & 0 deletions drivers/net/can/c_can/c_can.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
#define IF_ENUM_REG_LEN 11
#define C_CAN_IFACE(reg, iface) (C_CAN_IF1_##reg + (iface) * IF_ENUM_REG_LEN)

/* control extension register D_CAN specific */
#define CONTROL_EX_PDR BIT(8)

/* control register */
#define CONTROL_TEST BIT(7)
#define CONTROL_CCE BIT(6)
Expand All @@ -65,6 +68,7 @@
#define TEST_BASIC BIT(2)

/* status register */
#define STATUS_PDA BIT(10)
#define STATUS_BOFF BIT(7)
#define STATUS_EWARN BIT(6)
#define STATUS_EPASS BIT(5)
Expand Down Expand Up @@ -164,6 +168,9 @@
/* minimum timeout for checking BUSY status */
#define MIN_TIMEOUT_VALUE 6

/* Wait for ~1 sec for INIT bit */
#define INIT_WAIT_MS 1000

/* napi related */
#define C_CAN_NAPI_WEIGHT C_CAN_MSG_OBJ_RX_NUM

Expand Down Expand Up @@ -1153,6 +1160,77 @@ struct net_device *alloc_c_can_dev(void)
}
EXPORT_SYMBOL_GPL(alloc_c_can_dev);

#ifdef CONFIG_PM
int c_can_power_down(struct net_device *dev)
{
u32 val;
unsigned long time_out;
struct c_can_priv *priv = netdev_priv(dev);

if (!(dev->flags & IFF_UP))
return 0;

WARN_ON(priv->type != BOSCH_D_CAN);

/* set PDR value so the device goes to power down mode */
val = priv->read_reg(priv, C_CAN_CTRL_EX_REG);
val |= CONTROL_EX_PDR;
priv->write_reg(priv, C_CAN_CTRL_EX_REG, val);

/* Wait for the PDA bit to get set */
time_out = jiffies + msecs_to_jiffies(INIT_WAIT_MS);
while (!(priv->read_reg(priv, C_CAN_STS_REG) & STATUS_PDA) &&
time_after(time_out, jiffies))
cpu_relax();

if (time_after(jiffies, time_out))
return -ETIMEDOUT;

c_can_stop(dev);

c_can_pm_runtime_put_sync(priv);

return 0;
}
EXPORT_SYMBOL_GPL(c_can_power_down);

int c_can_power_up(struct net_device *dev)
{
u32 val;
unsigned long time_out;
struct c_can_priv *priv = netdev_priv(dev);

if (!(dev->flags & IFF_UP))
return 0;

WARN_ON(priv->type != BOSCH_D_CAN);

c_can_pm_runtime_get_sync(priv);

/* Clear PDR and INIT bits */
val = priv->read_reg(priv, C_CAN_CTRL_EX_REG);
val &= ~CONTROL_EX_PDR;
priv->write_reg(priv, C_CAN_CTRL_EX_REG, val);
val = priv->read_reg(priv, C_CAN_CTRL_REG);
val &= ~CONTROL_INIT;
priv->write_reg(priv, C_CAN_CTRL_REG, val);

/* Wait for the PDA bit to get clear */
time_out = jiffies + msecs_to_jiffies(INIT_WAIT_MS);
while ((priv->read_reg(priv, C_CAN_STS_REG) & STATUS_PDA) &&
time_after(time_out, jiffies))
cpu_relax();

if (time_after(jiffies, time_out))
return -ETIMEDOUT;

c_can_start(dev);

return 0;
}
EXPORT_SYMBOL_GPL(c_can_power_up);
#endif

void free_c_can_dev(struct net_device *dev)
{
free_candev(dev);
Expand Down
8 changes: 8 additions & 0 deletions drivers/net/can/c_can/c_can.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

enum reg {
C_CAN_CTRL_REG = 0,
C_CAN_CTRL_EX_REG,
C_CAN_STS_REG,
C_CAN_ERR_CNT_REG,
C_CAN_BTR_REG,
Expand Down Expand Up @@ -104,6 +105,7 @@ static const u16 reg_map_c_can[] = {

static const u16 reg_map_d_can[] = {
[C_CAN_CTRL_REG] = 0x00,
[C_CAN_CTRL_EX_REG] = 0x02,
[C_CAN_STS_REG] = 0x04,
[C_CAN_ERR_CNT_REG] = 0x08,
[C_CAN_BTR_REG] = 0x0C,
Expand Down Expand Up @@ -166,11 +168,17 @@ struct c_can_priv {
unsigned int tx_echo;
void *priv; /* for board-specific data */
u16 irqstatus;
enum c_can_dev_id type;
};

struct net_device *alloc_c_can_dev(void);
void free_c_can_dev(struct net_device *dev);
int register_c_can_dev(struct net_device *dev);
void unregister_c_can_dev(struct net_device *dev);

#ifdef CONFIG_PM
int c_can_power_up(struct net_device *dev);
int c_can_power_down(struct net_device *dev);
#endif

#endif /* C_CAN_H */
62 changes: 62 additions & 0 deletions drivers/net/can/c_can/c_can_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
priv->device = &pdev->dev;
priv->can.clock.freq = clk_get_rate(clk);
priv->priv = clk;
priv->type = id->driver_data;

platform_set_drvdata(pdev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);
Expand Down Expand Up @@ -232,6 +233,65 @@ static int __devexit c_can_plat_remove(struct platform_device *pdev)
return 0;
}

#ifdef CONFIG_PM
static int c_can_suspend(struct platform_device *pdev, pm_message_t state)
{
int ret;
struct net_device *ndev = platform_get_drvdata(pdev);
struct c_can_priv *priv = netdev_priv(ndev);

if (priv->type != BOSCH_D_CAN) {
dev_warn(&pdev->dev, "Not supported\n");
return 0;
}

if (netif_running(ndev)) {
netif_stop_queue(ndev);
netif_device_detach(ndev);
}

ret = c_can_power_down(ndev);
if (ret) {
netdev_err(ndev, "failed to enter power down mode\n");
return ret;
}

priv->can.state = CAN_STATE_SLEEPING;

return 0;
}

static int c_can_resume(struct platform_device *pdev)
{
int ret;
struct net_device *ndev = platform_get_drvdata(pdev);
struct c_can_priv *priv = netdev_priv(ndev);

if (priv->type != BOSCH_D_CAN) {
dev_warn(&pdev->dev, "Not supported\n");
return 0;
}

ret = c_can_power_up(ndev);
if (ret) {
netdev_err(ndev, "Still in power down mode\n");
return ret;
}

priv->can.state = CAN_STATE_ERROR_ACTIVE;

if (netif_running(ndev)) {
netif_device_attach(ndev);
netif_start_queue(ndev);
}

return 0;
}
#else
#define c_can_suspend NULL
#define c_can_resume NULL
#endif

static struct platform_driver c_can_plat_driver = {
.driver = {
.name = KBUILD_MODNAME,
Expand All @@ -240,6 +300,8 @@ static struct platform_driver c_can_plat_driver = {
},
.probe = c_can_plat_probe,
.remove = __devexit_p(c_can_plat_remove),
.suspend = c_can_suspend,
.resume = c_can_resume,
.id_table = c_can_id_table,
};

Expand Down

0 comments on commit 8212003

Please sign in to comment.