1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Intel Quark MFD PCI driver for I2C & GPIO |
4 | * |
5 | * Copyright(c) 2014 Intel Corporation. |
6 | * |
7 | * Intel Quark PCI device for I2C and GPIO controller sharing the same |
8 | * PCI function. This PCI driver will split the 2 devices into their |
9 | * respective drivers. |
10 | */ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/pci.h> |
15 | #include <linux/mfd/core.h> |
16 | #include <linux/clkdev.h> |
17 | #include <linux/clk-provider.h> |
18 | #include <linux/dmi.h> |
19 | #include <linux/i2c.h> |
20 | #include <linux/property.h> |
21 | |
22 | /* PCI BAR for register base address */ |
23 | #define MFD_I2C_BAR 0 |
24 | #define MFD_GPIO_BAR 1 |
25 | |
26 | /* ACPI _ADR value to match the child node */ |
27 | #define MFD_ACPI_MATCH_GPIO 0ULL |
28 | #define MFD_ACPI_MATCH_I2C 1ULL |
29 | |
30 | #define INTEL_QUARK_IORES_MEM 0 |
31 | #define INTEL_QUARK_IORES_IRQ 1 |
32 | |
33 | #define INTEL_QUARK_I2C_CONTROLLER_CLK "i2c_designware.0" |
34 | |
35 | /* The Quark I2C controller source clock */ |
36 | #define INTEL_QUARK_I2C_CLK_HZ 33000000 |
37 | |
38 | struct intel_quark_mfd { |
39 | struct clk *i2c_clk; |
40 | struct clk_lookup *i2c_clk_lookup; |
41 | }; |
42 | |
43 | static const struct property_entry intel_quark_i2c_controller_standard_properties[] = { |
44 | PROPERTY_ENTRY_U32("clock-frequency" , I2C_MAX_STANDARD_MODE_FREQ), |
45 | { } |
46 | }; |
47 | |
48 | static const struct software_node intel_quark_i2c_controller_standard_node = { |
49 | .name = "intel-quark-i2c-controller" , |
50 | .properties = intel_quark_i2c_controller_standard_properties, |
51 | }; |
52 | |
53 | static const struct property_entry intel_quark_i2c_controller_fast_properties[] = { |
54 | PROPERTY_ENTRY_U32("clock-frequency" , I2C_MAX_FAST_MODE_FREQ), |
55 | { } |
56 | }; |
57 | |
58 | static const struct software_node intel_quark_i2c_controller_fast_node = { |
59 | .name = "intel-quark-i2c-controller" , |
60 | .properties = intel_quark_i2c_controller_fast_properties, |
61 | }; |
62 | |
63 | static const struct dmi_system_id dmi_platform_info[] = { |
64 | { |
65 | .matches = { |
66 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "Galileo" ), |
67 | }, |
68 | .driver_data = (void *)&intel_quark_i2c_controller_standard_node, |
69 | }, |
70 | { |
71 | .matches = { |
72 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2" ), |
73 | }, |
74 | .driver_data = (void *)&intel_quark_i2c_controller_fast_node, |
75 | }, |
76 | { |
77 | .matches = { |
78 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000" ), |
79 | }, |
80 | .driver_data = (void *)&intel_quark_i2c_controller_fast_node, |
81 | }, |
82 | {} |
83 | }; |
84 | |
85 | /* This is used as a place holder and will be modified at run-time */ |
86 | static struct resource intel_quark_i2c_res[] = { |
87 | [INTEL_QUARK_IORES_MEM] = { |
88 | .flags = IORESOURCE_MEM, |
89 | }, |
90 | [INTEL_QUARK_IORES_IRQ] = { |
91 | .flags = IORESOURCE_IRQ, |
92 | }, |
93 | }; |
94 | |
95 | static struct mfd_cell_acpi_match intel_quark_acpi_match_i2c = { |
96 | .adr = MFD_ACPI_MATCH_I2C, |
97 | }; |
98 | |
99 | /* This is used as a place holder and will be modified at run-time */ |
100 | static struct resource intel_quark_gpio_res[] = { |
101 | [INTEL_QUARK_IORES_MEM] = { |
102 | .flags = IORESOURCE_MEM, |
103 | }, |
104 | [INTEL_QUARK_IORES_IRQ] = { |
105 | .flags = IORESOURCE_IRQ, |
106 | }, |
107 | }; |
108 | |
109 | static struct mfd_cell_acpi_match intel_quark_acpi_match_gpio = { |
110 | .adr = MFD_ACPI_MATCH_GPIO, |
111 | }; |
112 | |
113 | static const struct software_node intel_quark_gpio_controller_node = { |
114 | .name = "intel-quark-gpio-controller" , |
115 | }; |
116 | |
117 | static const struct property_entry intel_quark_gpio_portA_properties[] = { |
118 | PROPERTY_ENTRY_U32("reg" , 0), |
119 | PROPERTY_ENTRY_U32("snps,nr-gpios" , 8), |
120 | PROPERTY_ENTRY_U32("gpio-base" , 8), |
121 | { } |
122 | }; |
123 | |
124 | static const struct software_node intel_quark_gpio_portA_node = { |
125 | .name = "portA" , |
126 | .parent = &intel_quark_gpio_controller_node, |
127 | .properties = intel_quark_gpio_portA_properties, |
128 | }; |
129 | |
130 | static const struct software_node *intel_quark_gpio_node_group[] = { |
131 | &intel_quark_gpio_controller_node, |
132 | &intel_quark_gpio_portA_node, |
133 | NULL |
134 | }; |
135 | |
136 | static struct mfd_cell intel_quark_mfd_cells[] = { |
137 | [MFD_I2C_BAR] = { |
138 | .id = MFD_I2C_BAR, |
139 | .name = "i2c_designware" , |
140 | .acpi_match = &intel_quark_acpi_match_i2c, |
141 | .num_resources = ARRAY_SIZE(intel_quark_i2c_res), |
142 | .resources = intel_quark_i2c_res, |
143 | .ignore_resource_conflicts = true, |
144 | }, |
145 | [MFD_GPIO_BAR] = { |
146 | .id = MFD_GPIO_BAR, |
147 | .name = "gpio-dwapb" , |
148 | .acpi_match = &intel_quark_acpi_match_gpio, |
149 | .num_resources = ARRAY_SIZE(intel_quark_gpio_res), |
150 | .resources = intel_quark_gpio_res, |
151 | .ignore_resource_conflicts = true, |
152 | }, |
153 | }; |
154 | |
155 | static const struct pci_device_id intel_quark_mfd_ids[] = { |
156 | { PCI_VDEVICE(INTEL, 0x0934), }, |
157 | {}, |
158 | }; |
159 | MODULE_DEVICE_TABLE(pci, intel_quark_mfd_ids); |
160 | |
161 | static int intel_quark_register_i2c_clk(struct device *dev) |
162 | { |
163 | struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev); |
164 | struct clk *i2c_clk; |
165 | |
166 | i2c_clk = clk_register_fixed_rate(dev, |
167 | INTEL_QUARK_I2C_CONTROLLER_CLK, NULL, |
168 | flags: 0, INTEL_QUARK_I2C_CLK_HZ); |
169 | if (IS_ERR(ptr: i2c_clk)) |
170 | return PTR_ERR(ptr: i2c_clk); |
171 | |
172 | quark_mfd->i2c_clk = i2c_clk; |
173 | quark_mfd->i2c_clk_lookup = clkdev_create(clk: i2c_clk, NULL, |
174 | INTEL_QUARK_I2C_CONTROLLER_CLK); |
175 | |
176 | if (!quark_mfd->i2c_clk_lookup) { |
177 | clk_unregister(clk: quark_mfd->i2c_clk); |
178 | dev_err(dev, "Fixed clk register failed\n" ); |
179 | return -ENOMEM; |
180 | } |
181 | |
182 | return 0; |
183 | } |
184 | |
185 | static void intel_quark_unregister_i2c_clk(struct device *dev) |
186 | { |
187 | struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev); |
188 | |
189 | if (!quark_mfd->i2c_clk_lookup) |
190 | return; |
191 | |
192 | clkdev_drop(cl: quark_mfd->i2c_clk_lookup); |
193 | clk_unregister(clk: quark_mfd->i2c_clk); |
194 | } |
195 | |
196 | static int intel_quark_i2c_setup(struct pci_dev *pdev) |
197 | { |
198 | struct mfd_cell *cell = &intel_quark_mfd_cells[MFD_I2C_BAR]; |
199 | struct resource *res = intel_quark_i2c_res; |
200 | const struct dmi_system_id *dmi_id; |
201 | |
202 | res[INTEL_QUARK_IORES_MEM].start = pci_resource_start(pdev, MFD_I2C_BAR); |
203 | res[INTEL_QUARK_IORES_MEM].end = pci_resource_end(pdev, MFD_I2C_BAR); |
204 | |
205 | res[INTEL_QUARK_IORES_IRQ].start = pci_irq_vector(dev: pdev, nr: 0); |
206 | res[INTEL_QUARK_IORES_IRQ].end = pci_irq_vector(dev: pdev, nr: 0); |
207 | |
208 | /* Normal mode by default */ |
209 | cell->swnode = &intel_quark_i2c_controller_standard_node; |
210 | |
211 | dmi_id = dmi_first_match(list: dmi_platform_info); |
212 | if (dmi_id) |
213 | cell->swnode = (struct software_node *)dmi_id->driver_data; |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | static int intel_quark_gpio_setup(struct pci_dev *pdev) |
219 | { |
220 | struct mfd_cell *cell = &intel_quark_mfd_cells[MFD_GPIO_BAR]; |
221 | struct resource *res = intel_quark_gpio_res; |
222 | int ret; |
223 | |
224 | res[INTEL_QUARK_IORES_MEM].start = pci_resource_start(pdev, MFD_GPIO_BAR); |
225 | res[INTEL_QUARK_IORES_MEM].end = pci_resource_end(pdev, MFD_GPIO_BAR); |
226 | |
227 | res[INTEL_QUARK_IORES_IRQ].start = pci_irq_vector(dev: pdev, nr: 0); |
228 | res[INTEL_QUARK_IORES_IRQ].end = pci_irq_vector(dev: pdev, nr: 0); |
229 | |
230 | ret = software_node_register_node_group(node_group: intel_quark_gpio_node_group); |
231 | if (ret) |
232 | return ret; |
233 | |
234 | cell->swnode = &intel_quark_gpio_controller_node; |
235 | return 0; |
236 | } |
237 | |
238 | static int intel_quark_mfd_probe(struct pci_dev *pdev, |
239 | const struct pci_device_id *id) |
240 | { |
241 | struct intel_quark_mfd *quark_mfd; |
242 | int ret; |
243 | |
244 | ret = pcim_enable_device(pdev); |
245 | if (ret) |
246 | return ret; |
247 | |
248 | quark_mfd = devm_kzalloc(dev: &pdev->dev, size: sizeof(*quark_mfd), GFP_KERNEL); |
249 | if (!quark_mfd) |
250 | return -ENOMEM; |
251 | |
252 | dev_set_drvdata(dev: &pdev->dev, data: quark_mfd); |
253 | |
254 | ret = intel_quark_register_i2c_clk(dev: &pdev->dev); |
255 | if (ret) |
256 | return ret; |
257 | |
258 | pci_set_master(dev: pdev); |
259 | |
260 | /* This driver only requires 1 IRQ vector */ |
261 | ret = pci_alloc_irq_vectors(dev: pdev, min_vecs: 1, max_vecs: 1, PCI_IRQ_ALL_TYPES); |
262 | if (ret < 0) |
263 | goto err_unregister_i2c_clk; |
264 | |
265 | ret = intel_quark_i2c_setup(pdev); |
266 | if (ret) |
267 | goto err_free_irq_vectors; |
268 | |
269 | ret = intel_quark_gpio_setup(pdev); |
270 | if (ret) |
271 | goto err_free_irq_vectors; |
272 | |
273 | ret = mfd_add_devices(parent: &pdev->dev, id: 0, cells: intel_quark_mfd_cells, |
274 | ARRAY_SIZE(intel_quark_mfd_cells), NULL, irq_base: 0, |
275 | NULL); |
276 | if (ret) |
277 | goto err_unregister_gpio_node_group; |
278 | |
279 | return 0; |
280 | |
281 | err_unregister_gpio_node_group: |
282 | software_node_unregister_node_group(node_group: intel_quark_gpio_node_group); |
283 | err_free_irq_vectors: |
284 | pci_free_irq_vectors(dev: pdev); |
285 | err_unregister_i2c_clk: |
286 | intel_quark_unregister_i2c_clk(dev: &pdev->dev); |
287 | return ret; |
288 | } |
289 | |
290 | static void intel_quark_mfd_remove(struct pci_dev *pdev) |
291 | { |
292 | mfd_remove_devices(parent: &pdev->dev); |
293 | software_node_unregister_node_group(node_group: intel_quark_gpio_node_group); |
294 | pci_free_irq_vectors(dev: pdev); |
295 | intel_quark_unregister_i2c_clk(dev: &pdev->dev); |
296 | } |
297 | |
298 | static struct pci_driver intel_quark_mfd_driver = { |
299 | .name = "intel_quark_mfd_i2c_gpio" , |
300 | .id_table = intel_quark_mfd_ids, |
301 | .probe = intel_quark_mfd_probe, |
302 | .remove = intel_quark_mfd_remove, |
303 | }; |
304 | |
305 | module_pci_driver(intel_quark_mfd_driver); |
306 | |
307 | MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>" ); |
308 | MODULE_DESCRIPTION("Intel Quark MFD PCI driver for I2C & GPIO" ); |
309 | MODULE_LICENSE("GPL v2" ); |
310 | |