• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   *  cb710/core.c
4   *
5   *  Copyright by Michał Mirosław, 2008-2009
6   */
7  #include <linux/kernel.h>
8  #include <linux/module.h>
9  #include <linux/pci.h>
10  #include <linux/spinlock.h>
11  #include <linux/idr.h>
12  #include <linux/cb710.h>
13  #include <linux/gfp.h>
14  
15  static DEFINE_IDA(cb710_ida);
16  
cb710_pci_update_config_reg(struct pci_dev * pdev,int reg,uint32_t mask,uint32_t xor)17  void cb710_pci_update_config_reg(struct pci_dev *pdev,
18  	int reg, uint32_t mask, uint32_t xor)
19  {
20  	u32 rval;
21  
22  	pci_read_config_dword(pdev, reg, &rval);
23  	rval = (rval & mask) ^ xor;
24  	pci_write_config_dword(pdev, reg, rval);
25  }
26  EXPORT_SYMBOL_GPL(cb710_pci_update_config_reg);
27  
28  /* Some magic writes based on Windows driver init code */
cb710_pci_configure(struct pci_dev * pdev)29  static int cb710_pci_configure(struct pci_dev *pdev)
30  {
31  	unsigned int devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
32  	struct pci_dev *pdev0;
33  	u32 val;
34  
35  	cb710_pci_update_config_reg(pdev, 0x48,
36  		~0x000000FF, 0x0000003F);
37  
38  	pci_read_config_dword(pdev, 0x48, &val);
39  	if (val & 0x80000000)
40  		return 0;
41  
42  	pdev0 = pci_get_slot(pdev->bus, devfn);
43  	if (!pdev0)
44  		return -ENODEV;
45  
46  	if (pdev0->vendor == PCI_VENDOR_ID_ENE
47  	    && pdev0->device == PCI_DEVICE_ID_ENE_720) {
48  		cb710_pci_update_config_reg(pdev0, 0x8C,
49  			~0x00F00000, 0x00100000);
50  		cb710_pci_update_config_reg(pdev0, 0xB0,
51  			~0x08000000, 0x08000000);
52  	}
53  
54  	cb710_pci_update_config_reg(pdev0, 0x8C,
55  		~0x00000F00, 0x00000200);
56  	cb710_pci_update_config_reg(pdev0, 0x90,
57  		~0x00060000, 0x00040000);
58  
59  	pci_dev_put(pdev0);
60  
61  	return 0;
62  }
63  
cb710_irq_handler(int irq,void * data)64  static irqreturn_t cb710_irq_handler(int irq, void *data)
65  {
66  	struct cb710_chip *chip = data;
67  	struct cb710_slot *slot = &chip->slot[0];
68  	irqreturn_t handled = IRQ_NONE;
69  	unsigned nr;
70  
71  	spin_lock(&chip->irq_lock); /* incl. smp_rmb() */
72  
73  	for (nr = chip->slots; nr; ++slot, --nr) {
74  		cb710_irq_handler_t handler_func = slot->irq_handler;
75  		if (handler_func && handler_func(slot))
76  			handled = IRQ_HANDLED;
77  	}
78  
79  	spin_unlock(&chip->irq_lock);
80  
81  	return handled;
82  }
83  
cb710_release_slot(struct device * dev)84  static void cb710_release_slot(struct device *dev)
85  {
86  #ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
87  	struct cb710_slot *slot = cb710_pdev_to_slot(to_platform_device(dev));
88  	struct cb710_chip *chip = cb710_slot_to_chip(slot);
89  
90  	/* slot struct can be freed now */
91  	atomic_dec(&chip->slot_refs_count);
92  #endif
93  }
94  
cb710_register_slot(struct cb710_chip * chip,unsigned slot_mask,unsigned io_offset,const char * name)95  static int cb710_register_slot(struct cb710_chip *chip,
96  	unsigned slot_mask, unsigned io_offset, const char *name)
97  {
98  	int nr = chip->slots;
99  	struct cb710_slot *slot = &chip->slot[nr];
100  	int err;
101  
102  	dev_dbg(cb710_chip_dev(chip),
103  		"register: %s.%d; slot %d; mask %d; IO offset: 0x%02X\n",
104  		name, chip->platform_id, nr, slot_mask, io_offset);
105  
106  	/* slot->irq_handler == NULL here; this needs to be
107  	 * seen before platform_device_register() */
108  	++chip->slots;
109  	smp_wmb();
110  
111  	slot->iobase = chip->iobase + io_offset;
112  	slot->pdev.name = name;
113  	slot->pdev.id = chip->platform_id;
114  	slot->pdev.dev.parent = &chip->pdev->dev;
115  	slot->pdev.dev.release = cb710_release_slot;
116  
117  	err = platform_device_register(&slot->pdev);
118  
119  #ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
120  	atomic_inc(&chip->slot_refs_count);
121  #endif
122  
123  	if (err) {
124  		/* device_initialize() called from platform_device_register()
125  		 * wants this on error path */
126  		platform_device_put(&slot->pdev);
127  
128  		/* slot->irq_handler == NULL here anyway, so no lock needed */
129  		--chip->slots;
130  		return err;
131  	}
132  
133  	chip->slot_mask |= slot_mask;
134  
135  	return 0;
136  }
137  
cb710_unregister_slot(struct cb710_chip * chip,unsigned slot_mask)138  static void cb710_unregister_slot(struct cb710_chip *chip,
139  	unsigned slot_mask)
140  {
141  	int nr = chip->slots - 1;
142  
143  	if (!(chip->slot_mask & slot_mask))
144  		return;
145  
146  	platform_device_unregister(&chip->slot[nr].pdev);
147  
148  	/* complementary to spin_unlock() in cb710_set_irq_handler() */
149  	smp_rmb();
150  	BUG_ON(chip->slot[nr].irq_handler != NULL);
151  
152  	/* slot->irq_handler == NULL here, so no lock needed */
153  	--chip->slots;
154  	chip->slot_mask &= ~slot_mask;
155  }
156  
cb710_set_irq_handler(struct cb710_slot * slot,cb710_irq_handler_t handler)157  void cb710_set_irq_handler(struct cb710_slot *slot,
158  	cb710_irq_handler_t handler)
159  {
160  	struct cb710_chip *chip = cb710_slot_to_chip(slot);
161  	unsigned long flags;
162  
163  	spin_lock_irqsave(&chip->irq_lock, flags);
164  	slot->irq_handler = handler;
165  	spin_unlock_irqrestore(&chip->irq_lock, flags);
166  }
167  EXPORT_SYMBOL_GPL(cb710_set_irq_handler);
168  
169  #ifdef CONFIG_PM
170  
cb710_suspend(struct pci_dev * pdev,pm_message_t state)171  static int cb710_suspend(struct pci_dev *pdev, pm_message_t state)
172  {
173  	struct cb710_chip *chip = pci_get_drvdata(pdev);
174  
175  	devm_free_irq(&pdev->dev, pdev->irq, chip);
176  	pci_save_state(pdev);
177  	pci_disable_device(pdev);
178  	if (state.event & PM_EVENT_SLEEP)
179  		pci_set_power_state(pdev, PCI_D3hot);
180  	return 0;
181  }
182  
cb710_resume(struct pci_dev * pdev)183  static int cb710_resume(struct pci_dev *pdev)
184  {
185  	struct cb710_chip *chip = pci_get_drvdata(pdev);
186  	int err;
187  
188  	pci_set_power_state(pdev, PCI_D0);
189  	pci_restore_state(pdev);
190  	err = pcim_enable_device(pdev);
191  	if (err)
192  		return err;
193  
194  	return devm_request_irq(&pdev->dev, pdev->irq,
195  		cb710_irq_handler, IRQF_SHARED, KBUILD_MODNAME, chip);
196  }
197  
198  #endif /* CONFIG_PM */
199  
cb710_probe(struct pci_dev * pdev,const struct pci_device_id * ent)200  static int cb710_probe(struct pci_dev *pdev,
201  	const struct pci_device_id *ent)
202  {
203  	struct cb710_chip *chip;
204  	u32 val;
205  	int err;
206  	int n = 0;
207  
208  	err = cb710_pci_configure(pdev);
209  	if (err)
210  		return err;
211  
212  	/* this is actually magic... */
213  	pci_read_config_dword(pdev, 0x48, &val);
214  	if (!(val & 0x80000000)) {
215  		pci_write_config_dword(pdev, 0x48, val|0x71000000);
216  		pci_read_config_dword(pdev, 0x48, &val);
217  	}
218  
219  	dev_dbg(&pdev->dev, "PCI config[0x48] = 0x%08X\n", val);
220  	if (!(val & 0x70000000))
221  		return -ENODEV;
222  	val = (val >> 28) & 7;
223  	if (val & CB710_SLOT_MMC)
224  		++n;
225  	if (val & CB710_SLOT_MS)
226  		++n;
227  	if (val & CB710_SLOT_SM)
228  		++n;
229  
230  	chip = devm_kzalloc(&pdev->dev, struct_size(chip, slot, n),
231  			    GFP_KERNEL);
232  	if (!chip)
233  		return -ENOMEM;
234  
235  	err = pcim_enable_device(pdev);
236  	if (err)
237  		return err;
238  
239  	err = pcim_iomap_regions(pdev, 0x0001, KBUILD_MODNAME);
240  	if (err)
241  		return err;
242  
243  	spin_lock_init(&chip->irq_lock);
244  	chip->pdev = pdev;
245  	chip->iobase = pcim_iomap_table(pdev)[0];
246  
247  	pci_set_drvdata(pdev, chip);
248  
249  	err = devm_request_irq(&pdev->dev, pdev->irq,
250  		cb710_irq_handler, IRQF_SHARED, KBUILD_MODNAME, chip);
251  	if (err)
252  		return err;
253  
254  	err = ida_alloc(&cb710_ida, GFP_KERNEL);
255  	if (err < 0)
256  		return err;
257  	chip->platform_id = err;
258  
259  	dev_info(&pdev->dev, "id %d, IO 0x%p, IRQ %d\n",
260  		chip->platform_id, chip->iobase, pdev->irq);
261  
262  	if (val & CB710_SLOT_MMC) {	/* MMC/SD slot */
263  		err = cb710_register_slot(chip,
264  			CB710_SLOT_MMC, 0x00, "cb710-mmc");
265  		if (err)
266  			return err;
267  	}
268  
269  	if (val & CB710_SLOT_MS) {	/* MemoryStick slot */
270  		err = cb710_register_slot(chip,
271  			CB710_SLOT_MS, 0x40, "cb710-ms");
272  		if (err)
273  			goto unreg_mmc;
274  	}
275  
276  	if (val & CB710_SLOT_SM) {	/* SmartMedia slot */
277  		err = cb710_register_slot(chip,
278  			CB710_SLOT_SM, 0x60, "cb710-sm");
279  		if (err)
280  			goto unreg_ms;
281  	}
282  
283  	return 0;
284  unreg_ms:
285  	cb710_unregister_slot(chip, CB710_SLOT_MS);
286  unreg_mmc:
287  	cb710_unregister_slot(chip, CB710_SLOT_MMC);
288  
289  #ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
290  	BUG_ON(atomic_read(&chip->slot_refs_count) != 0);
291  #endif
292  	return err;
293  }
294  
cb710_remove_one(struct pci_dev * pdev)295  static void cb710_remove_one(struct pci_dev *pdev)
296  {
297  	struct cb710_chip *chip = pci_get_drvdata(pdev);
298  
299  	cb710_unregister_slot(chip, CB710_SLOT_SM);
300  	cb710_unregister_slot(chip, CB710_SLOT_MS);
301  	cb710_unregister_slot(chip, CB710_SLOT_MMC);
302  #ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
303  	BUG_ON(atomic_read(&chip->slot_refs_count) != 0);
304  #endif
305  
306  	ida_free(&cb710_ida, chip->platform_id);
307  }
308  
309  static const struct pci_device_id cb710_pci_tbl[] = {
310  	{ PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH,
311  		PCI_ANY_ID, PCI_ANY_ID, },
312  	{ 0, }
313  };
314  
315  static struct pci_driver cb710_driver = {
316  	.name = KBUILD_MODNAME,
317  	.id_table = cb710_pci_tbl,
318  	.probe = cb710_probe,
319  	.remove = cb710_remove_one,
320  #ifdef CONFIG_PM
321  	.suspend = cb710_suspend,
322  	.resume = cb710_resume,
323  #endif
324  };
325  
cb710_init_module(void)326  static int __init cb710_init_module(void)
327  {
328  	return pci_register_driver(&cb710_driver);
329  }
330  
cb710_cleanup_module(void)331  static void __exit cb710_cleanup_module(void)
332  {
333  	pci_unregister_driver(&cb710_driver);
334  	ida_destroy(&cb710_ida);
335  }
336  
337  module_init(cb710_init_module);
338  module_exit(cb710_cleanup_module);
339  
340  MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
341  MODULE_DESCRIPTION("ENE CB710 memory card reader driver");
342  MODULE_LICENSE("GPL");
343  MODULE_DEVICE_TABLE(pci, cb710_pci_tbl);
344