Blame view

drivers/spi/spi-pxa2xx-pci.c 3.97 KB
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
1
2
3
4
5
6
7
  /*
   * CE4100's SPI device is more or less the same one as found on PXA
   *
   */
  #include <linux/pci.h>
  #include <linux/platform_device.h>
  #include <linux/of_device.h>
d7614de42   Paul Gortmaker   spi: Add module.h...
8
  #include <linux/module.h>
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
9
  #include <linux/spi/pxa2xx_spi.h>
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
10
  struct ce4100_info {
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
11
  	struct ssp_device ssp;
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
12
  	struct platform_device *spi_pdev;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  };
  
  static DEFINE_MUTEX(ssp_lock);
  static LIST_HEAD(ssp_list);
  
  struct ssp_device *pxa_ssp_request(int port, const char *label)
  {
  	struct ssp_device *ssp = NULL;
  
  	mutex_lock(&ssp_lock);
  
  	list_for_each_entry(ssp, &ssp_list, node) {
  		if (ssp->port_id == port && ssp->use_count == 0) {
  			ssp->use_count++;
  			ssp->label = label;
  			break;
  		}
  	}
  
  	mutex_unlock(&ssp_lock);
  
  	if (&ssp->node == &ssp_list)
  		return NULL;
  
  	return ssp;
  }
  EXPORT_SYMBOL_GPL(pxa_ssp_request);
  
  void pxa_ssp_free(struct ssp_device *ssp)
  {
  	mutex_lock(&ssp_lock);
  	if (ssp->use_count) {
  		ssp->use_count--;
  		ssp->label = NULL;
  	} else
  		dev_err(&ssp->pdev->dev, "device already free
  ");
  	mutex_unlock(&ssp_lock);
  }
  EXPORT_SYMBOL_GPL(pxa_ssp_free);
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
53
54
55
56
57
58
  static int __devinit ce4100_spi_probe(struct pci_dev *dev,
  		const struct pci_device_id *ent)
  {
  	int ret;
  	resource_size_t phys_beg;
  	resource_size_t phys_len;
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
59
  	struct ce4100_info *spi_info;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
60
  	struct platform_device *pdev;
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
61
  	struct pxa2xx_spi_master spi_pdata;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  	struct ssp_device *ssp;
  
  	ret = pci_enable_device(dev);
  	if (ret)
  		return ret;
  
  	phys_beg = pci_resource_start(dev, 0);
  	phys_len = pci_resource_len(dev, 0);
  
  	if (!request_mem_region(phys_beg, phys_len,
  				"CE4100 SPI")) {
  		dev_err(&dev->dev, "Can't request register space.
  ");
  		ret = -EBUSY;
  		return ret;
  	}
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
78
  	pdev = platform_device_alloc("pxa2xx-spi", dev->devfn);
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
79
  	spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL);
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
80
  	if (!pdev || !spi_info ) {
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
81
  		ret = -ENOMEM;
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
82
  		goto err_nomem;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
83
  	}
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
84
85
  	memset(&spi_pdata, 0, sizeof(spi_pdata));
  	spi_pdata.num_chipselect = dev->devfn;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
86

0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
87
88
89
  	ret = platform_device_add_data(pdev, &spi_pdata, sizeof(spi_pdata));
  	if (ret)
  		goto err_nomem;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
90

0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
91
  	pdev->dev.parent = &dev->dev;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
92
  	pdev->dev.of_node = dev->dev.of_node;
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
93
  	ssp = &spi_info->ssp;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
94
95
96
97
98
99
  	ssp->phys_base = pci_resource_start(dev, 0);
  	ssp->mmio_base = ioremap(phys_beg, phys_len);
  	if (!ssp->mmio_base) {
  		dev_err(&pdev->dev, "failed to ioremap() registers
  ");
  		ret = -EIO;
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
100
  		goto err_nomem;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
101
102
103
104
105
106
107
108
109
110
  	}
  	ssp->irq = dev->irq;
  	ssp->port_id = pdev->id;
  	ssp->type = PXA25x_SSP;
  
  	mutex_lock(&ssp_lock);
  	list_add(&ssp->node, &ssp_list);
  	mutex_unlock(&ssp_lock);
  
  	pci_set_drvdata(dev, spi_info);
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
111
  	ret = platform_device_add(pdev);
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
112
113
114
115
116
117
118
119
120
121
122
  	if (ret)
  		goto err_dev_add;
  
  	return ret;
  
  err_dev_add:
  	pci_set_drvdata(dev, NULL);
  	mutex_lock(&ssp_lock);
  	list_del(&ssp->node);
  	mutex_unlock(&ssp_lock);
  	iounmap(ssp->mmio_base);
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
123
  err_nomem:
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
124
  	release_mem_region(phys_beg, phys_len);
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
125
126
  	platform_device_put(pdev);
  	kfree(spi_info);
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
127
128
129
130
131
  	return ret;
  }
  
  static void __devexit ce4100_spi_remove(struct pci_dev *dev)
  {
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
132
  	struct ce4100_info *spi_info;
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
133
134
135
  	struct ssp_device *ssp;
  
  	spi_info = pci_get_drvdata(dev);
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
136
  	ssp = &spi_info->ssp;
0f3e1d27a   Sebastian Andrzej Siewior   spi/pxa2xx pci: f...
137
  	platform_device_unregister(spi_info->spi_pdev);
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  
  	iounmap(ssp->mmio_base);
  	release_mem_region(pci_resource_start(dev, 0),
  			pci_resource_len(dev, 0));
  
  	mutex_lock(&ssp_lock);
  	list_del(&ssp->node);
  	mutex_unlock(&ssp_lock);
  
  	pci_set_drvdata(dev, NULL);
  	pci_disable_device(dev);
  	kfree(spi_info);
  }
  
  static struct pci_device_id ce4100_spi_devices[] __devinitdata = {
d6ea3df0d   Sebastian Andrzej Siewior   spi/pxa2xx: Add C...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) },
  	{ },
  };
  MODULE_DEVICE_TABLE(pci, ce4100_spi_devices);
  
  static struct pci_driver ce4100_spi_driver = {
  	.name           = "ce4100_spi",
  	.id_table       = ce4100_spi_devices,
  	.probe          = ce4100_spi_probe,
  	.remove         = __devexit_p(ce4100_spi_remove),
  };
  
  static int __init ce4100_spi_init(void)
  {
  	return pci_register_driver(&ce4100_spi_driver);
  }
  module_init(ce4100_spi_init);
  
  static void __exit ce4100_spi_exit(void)
  {
  	pci_unregister_driver(&ce4100_spi_driver);
  }
  module_exit(ce4100_spi_exit);
  
  MODULE_DESCRIPTION("CE4100 PCI-SPI glue code for PXA's driver");
  MODULE_LICENSE("GPL v2");
  MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");