Blame view

drivers/char/xillybus/xillybus_pcie.c 5.35 KB
48bae0507   Eli Billauer   staging: New driv...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  /*
   * linux/drivers/misc/xillybus_pcie.c
   *
   * Copyright 2011 Xillybus Ltd, http://xillybus.com
   *
   * Driver for the Xillybus FPGA/host framework using PCI Express.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the smems of the GNU General Public License as published by
   * the Free Software Foundation; version 2 of the License.
   */
  
  #include <linux/module.h>
  #include <linux/pci.h>
  #include <linux/pci-aspm.h>
  #include <linux/slab.h>
  #include "xillybus.h"
  
  MODULE_DESCRIPTION("Xillybus driver for PCIe");
  MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
  MODULE_VERSION("1.06");
  MODULE_ALIAS("xillybus_pcie");
  MODULE_LICENSE("GPL v2");
  
  #define PCI_DEVICE_ID_XILLYBUS		0xebeb
  
  #define PCI_VENDOR_ID_ALTERA		0x1172
  #define PCI_VENDOR_ID_ACTEL		0x11aa
  #define PCI_VENDOR_ID_LATTICE		0x1204
e71042f24   Eli Billauer   staging: xillybus...
30
  static const char xillyname[] = "xillybus_pcie";
41e043fcf   Jingoo Han   staging: remove D...
31
  static const struct pci_device_id xillyids[] = {
48bae0507   Eli Billauer   staging: New driv...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  	{PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILLYBUS)},
  	{PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_XILLYBUS)},
  	{PCI_DEVICE(PCI_VENDOR_ID_ACTEL, PCI_DEVICE_ID_XILLYBUS)},
  	{PCI_DEVICE(PCI_VENDOR_ID_LATTICE, PCI_DEVICE_ID_XILLYBUS)},
  	{ /* End: all zeroes */ }
  };
  
  static int xilly_pci_direction(int direction)
  {
  	switch (direction) {
  	case DMA_TO_DEVICE:
  		return PCI_DMA_TODEVICE;
  	case DMA_FROM_DEVICE:
  		return PCI_DMA_FROMDEVICE;
  	default:
  		return PCI_DMA_BIDIRECTIONAL;
  	}
  }
  
  static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep,
  					      dma_addr_t dma_handle,
  					      size_t size,
  					      int direction)
  {
  	pci_dma_sync_single_for_cpu(ep->pdev,
  				    dma_handle,
  				    size,
  				    xilly_pci_direction(direction));
  }
  
  static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep,
  						 dma_addr_t dma_handle,
  						 size_t size,
  						 int direction)
  {
  	pci_dma_sync_single_for_device(ep->pdev,
  				       dma_handle,
  				       size,
  				       xilly_pci_direction(direction));
  }
525be905d   Eli Billauer   staging: xillybus...
72
73
74
75
76
77
78
79
80
  static void xilly_pci_unmap(void *ptr)
  {
  	struct xilly_mapping *data = ptr;
  
  	pci_unmap_single(data->device, data->dma_addr,
  			 data->size, data->direction);
  
  	kfree(ptr);
  }
48bae0507   Eli Billauer   staging: New driv...
81
82
83
84
85
  /*
   * Map either through the PCI DMA mapper or the non_PCI one. Behind the
   * scenes exactly the same functions are called with the same parameters,
   * but that can change.
   */
525be905d   Eli Billauer   staging: xillybus...
86
87
88
89
90
  static int xilly_map_single_pci(struct xilly_endpoint *ep,
  				void *ptr,
  				size_t size,
  				int direction,
  				dma_addr_t *ret_dma_handle
48bae0507   Eli Billauer   staging: New driv...
91
92
  	)
  {
48bae0507   Eli Billauer   staging: New driv...
93
  	int pci_direction;
525be905d   Eli Billauer   staging: xillybus...
94
95
  	dma_addr_t addr;
  	struct xilly_mapping *this;
48bae0507   Eli Billauer   staging: New driv...
96

525be905d   Eli Billauer   staging: xillybus...
97
  	this = kzalloc(sizeof(*this), GFP_KERNEL);
48bae0507   Eli Billauer   staging: New driv...
98
  	if (!this)
525be905d   Eli Billauer   staging: xillybus...
99
  		return -ENOMEM;
48bae0507   Eli Billauer   staging: New driv...
100
101
  
  	pci_direction = xilly_pci_direction(direction);
525be905d   Eli Billauer   staging: xillybus...
102

48bae0507   Eli Billauer   staging: New driv...
103
  	addr = pci_map_single(ep->pdev, ptr, size, pci_direction);
48bae0507   Eli Billauer   staging: New driv...
104
105
106
  
  	if (pci_dma_mapping_error(ep->pdev, addr)) {
  		kfree(this);
525be905d   Eli Billauer   staging: xillybus...
107
  		return -ENODEV;
48bae0507   Eli Billauer   staging: New driv...
108
  	}
525be905d   Eli Billauer   staging: xillybus...
109
  	this->device = ep->pdev;
48bae0507   Eli Billauer   staging: New driv...
110
  	this->dma_addr = addr;
48bae0507   Eli Billauer   staging: New driv...
111
  	this->size = size;
525be905d   Eli Billauer   staging: xillybus...
112
  	this->direction = pci_direction;
48bae0507   Eli Billauer   staging: New driv...
113

525be905d   Eli Billauer   staging: xillybus...
114
  	*ret_dma_handle = addr;
48bae0507   Eli Billauer   staging: New driv...
115

bd83a4ab5   Sudip Mukherjee   char: xillybus: u...
116
  	return devm_add_action_or_reset(ep->dev, xilly_pci_unmap, this);
48bae0507   Eli Billauer   staging: New driv...
117
118
119
120
  }
  
  static struct xilly_endpoint_hardware pci_hw = {
  	.owner = THIS_MODULE,
7ee9ded22   Eli Billauer   staging: xillybus...
121
122
  	.hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci,
  	.hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci,
48bae0507   Eli Billauer   staging: New driv...
123
  	.map_single = xilly_map_single_pci,
48bae0507   Eli Billauer   staging: New driv...
124
125
126
  };
  
  static int xilly_probe(struct pci_dev *pdev,
91a2dea8f   Eli Billauer   staging: xillybus...
127
  		       const struct pci_device_id *ent)
48bae0507   Eli Billauer   staging: New driv...
128
129
  {
  	struct xilly_endpoint *endpoint;
40931bbbf   Eli Billauer   staging: xillybus...
130
  	int rc;
48bae0507   Eli Billauer   staging: New driv...
131

26a6fc93f   Eli Billauer   staging: xillybus...
132
  	endpoint = xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw);
48bae0507   Eli Billauer   staging: New driv...
133
134
135
136
137
  
  	if (!endpoint)
  		return -ENOMEM;
  
  	pci_set_drvdata(pdev, endpoint);
9267462e6   Eli Billauer   staging: xillybus...
138
  	rc = pcim_enable_device(pdev);
48bae0507   Eli Billauer   staging: New driv...
139
  	if (rc) {
2cb85c084   Eli Billauer   staging: xillybus...
140
  		dev_err(endpoint->dev,
9267462e6   Eli Billauer   staging: xillybus...
141
142
143
  			"pcim_enable_device() failed. Aborting.
  ");
  		return rc;
48bae0507   Eli Billauer   staging: New driv...
144
  	}
9267462e6   Eli Billauer   staging: xillybus...
145
146
147
  	/* L0s has caused packet drops. No power saving, thank you. */
  
  	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
48bae0507   Eli Billauer   staging: New driv...
148
  	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
2cb85c084   Eli Billauer   staging: xillybus...
149
150
151
  		dev_err(endpoint->dev,
  			"Incorrect BAR configuration. Aborting.
  ");
9267462e6   Eli Billauer   staging: xillybus...
152
  		return -ENODEV;
48bae0507   Eli Billauer   staging: New driv...
153
  	}
9267462e6   Eli Billauer   staging: xillybus...
154
  	rc = pcim_iomap_regions(pdev, 0x01, xillyname);
48bae0507   Eli Billauer   staging: New driv...
155
  	if (rc) {
2cb85c084   Eli Billauer   staging: xillybus...
156
  		dev_err(endpoint->dev,
9267462e6   Eli Billauer   staging: xillybus...
157
158
159
  			"pcim_iomap_regions() failed. Aborting.
  ");
  		return rc;
48bae0507   Eli Billauer   staging: New driv...
160
  	}
9267462e6   Eli Billauer   staging: xillybus...
161
  	endpoint->registers = pcim_iomap_table(pdev)[0];
48bae0507   Eli Billauer   staging: New driv...
162
163
164
165
166
  
  	pci_set_master(pdev);
  
  	/* Set up a single MSI interrupt */
  	if (pci_enable_msi(pdev)) {
2cb85c084   Eli Billauer   staging: xillybus...
167
168
169
  		dev_err(endpoint->dev,
  			"Failed to enable MSI interrupts. Aborting.
  ");
9267462e6   Eli Billauer   staging: xillybus...
170
  		return -ENODEV;
48bae0507   Eli Billauer   staging: New driv...
171
  	}
9267462e6   Eli Billauer   staging: xillybus...
172
173
  	rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0,
  			      xillyname, endpoint);
48bae0507   Eli Billauer   staging: New driv...
174
  	if (rc) {
2cb85c084   Eli Billauer   staging: xillybus...
175
176
177
  		dev_err(endpoint->dev,
  			"Failed to register MSI handler. Aborting.
  ");
9267462e6   Eli Billauer   staging: xillybus...
178
  		return -ENODEV;
48bae0507   Eli Billauer   staging: New driv...
179
180
181
  	}
  
  	/*
6497a8757   Eli Billauer   char: xillybus: A...
182
183
184
185
  	 * Some (old and buggy?) hardware drops 64-bit addressed PCIe packets,
  	 * even when the PCIe driver claims that a 64-bit mask is OK. On the
  	 * other hand, on some architectures, 64-bit addressing is mandatory.
  	 * So go for the 64-bit mask only when failing is the other option.
48bae0507   Eli Billauer   staging: New driv...
186
  	 */
c14cc622d   Eli Billauer   staging: xillybus...
187
  	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
48bae0507   Eli Billauer   staging: New driv...
188
  		endpoint->dma_using_dac = 0;
6497a8757   Eli Billauer   char: xillybus: A...
189
190
  	} else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
  		endpoint->dma_using_dac = 1;
c14cc622d   Eli Billauer   staging: xillybus...
191
  	} else {
2cb85c084   Eli Billauer   staging: xillybus...
192
193
  		dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.
  ");
9267462e6   Eli Billauer   staging: xillybus...
194
  		return -ENODEV;
48bae0507   Eli Billauer   staging: New driv...
195
  	}
525be905d   Eli Billauer   staging: xillybus...
196
  	return xillybus_endpoint_discovery(endpoint);
48bae0507   Eli Billauer   staging: New driv...
197
198
199
200
201
202
203
  }
  
  static void xilly_remove(struct pci_dev *pdev)
  {
  	struct xilly_endpoint *endpoint = pci_get_drvdata(pdev);
  
  	xillybus_endpoint_remove(endpoint);
48bae0507   Eli Billauer   staging: New driv...
204
205
206
207
208
209
210
211
212
213
214
215
  }
  
  MODULE_DEVICE_TABLE(pci, xillyids);
  
  static struct pci_driver xillybus_driver = {
  	.name = xillyname,
  	.id_table = xillyids,
  	.probe = xilly_probe,
  	.remove = xilly_remove,
  };
  
  module_pci_driver(xillybus_driver);