diff options
Diffstat (limited to 'drivers/net/can/c_can/c_can_pci.c')
| -rw-r--r-- | drivers/net/can/c_can/c_can_pci.c | 89 | 
1 files changed, 80 insertions, 9 deletions
diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c index b374be7891a..5d11e0e4225 100644 --- a/drivers/net/can/c_can/c_can_pci.c +++ b/drivers/net/can/c_can/c_can_pci.c @@ -19,9 +19,13 @@  #include "c_can.h" +#define PCI_DEVICE_ID_PCH_CAN	0x8818 +#define PCH_PCI_SOFT_RESET	0x01fc +  enum c_can_pci_reg_align {  	C_CAN_REG_ALIGN_16,  	C_CAN_REG_ALIGN_32, +	C_CAN_REG_32,  };  struct c_can_pci_data { @@ -31,6 +35,10 @@ struct c_can_pci_data {  	enum c_can_pci_reg_align reg_align;  	/* Set the frequency */  	unsigned int freq; +	/* PCI bar number */ +	int bar; +	/* Callback for reset */ +	void (*init)(const struct c_can_priv *priv, bool enable);  };  /* @@ -39,30 +47,70 @@ struct c_can_pci_data {   * registers can be aligned to a 16-bit boundary or 32-bit boundary etc.   * Handle the same by providing a common read/write interface.   */ -static u16 c_can_pci_read_reg_aligned_to_16bit(struct c_can_priv *priv, +static u16 c_can_pci_read_reg_aligned_to_16bit(const struct c_can_priv *priv,  						enum reg index)  {  	return readw(priv->base + priv->regs[index]);  } -static void c_can_pci_write_reg_aligned_to_16bit(struct c_can_priv *priv, +static void c_can_pci_write_reg_aligned_to_16bit(const struct c_can_priv *priv,  						enum reg index, u16 val)  {  	writew(val, priv->base + priv->regs[index]);  } -static u16 c_can_pci_read_reg_aligned_to_32bit(struct c_can_priv *priv, +static u16 c_can_pci_read_reg_aligned_to_32bit(const struct c_can_priv *priv,  						enum reg index)  {  	return readw(priv->base + 2 * priv->regs[index]);  } -static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv, +static void c_can_pci_write_reg_aligned_to_32bit(const struct c_can_priv *priv,  						enum reg index, u16 val)  {  	writew(val, priv->base + 2 * priv->regs[index]);  } +static u16 c_can_pci_read_reg_32bit(const struct c_can_priv *priv, +				    enum reg index) +{ +	return (u16)ioread32(priv->base + 2 * priv->regs[index]); +} + +static void c_can_pci_write_reg_32bit(const struct c_can_priv *priv, +				      enum reg index, u16 val) +{ +	iowrite32((u32)val, priv->base + 2 * priv->regs[index]); +} + +static u32 c_can_pci_read_reg32(const struct c_can_priv *priv, enum reg index) +{ +	u32 val; + +	val = priv->read_reg(priv, index); +	val |= ((u32) priv->read_reg(priv, index + 1)) << 16; + +	return val; +} + +static void c_can_pci_write_reg32(const struct c_can_priv *priv, enum reg index, +		u32 val) +{ +	priv->write_reg(priv, index + 1, val >> 16); +	priv->write_reg(priv, index, val); +} + +static void c_can_pci_reset_pch(const struct c_can_priv *priv, bool enable) +{ +	if (enable) { +		u32 __iomem *addr = priv->base + PCH_PCI_SOFT_RESET; + +		/* write to sw reset register */ +		iowrite32(1, addr); +		iowrite32(0, addr); +	} +} +  static int c_can_pci_probe(struct pci_dev *pdev,  			   const struct pci_device_id *ent)  { @@ -84,10 +132,14 @@ static int c_can_pci_probe(struct pci_dev *pdev,  		goto out_disable_device;  	} -	pci_set_master(pdev); -	pci_enable_msi(pdev); +	ret = pci_enable_msi(pdev); +	if (!ret) { +		dev_info(&pdev->dev, "MSI enabled\n"); +		pci_set_master(pdev); +	} -	addr = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); +	addr = pci_iomap(pdev, c_can_pci_data->bar, +			 pci_resource_len(pdev, c_can_pci_data->bar));  	if (!addr) {  		dev_err(&pdev->dev,  			"device has no PCI memory resources, " @@ -132,6 +184,8 @@ static int c_can_pci_probe(struct pci_dev *pdev,  		goto out_free_c_can;  	} +	priv->type = c_can_pci_data->type; +  	/* Configure access to registers */  	switch (c_can_pci_data->reg_align) {  	case C_CAN_REG_ALIGN_32: @@ -142,10 +196,18 @@ static int c_can_pci_probe(struct pci_dev *pdev,  		priv->read_reg = c_can_pci_read_reg_aligned_to_16bit;  		priv->write_reg = c_can_pci_write_reg_aligned_to_16bit;  		break; +	case C_CAN_REG_32: +		priv->read_reg = c_can_pci_read_reg_32bit; +		priv->write_reg = c_can_pci_write_reg_32bit; +		break;  	default:  		ret = -EINVAL;  		goto out_free_c_can;  	} +	priv->read_reg32 = c_can_pci_read_reg32; +	priv->write_reg32 = c_can_pci_write_reg32; + +	priv->raminit = c_can_pci_data->init;  	ret = register_c_can_dev(dev);  	if (ret) { @@ -160,7 +222,6 @@ static int c_can_pci_probe(struct pci_dev *pdev,  	return 0;  out_free_c_can: -	pci_set_drvdata(pdev, NULL);  	free_c_can_dev(dev);  out_iounmap:  	pci_iounmap(pdev, addr); @@ -181,7 +242,6 @@ static void c_can_pci_remove(struct pci_dev *pdev)  	unregister_c_can_dev(dev); -	pci_set_drvdata(pdev, NULL);  	free_c_can_dev(dev);  	pci_iounmap(pdev, priv->base); @@ -195,6 +255,15 @@ static struct c_can_pci_data c_can_sta2x11= {  	.type = BOSCH_C_CAN,  	.reg_align = C_CAN_REG_ALIGN_32,  	.freq = 52000000, /* 52 Mhz */ +	.bar = 0, +}; + +static struct c_can_pci_data c_can_pch = { +	.type = BOSCH_C_CAN, +	.reg_align = C_CAN_REG_32, +	.freq = 50000000, /* 50 MHz */ +	.init = c_can_pci_reset_pch, +	.bar = 1,  };  #define C_CAN_ID(_vend, _dev, _driverdata) {		\ @@ -204,6 +273,8 @@ static struct c_can_pci_data c_can_sta2x11= {  static DEFINE_PCI_DEVICE_TABLE(c_can_pci_tbl) = {  	C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN,  		 c_can_sta2x11), +	C_CAN_ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_CAN, +		 c_can_pch),  	{},  };  static struct pci_driver c_can_pci_driver = {  | 
