1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2018 Oleksij Rempel <linux@rempel-privat.de>
5 * Driver for Alcor Micro AU6601 and AU6621 controllers
8 #include <linux/delay.h>
9 #include <linux/interrupt.h>
11 #include <linux/irq.h>
12 #include <linux/mfd/core.h>
13 #include <linux/module.h>
14 #include <linux/pci.h>
15 #include <linux/platform_device.h>
18 #include <linux/alcor_pci.h>
20 #define DRV_NAME_ALCOR_PCI "alcor_pci"
22 static DEFINE_IDA(alcor_pci_idr
);
24 static struct mfd_cell alcor_pci_cells
[] = {
26 .name
= DRV_NAME_ALCOR_PCI_SDMMC
,
29 .name
= DRV_NAME_ALCOR_PCI_MS
,
33 static const struct alcor_dev_cfg alcor_cfg
= {
37 static const struct alcor_dev_cfg au6621_cfg
= {
41 static const struct alcor_dev_cfg au6625_cfg
= {
45 static const struct pci_device_id pci_ids
[] = {
46 { PCI_DEVICE(PCI_ID_ALCOR_MICRO
, PCI_ID_AU6601
),
47 .driver_data
= (kernel_ulong_t
)&alcor_cfg
},
48 { PCI_DEVICE(PCI_ID_ALCOR_MICRO
, PCI_ID_AU6621
),
49 .driver_data
= (kernel_ulong_t
)&au6621_cfg
},
50 { PCI_DEVICE(PCI_ID_ALCOR_MICRO
, PCI_ID_AU6625
),
51 .driver_data
= (kernel_ulong_t
)&au6625_cfg
},
54 MODULE_DEVICE_TABLE(pci
, pci_ids
);
56 void alcor_write8(struct alcor_pci_priv
*priv
, u8 val
, unsigned int addr
)
58 writeb(val
, priv
->iobase
+ addr
);
60 EXPORT_SYMBOL_GPL(alcor_write8
);
62 void alcor_write16(struct alcor_pci_priv
*priv
, u16 val
, unsigned int addr
)
64 writew(val
, priv
->iobase
+ addr
);
66 EXPORT_SYMBOL_GPL(alcor_write16
);
68 void alcor_write32(struct alcor_pci_priv
*priv
, u32 val
, unsigned int addr
)
70 writel(val
, priv
->iobase
+ addr
);
72 EXPORT_SYMBOL_GPL(alcor_write32
);
74 void alcor_write32be(struct alcor_pci_priv
*priv
, u32 val
, unsigned int addr
)
76 iowrite32be(val
, priv
->iobase
+ addr
);
78 EXPORT_SYMBOL_GPL(alcor_write32be
);
80 u8
alcor_read8(struct alcor_pci_priv
*priv
, unsigned int addr
)
82 return readb(priv
->iobase
+ addr
);
84 EXPORT_SYMBOL_GPL(alcor_read8
);
86 u32
alcor_read32(struct alcor_pci_priv
*priv
, unsigned int addr
)
88 return readl(priv
->iobase
+ addr
);
90 EXPORT_SYMBOL_GPL(alcor_read32
);
92 u32
alcor_read32be(struct alcor_pci_priv
*priv
, unsigned int addr
)
94 return ioread32be(priv
->iobase
+ addr
);
96 EXPORT_SYMBOL_GPL(alcor_read32be
);
98 static int alcor_pci_probe(struct pci_dev
*pdev
,
99 const struct pci_device_id
*ent
)
101 struct alcor_dev_cfg
*cfg
;
102 struct alcor_pci_priv
*priv
;
105 cfg
= (void *)ent
->driver_data
;
107 ret
= pcim_enable_device(pdev
);
111 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
115 ret
= ida_alloc(&alcor_pci_idr
, GFP_KERNEL
);
121 priv
->parent_pdev
= pdev
->bus
->self
;
122 priv
->dev
= &pdev
->dev
;
124 priv
->irq
= pdev
->irq
;
126 ret
= pci_request_regions(pdev
, DRV_NAME_ALCOR_PCI
);
128 dev_err(&pdev
->dev
, "Cannot request region\n");
133 if (!(pci_resource_flags(pdev
, bar
) & IORESOURCE_MEM
)) {
134 dev_err(&pdev
->dev
, "BAR %d is not iomem. Aborting.\n", bar
);
136 goto error_release_regions
;
139 priv
->iobase
= pcim_iomap(pdev
, bar
, 0);
142 goto error_release_regions
;
145 /* make sure irqs are disabled */
146 alcor_write32(priv
, 0, AU6601_REG_INT_ENABLE
);
147 alcor_write32(priv
, 0, AU6601_MS_INT_ENABLE
);
149 ret
= dma_set_mask_and_coherent(priv
->dev
, AU6601_SDMA_MASK
);
151 dev_err(priv
->dev
, "Failed to set DMA mask\n");
152 goto error_release_regions
;
155 pci_set_master(pdev
);
156 pci_set_drvdata(pdev
, priv
);
158 for (i
= 0; i
< ARRAY_SIZE(alcor_pci_cells
); i
++) {
159 alcor_pci_cells
[i
].platform_data
= priv
;
160 alcor_pci_cells
[i
].pdata_size
= sizeof(*priv
);
162 ret
= mfd_add_devices(&pdev
->dev
, priv
->id
, alcor_pci_cells
,
163 ARRAY_SIZE(alcor_pci_cells
), NULL
, 0, NULL
);
165 goto error_clear_drvdata
;
167 pci_disable_link_state(pdev
, PCIE_LINK_STATE_L0S
| PCIE_LINK_STATE_L1
);
172 pci_clear_master(pdev
);
173 pci_set_drvdata(pdev
, NULL
);
174 error_release_regions
:
175 pci_release_regions(pdev
);
177 ida_free(&alcor_pci_idr
, priv
->id
);
181 static void alcor_pci_remove(struct pci_dev
*pdev
)
183 struct alcor_pci_priv
*priv
;
185 priv
= pci_get_drvdata(pdev
);
187 mfd_remove_devices(&pdev
->dev
);
189 ida_free(&alcor_pci_idr
, priv
->id
);
191 pci_release_regions(pdev
);
192 pci_clear_master(pdev
);
193 pci_set_drvdata(pdev
, NULL
);
196 #ifdef CONFIG_PM_SLEEP
197 static int alcor_suspend(struct device
*dev
)
202 static int alcor_resume(struct device
*dev
)
204 struct alcor_pci_priv
*priv
= dev_get_drvdata(dev
);
206 pci_disable_link_state(priv
->pdev
,
207 PCIE_LINK_STATE_L0S
| PCIE_LINK_STATE_L1
);
211 #endif /* CONFIG_PM_SLEEP */
213 static SIMPLE_DEV_PM_OPS(alcor_pci_pm_ops
, alcor_suspend
, alcor_resume
);
215 static struct pci_driver alcor_driver
= {
216 .name
= DRV_NAME_ALCOR_PCI
,
218 .probe
= alcor_pci_probe
,
219 .remove
= alcor_pci_remove
,
221 .pm
= &alcor_pci_pm_ops
225 module_pci_driver(alcor_driver
);
227 MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
228 MODULE_DESCRIPTION("PCI driver for Alcor Micro AU6601 Secure Digital Host Controller Interface");
229 MODULE_LICENSE("GPL");