xref: /openbmc/linux/drivers/mfd/intel_quark_i2c_gpio.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
260ae5b9fSRaymond Tan /*
360ae5b9fSRaymond Tan  * Intel Quark MFD PCI driver for I2C & GPIO
460ae5b9fSRaymond Tan  *
560ae5b9fSRaymond Tan  * Copyright(c) 2014 Intel Corporation.
660ae5b9fSRaymond Tan  *
760ae5b9fSRaymond Tan  * Intel Quark PCI device for I2C and GPIO controller sharing the same
860ae5b9fSRaymond Tan  * PCI function. This PCI driver will split the 2 devices into their
960ae5b9fSRaymond Tan  * respective drivers.
1060ae5b9fSRaymond Tan  */
1160ae5b9fSRaymond Tan 
1260ae5b9fSRaymond Tan #include <linux/kernel.h>
1360ae5b9fSRaymond Tan #include <linux/module.h>
1460ae5b9fSRaymond Tan #include <linux/pci.h>
1560ae5b9fSRaymond Tan #include <linux/mfd/core.h>
1660ae5b9fSRaymond Tan #include <linux/clkdev.h>
1760ae5b9fSRaymond Tan #include <linux/clk-provider.h>
1860ae5b9fSRaymond Tan #include <linux/dmi.h>
1910d82adeSAndy Shevchenko #include <linux/i2c.h>
20c58ddd29SAndy Shevchenko #include <linux/property.h>
2160ae5b9fSRaymond Tan 
2260ae5b9fSRaymond Tan /* PCI BAR for register base address */
2360ae5b9fSRaymond Tan #define MFD_I2C_BAR		0
2460ae5b9fSRaymond Tan #define MFD_GPIO_BAR		1
2560ae5b9fSRaymond Tan 
26918fe70cSAndy Shevchenko /* ACPI _ADR value to match the child node */
27918fe70cSAndy Shevchenko #define MFD_ACPI_MATCH_GPIO	0ULL
28918fe70cSAndy Shevchenko #define MFD_ACPI_MATCH_I2C	1ULL
29918fe70cSAndy Shevchenko 
3060ae5b9fSRaymond Tan #define INTEL_QUARK_IORES_MEM	0
3160ae5b9fSRaymond Tan #define INTEL_QUARK_IORES_IRQ	1
3260ae5b9fSRaymond Tan 
3360ae5b9fSRaymond Tan #define INTEL_QUARK_I2C_CONTROLLER_CLK "i2c_designware.0"
3460ae5b9fSRaymond Tan 
3560ae5b9fSRaymond Tan /* The Quark I2C controller source clock */
3660ae5b9fSRaymond Tan #define INTEL_QUARK_I2C_CLK_HZ	33000000
3760ae5b9fSRaymond Tan 
3860ae5b9fSRaymond Tan struct intel_quark_mfd {
3960ae5b9fSRaymond Tan 	struct clk		*i2c_clk;
4060ae5b9fSRaymond Tan 	struct clk_lookup	*i2c_clk_lookup;
4160ae5b9fSRaymond Tan };
4260ae5b9fSRaymond Tan 
43c58ddd29SAndy Shevchenko static const struct property_entry intel_quark_i2c_controller_standard_properties[] = {
44c58ddd29SAndy Shevchenko 	PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_STANDARD_MODE_FREQ),
45c58ddd29SAndy Shevchenko 	{ }
46c58ddd29SAndy Shevchenko };
47c58ddd29SAndy Shevchenko 
48c58ddd29SAndy Shevchenko static const struct software_node intel_quark_i2c_controller_standard_node = {
49c58ddd29SAndy Shevchenko 	.name = "intel-quark-i2c-controller",
50c58ddd29SAndy Shevchenko 	.properties = intel_quark_i2c_controller_standard_properties,
51c58ddd29SAndy Shevchenko };
52c58ddd29SAndy Shevchenko 
53c58ddd29SAndy Shevchenko static const struct property_entry intel_quark_i2c_controller_fast_properties[] = {
54c58ddd29SAndy Shevchenko 	PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_FAST_MODE_FREQ),
55c58ddd29SAndy Shevchenko 	{ }
56c58ddd29SAndy Shevchenko };
57c58ddd29SAndy Shevchenko 
58c58ddd29SAndy Shevchenko static const struct software_node intel_quark_i2c_controller_fast_node = {
59c58ddd29SAndy Shevchenko 	.name = "intel-quark-i2c-controller",
60c58ddd29SAndy Shevchenko 	.properties = intel_quark_i2c_controller_fast_properties,
61c58ddd29SAndy Shevchenko };
62c58ddd29SAndy Shevchenko 
63b518d4adSJan Kiszka static const struct dmi_system_id dmi_platform_info[] = {
6460ae5b9fSRaymond Tan 	{
65b518d4adSJan Kiszka 		.matches = {
66b518d4adSJan Kiszka 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "Galileo"),
67b518d4adSJan Kiszka 		},
68c58ddd29SAndy Shevchenko 		.driver_data = (void *)&intel_quark_i2c_controller_standard_node,
6960ae5b9fSRaymond Tan 	},
7060ae5b9fSRaymond Tan 	{
71b518d4adSJan Kiszka 		.matches = {
72b518d4adSJan Kiszka 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"),
73b518d4adSJan Kiszka 		},
74c58ddd29SAndy Shevchenko 		.driver_data = (void *)&intel_quark_i2c_controller_fast_node,
7560ae5b9fSRaymond Tan 	},
76842086d2SJan Kiszka 	{
77842086d2SJan Kiszka 		.matches = {
78842086d2SJan Kiszka 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
79842086d2SJan Kiszka 		},
80c58ddd29SAndy Shevchenko 		.driver_data = (void *)&intel_quark_i2c_controller_fast_node,
81842086d2SJan Kiszka 	},
82bafc1facSAndy Shevchenko 	{}
8360ae5b9fSRaymond Tan };
8460ae5b9fSRaymond Tan 
85a61f4661SAndy Shevchenko /* This is used as a place holder and will be modified at run-time */
86a61f4661SAndy Shevchenko static struct resource intel_quark_i2c_res[] = {
8760ae5b9fSRaymond Tan 	[INTEL_QUARK_IORES_MEM] = {
8860ae5b9fSRaymond Tan 		.flags = IORESOURCE_MEM,
8960ae5b9fSRaymond Tan 	},
9060ae5b9fSRaymond Tan 	[INTEL_QUARK_IORES_IRQ] = {
9160ae5b9fSRaymond Tan 		.flags = IORESOURCE_IRQ,
9260ae5b9fSRaymond Tan 	},
9360ae5b9fSRaymond Tan };
9460ae5b9fSRaymond Tan 
95918fe70cSAndy Shevchenko static struct mfd_cell_acpi_match intel_quark_acpi_match_i2c = {
96918fe70cSAndy Shevchenko 	.adr = MFD_ACPI_MATCH_I2C,
97918fe70cSAndy Shevchenko };
98918fe70cSAndy Shevchenko 
99a61f4661SAndy Shevchenko /* This is used as a place holder and will be modified at run-time */
100a61f4661SAndy Shevchenko static struct resource intel_quark_gpio_res[] = {
10160ae5b9fSRaymond Tan 	[INTEL_QUARK_IORES_MEM] = {
10260ae5b9fSRaymond Tan 		.flags = IORESOURCE_MEM,
10360ae5b9fSRaymond Tan 	},
104*36edadf5SAndy Shevchenko 	[INTEL_QUARK_IORES_IRQ] = {
105*36edadf5SAndy Shevchenko 		.flags = IORESOURCE_IRQ,
106*36edadf5SAndy Shevchenko 	},
10760ae5b9fSRaymond Tan };
10860ae5b9fSRaymond Tan 
109918fe70cSAndy Shevchenko static struct mfd_cell_acpi_match intel_quark_acpi_match_gpio = {
110918fe70cSAndy Shevchenko 	.adr = MFD_ACPI_MATCH_GPIO,
111918fe70cSAndy Shevchenko };
112918fe70cSAndy Shevchenko 
113*36edadf5SAndy Shevchenko static const struct software_node intel_quark_gpio_controller_node = {
114*36edadf5SAndy Shevchenko 	.name = "intel-quark-gpio-controller",
115*36edadf5SAndy Shevchenko };
116*36edadf5SAndy Shevchenko 
117*36edadf5SAndy Shevchenko static const struct property_entry intel_quark_gpio_portA_properties[] = {
118*36edadf5SAndy Shevchenko 	PROPERTY_ENTRY_U32("reg", 0),
119*36edadf5SAndy Shevchenko 	PROPERTY_ENTRY_U32("snps,nr-gpios", 8),
120*36edadf5SAndy Shevchenko 	PROPERTY_ENTRY_U32("gpio-base", 8),
121*36edadf5SAndy Shevchenko 	{ }
122*36edadf5SAndy Shevchenko };
123*36edadf5SAndy Shevchenko 
124*36edadf5SAndy Shevchenko static const struct software_node intel_quark_gpio_portA_node = {
125*36edadf5SAndy Shevchenko 	.name = "portA",
126*36edadf5SAndy Shevchenko 	.parent = &intel_quark_gpio_controller_node,
127*36edadf5SAndy Shevchenko 	.properties = intel_quark_gpio_portA_properties,
128*36edadf5SAndy Shevchenko };
129*36edadf5SAndy Shevchenko 
130*36edadf5SAndy Shevchenko static const struct software_node *intel_quark_gpio_node_group[] = {
131*36edadf5SAndy Shevchenko 	&intel_quark_gpio_controller_node,
132*36edadf5SAndy Shevchenko 	&intel_quark_gpio_portA_node,
133*36edadf5SAndy Shevchenko 	NULL
134*36edadf5SAndy Shevchenko };
135*36edadf5SAndy Shevchenko 
13660ae5b9fSRaymond Tan static struct mfd_cell intel_quark_mfd_cells[] = {
13798d6e7fcSAndy Shevchenko 	[MFD_I2C_BAR] = {
138236fd469SAndy Shevchenko 		.id = MFD_I2C_BAR,
139236fd469SAndy Shevchenko 		.name = "i2c_designware",
140918fe70cSAndy Shevchenko 		.acpi_match = &intel_quark_acpi_match_i2c,
141236fd469SAndy Shevchenko 		.num_resources = ARRAY_SIZE(intel_quark_i2c_res),
142236fd469SAndy Shevchenko 		.resources = intel_quark_i2c_res,
143236fd469SAndy Shevchenko 		.ignore_resource_conflicts = true,
144236fd469SAndy Shevchenko 	},
14598d6e7fcSAndy Shevchenko 	[MFD_GPIO_BAR] = {
14698d6e7fcSAndy Shevchenko 		.id = MFD_GPIO_BAR,
14798d6e7fcSAndy Shevchenko 		.name = "gpio-dwapb",
14898d6e7fcSAndy Shevchenko 		.acpi_match = &intel_quark_acpi_match_gpio,
14998d6e7fcSAndy Shevchenko 		.num_resources = ARRAY_SIZE(intel_quark_gpio_res),
15098d6e7fcSAndy Shevchenko 		.resources = intel_quark_gpio_res,
15198d6e7fcSAndy Shevchenko 		.ignore_resource_conflicts = true,
15298d6e7fcSAndy Shevchenko 	},
15360ae5b9fSRaymond Tan };
15460ae5b9fSRaymond Tan 
15560ae5b9fSRaymond Tan static const struct pci_device_id intel_quark_mfd_ids[] = {
15660ae5b9fSRaymond Tan 	{ PCI_VDEVICE(INTEL, 0x0934), },
15760ae5b9fSRaymond Tan 	{},
15860ae5b9fSRaymond Tan };
15960ae5b9fSRaymond Tan MODULE_DEVICE_TABLE(pci, intel_quark_mfd_ids);
16060ae5b9fSRaymond Tan 
intel_quark_register_i2c_clk(struct device * dev)1619caac886SAndy Shevchenko static int intel_quark_register_i2c_clk(struct device *dev)
16260ae5b9fSRaymond Tan {
1639caac886SAndy Shevchenko 	struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev);
16460ae5b9fSRaymond Tan 	struct clk *i2c_clk;
16560ae5b9fSRaymond Tan 
1669caac886SAndy Shevchenko 	i2c_clk = clk_register_fixed_rate(dev,
16760ae5b9fSRaymond Tan 					  INTEL_QUARK_I2C_CONTROLLER_CLK, NULL,
16836a0c088SStephen Boyd 					  0, INTEL_QUARK_I2C_CLK_HZ);
169c4726abcSStephen Boyd 	if (IS_ERR(i2c_clk))
170c4726abcSStephen Boyd 		return PTR_ERR(i2c_clk);
17160ae5b9fSRaymond Tan 
17260ae5b9fSRaymond Tan 	quark_mfd->i2c_clk = i2c_clk;
173c4726abcSStephen Boyd 	quark_mfd->i2c_clk_lookup = clkdev_create(i2c_clk, NULL,
174c4726abcSStephen Boyd 						INTEL_QUARK_I2C_CONTROLLER_CLK);
17560ae5b9fSRaymond Tan 
176c4726abcSStephen Boyd 	if (!quark_mfd->i2c_clk_lookup) {
1777f0c5ae1SAndy Shevchenko 		clk_unregister(quark_mfd->i2c_clk);
1789caac886SAndy Shevchenko 		dev_err(dev, "Fixed clk register failed\n");
179c4726abcSStephen Boyd 		return -ENOMEM;
180c4726abcSStephen Boyd 	}
18160ae5b9fSRaymond Tan 
182c4726abcSStephen Boyd 	return 0;
18360ae5b9fSRaymond Tan }
18460ae5b9fSRaymond Tan 
intel_quark_unregister_i2c_clk(struct device * dev)1859caac886SAndy Shevchenko static void intel_quark_unregister_i2c_clk(struct device *dev)
18660ae5b9fSRaymond Tan {
1879caac886SAndy Shevchenko 	struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev);
18860ae5b9fSRaymond Tan 
1897f0c5ae1SAndy Shevchenko 	if (!quark_mfd->i2c_clk_lookup)
19060ae5b9fSRaymond Tan 		return;
19160ae5b9fSRaymond Tan 
19260ae5b9fSRaymond Tan 	clkdev_drop(quark_mfd->i2c_clk_lookup);
19360ae5b9fSRaymond Tan 	clk_unregister(quark_mfd->i2c_clk);
19460ae5b9fSRaymond Tan }
19560ae5b9fSRaymond Tan 
intel_quark_i2c_setup(struct pci_dev * pdev)1966dac44c6SAndy Shevchenko static int intel_quark_i2c_setup(struct pci_dev *pdev)
19760ae5b9fSRaymond Tan {
1986dac44c6SAndy Shevchenko 	struct mfd_cell *cell = &intel_quark_mfd_cells[MFD_I2C_BAR];
1996dac44c6SAndy Shevchenko 	struct resource *res = intel_quark_i2c_res;
200b518d4adSJan Kiszka 	const struct dmi_system_id *dmi_id;
20160ae5b9fSRaymond Tan 
2026dac44c6SAndy Shevchenko 	res[INTEL_QUARK_IORES_MEM].start = pci_resource_start(pdev, MFD_I2C_BAR);
2036dac44c6SAndy Shevchenko 	res[INTEL_QUARK_IORES_MEM].end = pci_resource_end(pdev, MFD_I2C_BAR);
20460ae5b9fSRaymond Tan 
20591076ebbSAndy Shevchenko 	res[INTEL_QUARK_IORES_IRQ].start = pci_irq_vector(pdev, 0);
20691076ebbSAndy Shevchenko 	res[INTEL_QUARK_IORES_IRQ].end = pci_irq_vector(pdev, 0);
20760ae5b9fSRaymond Tan 
208bafc1facSAndy Shevchenko 	/* Normal mode by default */
209c58ddd29SAndy Shevchenko 	cell->swnode = &intel_quark_i2c_controller_standard_node;
21060ae5b9fSRaymond Tan 
211b518d4adSJan Kiszka 	dmi_id = dmi_first_match(dmi_platform_info);
212b518d4adSJan Kiszka 	if (dmi_id)
213c58ddd29SAndy Shevchenko 		cell->swnode = (struct software_node *)dmi_id->driver_data;
21460ae5b9fSRaymond Tan 
21560ae5b9fSRaymond Tan 	return 0;
21660ae5b9fSRaymond Tan }
21760ae5b9fSRaymond Tan 
intel_quark_gpio_setup(struct pci_dev * pdev)2186dac44c6SAndy Shevchenko static int intel_quark_gpio_setup(struct pci_dev *pdev)
21960ae5b9fSRaymond Tan {
2206dac44c6SAndy Shevchenko 	struct mfd_cell *cell = &intel_quark_mfd_cells[MFD_GPIO_BAR];
2216dac44c6SAndy Shevchenko 	struct resource *res = intel_quark_gpio_res;
222*36edadf5SAndy Shevchenko 	int ret;
22360ae5b9fSRaymond Tan 
2246dac44c6SAndy Shevchenko 	res[INTEL_QUARK_IORES_MEM].start = pci_resource_start(pdev, MFD_GPIO_BAR);
2256dac44c6SAndy Shevchenko 	res[INTEL_QUARK_IORES_MEM].end = pci_resource_end(pdev, MFD_GPIO_BAR);
22660ae5b9fSRaymond Tan 
227*36edadf5SAndy Shevchenko 	res[INTEL_QUARK_IORES_IRQ].start = pci_irq_vector(pdev, 0);
228*36edadf5SAndy Shevchenko 	res[INTEL_QUARK_IORES_IRQ].end = pci_irq_vector(pdev, 0);
22960ae5b9fSRaymond Tan 
230*36edadf5SAndy Shevchenko 	ret = software_node_register_node_group(intel_quark_gpio_node_group);
231*36edadf5SAndy Shevchenko 	if (ret)
232*36edadf5SAndy Shevchenko 		return ret;
23360ae5b9fSRaymond Tan 
234*36edadf5SAndy Shevchenko 	cell->swnode = &intel_quark_gpio_controller_node;
23560ae5b9fSRaymond Tan 	return 0;
23660ae5b9fSRaymond Tan }
23760ae5b9fSRaymond Tan 
intel_quark_mfd_probe(struct pci_dev * pdev,const struct pci_device_id * id)23860ae5b9fSRaymond Tan static int intel_quark_mfd_probe(struct pci_dev *pdev,
23960ae5b9fSRaymond Tan 				 const struct pci_device_id *id)
24060ae5b9fSRaymond Tan {
24160ae5b9fSRaymond Tan 	struct intel_quark_mfd *quark_mfd;
24260ae5b9fSRaymond Tan 	int ret;
24360ae5b9fSRaymond Tan 
24460ae5b9fSRaymond Tan 	ret = pcim_enable_device(pdev);
24560ae5b9fSRaymond Tan 	if (ret)
24660ae5b9fSRaymond Tan 		return ret;
24760ae5b9fSRaymond Tan 
24860ae5b9fSRaymond Tan 	quark_mfd = devm_kzalloc(&pdev->dev, sizeof(*quark_mfd), GFP_KERNEL);
24960ae5b9fSRaymond Tan 	if (!quark_mfd)
25060ae5b9fSRaymond Tan 		return -ENOMEM;
25160ae5b9fSRaymond Tan 
2527f0c5ae1SAndy Shevchenko 	dev_set_drvdata(&pdev->dev, quark_mfd);
25360ae5b9fSRaymond Tan 
2549caac886SAndy Shevchenko 	ret = intel_quark_register_i2c_clk(&pdev->dev);
25560ae5b9fSRaymond Tan 	if (ret)
25660ae5b9fSRaymond Tan 		return ret;
25760ae5b9fSRaymond Tan 
25891076ebbSAndy Shevchenko 	pci_set_master(pdev);
25991076ebbSAndy Shevchenko 
26091076ebbSAndy Shevchenko 	/* This driver only requires 1 IRQ vector */
26191076ebbSAndy Shevchenko 	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
26291076ebbSAndy Shevchenko 	if (ret < 0)
2637f0c5ae1SAndy Shevchenko 		goto err_unregister_i2c_clk;
26460ae5b9fSRaymond Tan 
2656dac44c6SAndy Shevchenko 	ret = intel_quark_i2c_setup(pdev);
26660ae5b9fSRaymond Tan 	if (ret)
26791076ebbSAndy Shevchenko 		goto err_free_irq_vectors;
26860ae5b9fSRaymond Tan 
2696dac44c6SAndy Shevchenko 	ret = intel_quark_gpio_setup(pdev);
27060ae5b9fSRaymond Tan 	if (ret)
27191076ebbSAndy Shevchenko 		goto err_free_irq_vectors;
27260ae5b9fSRaymond Tan 
2737f0c5ae1SAndy Shevchenko 	ret = mfd_add_devices(&pdev->dev, 0, intel_quark_mfd_cells,
27460ae5b9fSRaymond Tan 			      ARRAY_SIZE(intel_quark_mfd_cells), NULL, 0,
27560ae5b9fSRaymond Tan 			      NULL);
2767f0c5ae1SAndy Shevchenko 	if (ret)
277*36edadf5SAndy Shevchenko 		goto err_unregister_gpio_node_group;
2787f0c5ae1SAndy Shevchenko 
2797f0c5ae1SAndy Shevchenko 	return 0;
2807f0c5ae1SAndy Shevchenko 
281*36edadf5SAndy Shevchenko err_unregister_gpio_node_group:
282*36edadf5SAndy Shevchenko 	software_node_unregister_node_group(intel_quark_gpio_node_group);
28391076ebbSAndy Shevchenko err_free_irq_vectors:
28491076ebbSAndy Shevchenko 	pci_free_irq_vectors(pdev);
2857f0c5ae1SAndy Shevchenko err_unregister_i2c_clk:
2869caac886SAndy Shevchenko 	intel_quark_unregister_i2c_clk(&pdev->dev);
2877f0c5ae1SAndy Shevchenko 	return ret;
28860ae5b9fSRaymond Tan }
28960ae5b9fSRaymond Tan 
intel_quark_mfd_remove(struct pci_dev * pdev)29060ae5b9fSRaymond Tan static void intel_quark_mfd_remove(struct pci_dev *pdev)
29160ae5b9fSRaymond Tan {
29260ae5b9fSRaymond Tan 	mfd_remove_devices(&pdev->dev);
293*36edadf5SAndy Shevchenko 	software_node_unregister_node_group(intel_quark_gpio_node_group);
29491076ebbSAndy Shevchenko 	pci_free_irq_vectors(pdev);
2951514ce49SAndy Shevchenko 	intel_quark_unregister_i2c_clk(&pdev->dev);
29660ae5b9fSRaymond Tan }
29760ae5b9fSRaymond Tan 
29860ae5b9fSRaymond Tan static struct pci_driver intel_quark_mfd_driver = {
29960ae5b9fSRaymond Tan 	.name		= "intel_quark_mfd_i2c_gpio",
30060ae5b9fSRaymond Tan 	.id_table	= intel_quark_mfd_ids,
30160ae5b9fSRaymond Tan 	.probe		= intel_quark_mfd_probe,
30260ae5b9fSRaymond Tan 	.remove		= intel_quark_mfd_remove,
30360ae5b9fSRaymond Tan };
30460ae5b9fSRaymond Tan 
30560ae5b9fSRaymond Tan module_pci_driver(intel_quark_mfd_driver);
30660ae5b9fSRaymond Tan 
30760ae5b9fSRaymond Tan MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
30860ae5b9fSRaymond Tan MODULE_DESCRIPTION("Intel Quark MFD PCI driver for I2C & GPIO");
30960ae5b9fSRaymond Tan MODULE_LICENSE("GPL v2");
310