Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 328050
b: refs/heads/master
c: 717ecc2
h: refs/heads/master
v: v3
  • Loading branch information
David S. Miller committed Sep 22, 2012
1 parent 1c8a76b commit ddb7fe9
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 41 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: c9f14bf3a49f86e6402a6e3476a180f2bdc8a71b
refs/heads/master: 717ecc276dadf31cd858d4e56ac71c3cc479fc19
49 changes: 49 additions & 0 deletions trunk/Documentation/devicetree/bindings/net/can/c_can.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
Bosch C_CAN/D_CAN controller Device Tree Bindings
-------------------------------------------------

Required properties:
- compatible : Should be "bosch,c_can" for C_CAN controllers and
"bosch,d_can" for D_CAN controllers.
- reg : physical base address and size of the C_CAN/D_CAN
registers map
- interrupts : property with a value describing the interrupt
number

Optional properties:
- ti,hwmods : Must be "d_can<n>" or "c_can<n>", n being the
instance number

Note: "ti,hwmods" field is used to fetch the base address and irq
resources from TI, omap hwmod data base during device registration.
Future plan is to migrate hwmod data base contents into device tree
blob so that, all the required data will be used from device tree dts
file.

Example:

Step1: SoC common .dtsi file

dcan1: d_can@481d0000 {
compatible = "bosch,d_can";
reg = <0x481d0000 0x2000>;
interrupts = <55>;
interrupt-parent = <&intc>;
status = "disabled";
};

(or)

dcan1: d_can@481d0000 {
compatible = "bosch,d_can";
ti,hwmods = "d_can1";
reg = <0x481d0000 0x2000>;
interrupts = <55>;
interrupt-parent = <&intc>;
status = "disabled";
};

Step 2: board specific .dts file

&dcan1 {
status = "okay";
};
127 changes: 125 additions & 2 deletions trunk/drivers/net/can/c_can/c_can.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <linux/if_ether.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>

#include <linux/can.h>
#include <linux/can/dev.h>
Expand All @@ -45,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 @@ -64,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 @@ -163,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 @@ -201,6 +209,30 @@ static const struct can_bittiming_const c_can_bittiming_const = {
.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)
{
return (priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) +
Expand Down Expand Up @@ -673,11 +705,15 @@ static int c_can_get_berr_counter(const struct net_device *dev,
unsigned int reg_err_counter;
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);
bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >>
ERR_CNT_REC_SHIFT;
bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK;

c_can_pm_runtime_put_sync(priv);

return 0;
}

Expand Down Expand Up @@ -1053,11 +1089,13 @@ static int c_can_open(struct net_device *dev)
int err;
struct c_can_priv *priv = netdev_priv(dev);

c_can_pm_runtime_get_sync(priv);

/* open the can device */
err = open_candev(dev);
if (err) {
netdev_err(dev, "failed to open can device\n");
return err;
goto exit_open_fail;
}

/* register interrupt handler */
Expand All @@ -1079,6 +1117,8 @@ static int c_can_open(struct net_device *dev)

exit_irq_fail:
close_candev(dev);
exit_open_fail:
c_can_pm_runtime_put_sync(priv);
return err;
}

Expand All @@ -1091,6 +1131,7 @@ static int c_can_close(struct net_device *dev)
c_can_stop(dev);
free_irq(dev->irq, dev);
close_candev(dev);
c_can_pm_runtime_put_sync(priv);

return 0;
}
Expand Down Expand Up @@ -1119,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 All @@ -1133,10 +1245,19 @@ static const struct net_device_ops c_can_netdev_ops = {

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->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);

Expand All @@ -1148,6 +1269,8 @@ void unregister_c_can_dev(struct net_device *dev)
c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS);

unregister_candev(dev);

c_can_pm_runtime_disable(priv);
}
EXPORT_SYMBOL_GPL(unregister_c_can_dev);

Expand Down
14 changes: 12 additions & 2 deletions trunk/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 @@ -143,15 +145,17 @@ static const u16 reg_map_d_can[] = {
};

enum c_can_dev_id {
C_CAN_DEVTYPE,
D_CAN_DEVTYPE,
BOSCH_C_CAN_PLATFORM,
BOSCH_C_CAN,
BOSCH_D_CAN,
};

/* c_can private data structure */
struct c_can_priv {
struct can_priv can; /* must be the first member */
struct napi_struct napi;
struct net_device *dev;
struct device *device;
int tx_object;
int current_status;
int last_status;
Expand All @@ -164,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 */
6 changes: 3 additions & 3 deletions trunk/drivers/net/can/c_can/c_can_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ static int __devinit c_can_pci_probe(struct pci_dev *pdev,

/* Configure CAN type */
switch (c_can_pci_data->type) {
case C_CAN_DEVTYPE:
case BOSCH_C_CAN:
priv->regs = reg_map_c_can;
break;
case D_CAN_DEVTYPE:
case BOSCH_D_CAN:
priv->regs = reg_map_d_can;
priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
break;
Expand Down Expand Up @@ -192,7 +192,7 @@ static void __devexit c_can_pci_remove(struct pci_dev *pdev)
}

static struct c_can_pci_data c_can_sta2x11= {
.type = C_CAN_DEVTYPE,
.type = BOSCH_C_CAN,
.reg_align = C_CAN_REG_ALIGN_32,
.freq = 52000000, /* 52 Mhz */
};
Expand Down
Loading

0 comments on commit ddb7fe9

Please sign in to comment.