18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel Quark MFD PCI driver for I2C & GPIO 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright(c) 2014 Intel Corporation. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Intel Quark PCI device for I2C and GPIO controller sharing the same 88c2ecf20Sopenharmony_ci * PCI function. This PCI driver will split the 2 devices into their 98c2ecf20Sopenharmony_ci * respective drivers. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 168c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 178c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 188c2ecf20Sopenharmony_ci#include <linux/dmi.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_data/gpio-dwapb.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_data/i2c-designware.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* PCI BAR for register base address */ 238c2ecf20Sopenharmony_ci#define MFD_I2C_BAR 0 248c2ecf20Sopenharmony_ci#define MFD_GPIO_BAR 1 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* ACPI _ADR value to match the child node */ 278c2ecf20Sopenharmony_ci#define MFD_ACPI_MATCH_GPIO 0ULL 288c2ecf20Sopenharmony_ci#define MFD_ACPI_MATCH_I2C 1ULL 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* The base GPIO number under GPIOLIB framework */ 318c2ecf20Sopenharmony_ci#define INTEL_QUARK_MFD_GPIO_BASE 8 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* The default number of South-Cluster GPIO on Quark. */ 348c2ecf20Sopenharmony_ci#define INTEL_QUARK_MFD_NGPIO 8 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* The DesignWare GPIO ports on Quark. */ 378c2ecf20Sopenharmony_ci#define INTEL_QUARK_GPIO_NPORTS 1 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define INTEL_QUARK_IORES_MEM 0 408c2ecf20Sopenharmony_ci#define INTEL_QUARK_IORES_IRQ 1 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define INTEL_QUARK_I2C_CONTROLLER_CLK "i2c_designware.0" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* The Quark I2C controller source clock */ 458c2ecf20Sopenharmony_ci#define INTEL_QUARK_I2C_CLK_HZ 33000000 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct intel_quark_mfd { 488c2ecf20Sopenharmony_ci struct device *dev; 498c2ecf20Sopenharmony_ci struct clk *i2c_clk; 508c2ecf20Sopenharmony_ci struct clk_lookup *i2c_clk_lookup; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const struct dmi_system_id dmi_platform_info[] = { 548c2ecf20Sopenharmony_ci { 558c2ecf20Sopenharmony_ci .matches = { 568c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_BOARD_NAME, "Galileo"), 578c2ecf20Sopenharmony_ci }, 588c2ecf20Sopenharmony_ci .driver_data = (void *)100000, 598c2ecf20Sopenharmony_ci }, 608c2ecf20Sopenharmony_ci { 618c2ecf20Sopenharmony_ci .matches = { 628c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"), 638c2ecf20Sopenharmony_ci }, 648c2ecf20Sopenharmony_ci .driver_data = (void *)400000, 658c2ecf20Sopenharmony_ci }, 668c2ecf20Sopenharmony_ci { 678c2ecf20Sopenharmony_ci .matches = { 688c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), 698c2ecf20Sopenharmony_ci }, 708c2ecf20Sopenharmony_ci .driver_data = (void *)400000, 718c2ecf20Sopenharmony_ci }, 728c2ecf20Sopenharmony_ci {} 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic struct resource intel_quark_i2c_res[] = { 768c2ecf20Sopenharmony_ci [INTEL_QUARK_IORES_MEM] = { 778c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 788c2ecf20Sopenharmony_ci }, 798c2ecf20Sopenharmony_ci [INTEL_QUARK_IORES_IRQ] = { 808c2ecf20Sopenharmony_ci .flags = IORESOURCE_IRQ, 818c2ecf20Sopenharmony_ci }, 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic struct mfd_cell_acpi_match intel_quark_acpi_match_i2c = { 858c2ecf20Sopenharmony_ci .adr = MFD_ACPI_MATCH_I2C, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic struct resource intel_quark_gpio_res[] = { 898c2ecf20Sopenharmony_ci [INTEL_QUARK_IORES_MEM] = { 908c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 918c2ecf20Sopenharmony_ci }, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct mfd_cell_acpi_match intel_quark_acpi_match_gpio = { 958c2ecf20Sopenharmony_ci .adr = MFD_ACPI_MATCH_GPIO, 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic struct mfd_cell intel_quark_mfd_cells[] = { 998c2ecf20Sopenharmony_ci { 1008c2ecf20Sopenharmony_ci .id = MFD_GPIO_BAR, 1018c2ecf20Sopenharmony_ci .name = "gpio-dwapb", 1028c2ecf20Sopenharmony_ci .acpi_match = &intel_quark_acpi_match_gpio, 1038c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(intel_quark_gpio_res), 1048c2ecf20Sopenharmony_ci .resources = intel_quark_gpio_res, 1058c2ecf20Sopenharmony_ci .ignore_resource_conflicts = true, 1068c2ecf20Sopenharmony_ci }, 1078c2ecf20Sopenharmony_ci { 1088c2ecf20Sopenharmony_ci .id = MFD_I2C_BAR, 1098c2ecf20Sopenharmony_ci .name = "i2c_designware", 1108c2ecf20Sopenharmony_ci .acpi_match = &intel_quark_acpi_match_i2c, 1118c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(intel_quark_i2c_res), 1128c2ecf20Sopenharmony_ci .resources = intel_quark_i2c_res, 1138c2ecf20Sopenharmony_ci .ignore_resource_conflicts = true, 1148c2ecf20Sopenharmony_ci }, 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic const struct pci_device_id intel_quark_mfd_ids[] = { 1188c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, 0x0934), }, 1198c2ecf20Sopenharmony_ci {}, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, intel_quark_mfd_ids); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int intel_quark_register_i2c_clk(struct device *dev) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev); 1268c2ecf20Sopenharmony_ci struct clk *i2c_clk; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci i2c_clk = clk_register_fixed_rate(dev, 1298c2ecf20Sopenharmony_ci INTEL_QUARK_I2C_CONTROLLER_CLK, NULL, 1308c2ecf20Sopenharmony_ci 0, INTEL_QUARK_I2C_CLK_HZ); 1318c2ecf20Sopenharmony_ci if (IS_ERR(i2c_clk)) 1328c2ecf20Sopenharmony_ci return PTR_ERR(i2c_clk); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci quark_mfd->i2c_clk = i2c_clk; 1358c2ecf20Sopenharmony_ci quark_mfd->i2c_clk_lookup = clkdev_create(i2c_clk, NULL, 1368c2ecf20Sopenharmony_ci INTEL_QUARK_I2C_CONTROLLER_CLK); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (!quark_mfd->i2c_clk_lookup) { 1398c2ecf20Sopenharmony_ci clk_unregister(quark_mfd->i2c_clk); 1408c2ecf20Sopenharmony_ci dev_err(dev, "Fixed clk register failed\n"); 1418c2ecf20Sopenharmony_ci return -ENOMEM; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void intel_quark_unregister_i2c_clk(struct device *dev) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (!quark_mfd->i2c_clk_lookup) 1528c2ecf20Sopenharmony_ci return; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci clkdev_drop(quark_mfd->i2c_clk_lookup); 1558c2ecf20Sopenharmony_ci clk_unregister(quark_mfd->i2c_clk); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int intel_quark_i2c_setup(struct pci_dev *pdev, struct mfd_cell *cell) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci const struct dmi_system_id *dmi_id; 1618c2ecf20Sopenharmony_ci struct dw_i2c_platform_data *pdata; 1628c2ecf20Sopenharmony_ci struct resource *res = (struct resource *)cell->resources; 1638c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci res[INTEL_QUARK_IORES_MEM].start = 1668c2ecf20Sopenharmony_ci pci_resource_start(pdev, MFD_I2C_BAR); 1678c2ecf20Sopenharmony_ci res[INTEL_QUARK_IORES_MEM].end = 1688c2ecf20Sopenharmony_ci pci_resource_end(pdev, MFD_I2C_BAR); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci res[INTEL_QUARK_IORES_IRQ].start = pdev->irq; 1718c2ecf20Sopenharmony_ci res[INTEL_QUARK_IORES_IRQ].end = pdev->irq; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 1748c2ecf20Sopenharmony_ci if (!pdata) 1758c2ecf20Sopenharmony_ci return -ENOMEM; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* Normal mode by default */ 1788c2ecf20Sopenharmony_ci pdata->i2c_scl_freq = 100000; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci dmi_id = dmi_first_match(dmi_platform_info); 1818c2ecf20Sopenharmony_ci if (dmi_id) 1828c2ecf20Sopenharmony_ci pdata->i2c_scl_freq = (uintptr_t)dmi_id->driver_data; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci cell->platform_data = pdata; 1858c2ecf20Sopenharmony_ci cell->pdata_size = sizeof(*pdata); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int intel_quark_gpio_setup(struct pci_dev *pdev, struct mfd_cell *cell) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct dwapb_platform_data *pdata; 1938c2ecf20Sopenharmony_ci struct resource *res = (struct resource *)cell->resources; 1948c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci res[INTEL_QUARK_IORES_MEM].start = 1978c2ecf20Sopenharmony_ci pci_resource_start(pdev, MFD_GPIO_BAR); 1988c2ecf20Sopenharmony_ci res[INTEL_QUARK_IORES_MEM].end = 1998c2ecf20Sopenharmony_ci pci_resource_end(pdev, MFD_GPIO_BAR); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 2028c2ecf20Sopenharmony_ci if (!pdata) 2038c2ecf20Sopenharmony_ci return -ENOMEM; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* For intel quark x1000, it has only one port: portA */ 2068c2ecf20Sopenharmony_ci pdata->nports = INTEL_QUARK_GPIO_NPORTS; 2078c2ecf20Sopenharmony_ci pdata->properties = devm_kcalloc(dev, pdata->nports, 2088c2ecf20Sopenharmony_ci sizeof(*pdata->properties), 2098c2ecf20Sopenharmony_ci GFP_KERNEL); 2108c2ecf20Sopenharmony_ci if (!pdata->properties) 2118c2ecf20Sopenharmony_ci return -ENOMEM; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Set the properties for portA */ 2148c2ecf20Sopenharmony_ci pdata->properties->fwnode = NULL; 2158c2ecf20Sopenharmony_ci pdata->properties->idx = 0; 2168c2ecf20Sopenharmony_ci pdata->properties->ngpio = INTEL_QUARK_MFD_NGPIO; 2178c2ecf20Sopenharmony_ci pdata->properties->gpio_base = INTEL_QUARK_MFD_GPIO_BASE; 2188c2ecf20Sopenharmony_ci pdata->properties->irq[0] = pdev->irq; 2198c2ecf20Sopenharmony_ci pdata->properties->irq_shared = true; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci cell->platform_data = pdata; 2228c2ecf20Sopenharmony_ci cell->pdata_size = sizeof(*pdata); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int intel_quark_mfd_probe(struct pci_dev *pdev, 2288c2ecf20Sopenharmony_ci const struct pci_device_id *id) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct intel_quark_mfd *quark_mfd; 2318c2ecf20Sopenharmony_ci int ret; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci ret = pcim_enable_device(pdev); 2348c2ecf20Sopenharmony_ci if (ret) 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci quark_mfd = devm_kzalloc(&pdev->dev, sizeof(*quark_mfd), GFP_KERNEL); 2388c2ecf20Sopenharmony_ci if (!quark_mfd) 2398c2ecf20Sopenharmony_ci return -ENOMEM; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci quark_mfd->dev = &pdev->dev; 2428c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, quark_mfd); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ret = intel_quark_register_i2c_clk(&pdev->dev); 2458c2ecf20Sopenharmony_ci if (ret) 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = intel_quark_i2c_setup(pdev, &intel_quark_mfd_cells[1]); 2498c2ecf20Sopenharmony_ci if (ret) 2508c2ecf20Sopenharmony_ci goto err_unregister_i2c_clk; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ret = intel_quark_gpio_setup(pdev, &intel_quark_mfd_cells[0]); 2538c2ecf20Sopenharmony_ci if (ret) 2548c2ecf20Sopenharmony_ci goto err_unregister_i2c_clk; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ret = mfd_add_devices(&pdev->dev, 0, intel_quark_mfd_cells, 2578c2ecf20Sopenharmony_ci ARRAY_SIZE(intel_quark_mfd_cells), NULL, 0, 2588c2ecf20Sopenharmony_ci NULL); 2598c2ecf20Sopenharmony_ci if (ret) 2608c2ecf20Sopenharmony_ci goto err_unregister_i2c_clk; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cierr_unregister_i2c_clk: 2658c2ecf20Sopenharmony_ci intel_quark_unregister_i2c_clk(&pdev->dev); 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void intel_quark_mfd_remove(struct pci_dev *pdev) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci intel_quark_unregister_i2c_clk(&pdev->dev); 2728c2ecf20Sopenharmony_ci mfd_remove_devices(&pdev->dev); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic struct pci_driver intel_quark_mfd_driver = { 2768c2ecf20Sopenharmony_ci .name = "intel_quark_mfd_i2c_gpio", 2778c2ecf20Sopenharmony_ci .id_table = intel_quark_mfd_ids, 2788c2ecf20Sopenharmony_ci .probe = intel_quark_mfd_probe, 2798c2ecf20Sopenharmony_ci .remove = intel_quark_mfd_remove, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cimodule_pci_driver(intel_quark_mfd_driver); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>"); 2858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Quark MFD PCI driver for I2C & GPIO"); 2868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 287