Commit | Line | Data |
---|---|---|
2025cf9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
60ae5b9f RT |
2 | /* |
3 | * Intel Quark MFD PCI driver for I2C & GPIO | |
4 | * | |
5 | * Copyright(c) 2014 Intel Corporation. | |
6 | * | |
60ae5b9f RT |
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> | |
10d82ade | 19 | #include <linux/i2c.h> |
c58ddd29 | 20 | #include <linux/property.h> |
60ae5b9f RT |
21 | |
22 | /* PCI BAR for register base address */ | |
23 | #define MFD_I2C_BAR 0 | |
24 | #define MFD_GPIO_BAR 1 | |
25 | ||
918fe70c AS |
26 | /* ACPI _ADR value to match the child node */ |
27 | #define MFD_ACPI_MATCH_GPIO 0ULL | |
28 | #define MFD_ACPI_MATCH_I2C 1ULL | |
29 | ||
60ae5b9f RT |
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 | ||
60ae5b9f | 38 | struct intel_quark_mfd { |
60ae5b9f RT |
39 | struct clk *i2c_clk; |
40 | struct clk_lookup *i2c_clk_lookup; | |
41 | }; | |
42 | ||
c58ddd29 AS |
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 | ||
b518d4ad | 63 | static const struct dmi_system_id dmi_platform_info[] = { |
60ae5b9f | 64 | { |
b518d4ad JK |
65 | .matches = { |
66 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "Galileo"), | |
67 | }, | |
c58ddd29 | 68 | .driver_data = (void *)&intel_quark_i2c_controller_standard_node, |
60ae5b9f RT |
69 | }, |
70 | { | |
b518d4ad JK |
71 | .matches = { |
72 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"), | |
73 | }, | |
c58ddd29 | 74 | .driver_data = (void *)&intel_quark_i2c_controller_fast_node, |
60ae5b9f | 75 | }, |
842086d2 JK |
76 | { |
77 | .matches = { | |
78 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), | |
842086d2 | 79 | }, |
c58ddd29 | 80 | .driver_data = (void *)&intel_quark_i2c_controller_fast_node, |
842086d2 | 81 | }, |
bafc1fac | 82 | {} |
60ae5b9f RT |
83 | }; |
84 | ||
a61f4661 AS |
85 | /* This is used as a place holder and will be modified at run-time */ |
86 | static struct resource intel_quark_i2c_res[] = { | |
60ae5b9f RT |
87 | [INTEL_QUARK_IORES_MEM] = { |
88 | .flags = IORESOURCE_MEM, | |
89 | }, | |
90 | [INTEL_QUARK_IORES_IRQ] = { | |
91 | .flags = IORESOURCE_IRQ, | |
92 | }, | |
93 | }; | |
94 | ||
918fe70c AS |
95 | static struct mfd_cell_acpi_match intel_quark_acpi_match_i2c = { |
96 | .adr = MFD_ACPI_MATCH_I2C, | |
97 | }; | |
98 | ||
a61f4661 AS |
99 | /* This is used as a place holder and will be modified at run-time */ |
100 | static struct resource intel_quark_gpio_res[] = { | |
60ae5b9f RT |
101 | [INTEL_QUARK_IORES_MEM] = { |
102 | .flags = IORESOURCE_MEM, | |
103 | }, | |
36edadf5 AS |
104 | [INTEL_QUARK_IORES_IRQ] = { |
105 | .flags = IORESOURCE_IRQ, | |
106 | }, | |
60ae5b9f RT |
107 | }; |
108 | ||
918fe70c AS |
109 | static struct mfd_cell_acpi_match intel_quark_acpi_match_gpio = { |
110 | .adr = MFD_ACPI_MATCH_GPIO, | |
111 | }; | |
112 | ||
36edadf5 AS |
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 | ||
60ae5b9f | 136 | static struct mfd_cell intel_quark_mfd_cells[] = { |
98d6e7fc | 137 | [MFD_I2C_BAR] = { |
236fd469 AS |
138 | .id = MFD_I2C_BAR, |
139 | .name = "i2c_designware", | |
918fe70c | 140 | .acpi_match = &intel_quark_acpi_match_i2c, |
236fd469 AS |
141 | .num_resources = ARRAY_SIZE(intel_quark_i2c_res), |
142 | .resources = intel_quark_i2c_res, | |
143 | .ignore_resource_conflicts = true, | |
144 | }, | |
98d6e7fc AS |
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 | }, | |
60ae5b9f RT |
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 | ||
9caac886 | 161 | static int intel_quark_register_i2c_clk(struct device *dev) |
60ae5b9f | 162 | { |
9caac886 | 163 | struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev); |
60ae5b9f | 164 | struct clk *i2c_clk; |
60ae5b9f | 165 | |
9caac886 | 166 | i2c_clk = clk_register_fixed_rate(dev, |
60ae5b9f | 167 | INTEL_QUARK_I2C_CONTROLLER_CLK, NULL, |
36a0c088 | 168 | 0, INTEL_QUARK_I2C_CLK_HZ); |
c4726abc SB |
169 | if (IS_ERR(i2c_clk)) |
170 | return PTR_ERR(i2c_clk); | |
60ae5b9f | 171 | |
60ae5b9f | 172 | quark_mfd->i2c_clk = i2c_clk; |
c4726abc SB |
173 | quark_mfd->i2c_clk_lookup = clkdev_create(i2c_clk, NULL, |
174 | INTEL_QUARK_I2C_CONTROLLER_CLK); | |
60ae5b9f | 175 | |
c4726abc | 176 | if (!quark_mfd->i2c_clk_lookup) { |
7f0c5ae1 | 177 | clk_unregister(quark_mfd->i2c_clk); |
9caac886 | 178 | dev_err(dev, "Fixed clk register failed\n"); |
c4726abc SB |
179 | return -ENOMEM; |
180 | } | |
60ae5b9f | 181 | |
c4726abc | 182 | return 0; |
60ae5b9f RT |
183 | } |
184 | ||
9caac886 | 185 | static void intel_quark_unregister_i2c_clk(struct device *dev) |
60ae5b9f | 186 | { |
9caac886 | 187 | struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev); |
60ae5b9f | 188 | |
7f0c5ae1 | 189 | if (!quark_mfd->i2c_clk_lookup) |
60ae5b9f RT |
190 | return; |
191 | ||
192 | clkdev_drop(quark_mfd->i2c_clk_lookup); | |
193 | clk_unregister(quark_mfd->i2c_clk); | |
194 | } | |
195 | ||
6dac44c6 | 196 | static int intel_quark_i2c_setup(struct pci_dev *pdev) |
60ae5b9f | 197 | { |
6dac44c6 AS |
198 | struct mfd_cell *cell = &intel_quark_mfd_cells[MFD_I2C_BAR]; |
199 | struct resource *res = intel_quark_i2c_res; | |
b518d4ad | 200 | const struct dmi_system_id *dmi_id; |
60ae5b9f | 201 | |
6dac44c6 AS |
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); | |
60ae5b9f | 204 | |
91076ebb AS |
205 | res[INTEL_QUARK_IORES_IRQ].start = pci_irq_vector(pdev, 0); |
206 | res[INTEL_QUARK_IORES_IRQ].end = pci_irq_vector(pdev, 0); | |
60ae5b9f | 207 | |
bafc1fac | 208 | /* Normal mode by default */ |
c58ddd29 | 209 | cell->swnode = &intel_quark_i2c_controller_standard_node; |
bafc1fac | 210 | |
b518d4ad JK |
211 | dmi_id = dmi_first_match(dmi_platform_info); |
212 | if (dmi_id) | |
c58ddd29 | 213 | cell->swnode = (struct software_node *)dmi_id->driver_data; |
60ae5b9f RT |
214 | |
215 | return 0; | |
216 | } | |
217 | ||
6dac44c6 | 218 | static int intel_quark_gpio_setup(struct pci_dev *pdev) |
60ae5b9f | 219 | { |
6dac44c6 AS |
220 | struct mfd_cell *cell = &intel_quark_mfd_cells[MFD_GPIO_BAR]; |
221 | struct resource *res = intel_quark_gpio_res; | |
36edadf5 | 222 | int ret; |
60ae5b9f | 223 | |
6dac44c6 AS |
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); | |
60ae5b9f | 226 | |
36edadf5 AS |
227 | res[INTEL_QUARK_IORES_IRQ].start = pci_irq_vector(pdev, 0); |
228 | res[INTEL_QUARK_IORES_IRQ].end = pci_irq_vector(pdev, 0); | |
60ae5b9f | 229 | |
36edadf5 AS |
230 | ret = software_node_register_node_group(intel_quark_gpio_node_group); |
231 | if (ret) | |
232 | return ret; | |
60ae5b9f | 233 | |
36edadf5 | 234 | cell->swnode = &intel_quark_gpio_controller_node; |
60ae5b9f RT |
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(&pdev->dev, sizeof(*quark_mfd), GFP_KERNEL); | |
249 | if (!quark_mfd) | |
250 | return -ENOMEM; | |
60ae5b9f | 251 | |
7f0c5ae1 | 252 | dev_set_drvdata(&pdev->dev, quark_mfd); |
60ae5b9f | 253 | |
9caac886 | 254 | ret = intel_quark_register_i2c_clk(&pdev->dev); |
60ae5b9f RT |
255 | if (ret) |
256 | return ret; | |
257 | ||
91076ebb AS |
258 | pci_set_master(pdev); |
259 | ||
260 | /* This driver only requires 1 IRQ vector */ | |
261 | ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); | |
262 | if (ret < 0) | |
7f0c5ae1 | 263 | goto err_unregister_i2c_clk; |
60ae5b9f | 264 | |
6dac44c6 | 265 | ret = intel_quark_i2c_setup(pdev); |
60ae5b9f | 266 | if (ret) |
91076ebb | 267 | goto err_free_irq_vectors; |
60ae5b9f | 268 | |
6dac44c6 | 269 | ret = intel_quark_gpio_setup(pdev); |
60ae5b9f | 270 | if (ret) |
91076ebb | 271 | goto err_free_irq_vectors; |
60ae5b9f | 272 | |
7f0c5ae1 AS |
273 | ret = mfd_add_devices(&pdev->dev, 0, intel_quark_mfd_cells, |
274 | ARRAY_SIZE(intel_quark_mfd_cells), NULL, 0, | |
275 | NULL); | |
276 | if (ret) | |
36edadf5 | 277 | goto err_unregister_gpio_node_group; |
7f0c5ae1 AS |
278 | |
279 | return 0; | |
60ae5b9f | 280 | |
36edadf5 AS |
281 | err_unregister_gpio_node_group: |
282 | software_node_unregister_node_group(intel_quark_gpio_node_group); | |
91076ebb AS |
283 | err_free_irq_vectors: |
284 | pci_free_irq_vectors(pdev); | |
7f0c5ae1 | 285 | err_unregister_i2c_clk: |
9caac886 | 286 | intel_quark_unregister_i2c_clk(&pdev->dev); |
7f0c5ae1 | 287 | return ret; |
60ae5b9f RT |
288 | } |
289 | ||
290 | static void intel_quark_mfd_remove(struct pci_dev *pdev) | |
291 | { | |
60ae5b9f | 292 | mfd_remove_devices(&pdev->dev); |
36edadf5 | 293 | software_node_unregister_node_group(intel_quark_gpio_node_group); |
91076ebb | 294 | pci_free_irq_vectors(pdev); |
1514ce49 | 295 | intel_quark_unregister_i2c_clk(&pdev->dev); |
60ae5b9f RT |
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"); |