brintos

brintos / linux-shallow public Read only

0
0
Text · 9.0 KiB · 2d0c734 Raw
402 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Synopsys DesignWare I2C adapter driver.4 *5 * Based on the TI DAVINCI I2C adapter driver.6 *7 * Copyright (C) 2006 Texas Instruments.8 * Copyright (C) 2007 MontaVista Software Inc.9 * Copyright (C) 2009 Provigent Ltd.10 */11#include <linux/clk-provider.h>12#include <linux/clk.h>13#include <linux/delay.h>14#include <linux/dmi.h>15#include <linux/err.h>16#include <linux/errno.h>17#include <linux/i2c.h>18#include <linux/interrupt.h>19#include <linux/io.h>20#include <linux/kernel.h>21#include <linux/mfd/syscon.h>22#include <linux/module.h>23#include <linux/platform_device.h>24#include <linux/pm.h>25#include <linux/pm_runtime.h>26#include <linux/property.h>27#include <linux/regmap.h>28#include <linux/reset.h>29#include <linux/sched.h>30#include <linux/slab.h>31#include <linux/units.h>32 33#include "i2c-designware-core.h"34 35static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)36{37	return clk_get_rate(dev->clk) / KILO;38}39 40#ifdef CONFIG_OF41#define BT1_I2C_CTL			0x10042#define BT1_I2C_CTL_ADDR_MASK		GENMASK(7, 0)43#define BT1_I2C_CTL_WR			BIT(8)44#define BT1_I2C_CTL_GO			BIT(31)45#define BT1_I2C_DI			0x10446#define BT1_I2C_DO			0x10847 48static int bt1_i2c_read(void *context, unsigned int reg, unsigned int *val)49{50	struct dw_i2c_dev *dev = context;51	int ret;52 53	/*54	 * Note these methods shouldn't ever fail because the system controller55	 * registers are memory mapped. We check the return value just in case.56	 */57	ret = regmap_write(dev->sysmap, BT1_I2C_CTL,58			   BT1_I2C_CTL_GO | (reg & BT1_I2C_CTL_ADDR_MASK));59	if (ret)60		return ret;61 62	return regmap_read(dev->sysmap, BT1_I2C_DO, val);63}64 65static int bt1_i2c_write(void *context, unsigned int reg, unsigned int val)66{67	struct dw_i2c_dev *dev = context;68	int ret;69 70	ret = regmap_write(dev->sysmap, BT1_I2C_DI, val);71	if (ret)72		return ret;73 74	return regmap_write(dev->sysmap, BT1_I2C_CTL,75		BT1_I2C_CTL_GO | BT1_I2C_CTL_WR | (reg & BT1_I2C_CTL_ADDR_MASK));76}77 78static const struct regmap_config bt1_i2c_cfg = {79	.reg_bits = 32,80	.val_bits = 32,81	.reg_stride = 4,82	.fast_io = true,83	.reg_read = bt1_i2c_read,84	.reg_write = bt1_i2c_write,85	.max_register = DW_IC_COMP_TYPE,86};87 88static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)89{90	dev->sysmap = syscon_node_to_regmap(dev->dev->of_node->parent);91	if (IS_ERR(dev->sysmap))92		return PTR_ERR(dev->sysmap);93 94	dev->map = devm_regmap_init(dev->dev, NULL, dev, &bt1_i2c_cfg);95	return PTR_ERR_OR_ZERO(dev->map);96}97#else98static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)99{100	return -ENODEV;101}102#endif103 104static int txgbe_i2c_request_regs(struct dw_i2c_dev *dev)105{106	dev->map = dev_get_regmap(dev->dev->parent, NULL);107	if (!dev->map)108		return -ENODEV;109 110	return 0;111}112 113static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev)114{115	pm_runtime_disable(dev->dev);116 117	if (dev->shared_with_punit)118		pm_runtime_put_noidle(dev->dev);119}120 121static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)122{123	struct platform_device *pdev = to_platform_device(dev->dev);124	int ret;125 126	switch (dev->flags & MODEL_MASK) {127	case MODEL_BAIKAL_BT1:128		ret = bt1_i2c_request_regs(dev);129		break;130	case MODEL_WANGXUN_SP:131		ret = txgbe_i2c_request_regs(dev);132		break;133	default:134		dev->base = devm_platform_ioremap_resource(pdev, 0);135		ret = PTR_ERR_OR_ZERO(dev->base);136		break;137	}138 139	return ret;140}141 142static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {143	{144		.ident = "Qtechnology QT5222",145		.matches = {146			DMI_MATCH(DMI_SYS_VENDOR, "Qtechnology"),147			DMI_MATCH(DMI_PRODUCT_NAME, "QT5222"),148		},149	},150	{ } /* terminate list */151};152 153static const struct i2c_dw_semaphore_callbacks i2c_dw_semaphore_cb_table[] = {154#ifdef CONFIG_I2C_DESIGNWARE_BAYTRAIL155	{156		.probe = i2c_dw_baytrail_probe_lock_support,157	},158#endif159#ifdef CONFIG_I2C_DESIGNWARE_AMDPSP160	{161		.probe = i2c_dw_amdpsp_probe_lock_support,162	},163#endif164	{}165};166 167static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)168{169	const struct i2c_dw_semaphore_callbacks *ptr;170	int i = 0;171	int ret;172 173	dev->semaphore_idx = -1;174 175	for (ptr = i2c_dw_semaphore_cb_table; ptr->probe; ptr++) {176		ret = ptr->probe(dev);177		if (ret) {178			/*179			 * If there is no semaphore device attached to this180			 * controller, we shouldn't abort general i2c_controller181			 * probe.182			 */183			if (ret != -ENODEV)184				return ret;185 186			i++;187			continue;188		}189 190		dev->semaphore_idx = i;191		break;192	}193 194	return 0;195}196 197static void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev)198{199	if (dev->semaphore_idx < 0)200		return;201 202	if (i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove)203		i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove(dev);204}205 206static int dw_i2c_plat_probe(struct platform_device *pdev)207{208	struct i2c_adapter *adap;209	struct dw_i2c_dev *dev;210	int irq, ret;211 212	irq = platform_get_irq(pdev, 0);213	if (irq < 0)214		return irq;215 216	dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);217	if (!dev)218		return -ENOMEM;219 220	dev->flags = (uintptr_t)device_get_match_data(&pdev->dev);221	if (device_property_present(&pdev->dev, "wx,i2c-snps-model"))222		dev->flags = MODEL_WANGXUN_SP | ACCESS_POLLING;223 224	dev->dev = &pdev->dev;225	dev->irq = irq;226	platform_set_drvdata(pdev, dev);227 228	ret = dw_i2c_plat_request_regs(dev);229	if (ret)230		return ret;231 232	dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);233	if (IS_ERR(dev->rst))234		return PTR_ERR(dev->rst);235 236	reset_control_deassert(dev->rst);237 238	ret = i2c_dw_fw_parse_and_configure(dev);239	if (ret)240		goto exit_reset;241 242	ret = i2c_dw_probe_lock_support(dev);243	if (ret)244		goto exit_reset;245 246	i2c_dw_configure(dev);247 248	/* Optional interface clock */249	dev->pclk = devm_clk_get_optional(&pdev->dev, "pclk");250	if (IS_ERR(dev->pclk)) {251		ret = PTR_ERR(dev->pclk);252		goto exit_reset;253	}254 255	dev->clk = devm_clk_get_optional(&pdev->dev, NULL);256	if (IS_ERR(dev->clk)) {257		ret = PTR_ERR(dev->clk);258		goto exit_reset;259	}260 261	ret = i2c_dw_prepare_clk(dev, true);262	if (ret)263		goto exit_reset;264 265	if (dev->clk) {266		struct i2c_timings *t = &dev->timings;267		u64 clk_khz;268 269		dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;270		clk_khz = dev->get_clk_rate_khz(dev);271 272		if (!dev->sda_hold_time && t->sda_hold_ns)273			dev->sda_hold_time =274				DIV_S64_ROUND_CLOSEST(clk_khz * t->sda_hold_ns, MICRO);275	}276 277	adap = &dev->adapter;278	adap->owner = THIS_MODULE;279	adap->class = dmi_check_system(dw_i2c_hwmon_class_dmi) ?280					I2C_CLASS_HWMON : I2C_CLASS_DEPRECATED;281	adap->nr = -1;282 283	if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {284		dev_pm_set_driver_flags(&pdev->dev,285					DPM_FLAG_SMART_PREPARE);286	} else {287		dev_pm_set_driver_flags(&pdev->dev,288					DPM_FLAG_SMART_PREPARE |289					DPM_FLAG_SMART_SUSPEND);290	}291 292	device_enable_async_suspend(&pdev->dev);293 294	/* The code below assumes runtime PM to be disabled. */295	WARN_ON(pm_runtime_enabled(&pdev->dev));296 297	pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);298	pm_runtime_use_autosuspend(&pdev->dev);299	pm_runtime_set_active(&pdev->dev);300 301	if (dev->shared_with_punit)302		pm_runtime_get_noresume(&pdev->dev);303 304	pm_runtime_enable(&pdev->dev);305 306	ret = i2c_dw_probe(dev);307	if (ret)308		goto exit_probe;309 310	return ret;311 312exit_probe:313	dw_i2c_plat_pm_cleanup(dev);314exit_reset:315	reset_control_assert(dev->rst);316	return ret;317}318 319static void dw_i2c_plat_remove(struct platform_device *pdev)320{321	struct dw_i2c_dev *dev = platform_get_drvdata(pdev);322 323	pm_runtime_get_sync(&pdev->dev);324 325	i2c_del_adapter(&dev->adapter);326 327	i2c_dw_disable(dev);328 329	pm_runtime_dont_use_autosuspend(&pdev->dev);330	pm_runtime_put_sync(&pdev->dev);331	dw_i2c_plat_pm_cleanup(dev);332 333	i2c_dw_remove_lock_support(dev);334 335	reset_control_assert(dev->rst);336}337 338static const struct of_device_id dw_i2c_of_match[] = {339	{ .compatible = "snps,designware-i2c", },340	{ .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },341	{ .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },342	{}343};344MODULE_DEVICE_TABLE(of, dw_i2c_of_match);345 346static const struct acpi_device_id dw_i2c_acpi_match[] = {347	{ "80860F41", ACCESS_NO_IRQ_SUSPEND },348	{ "808622C1", ACCESS_NO_IRQ_SUSPEND },349	{ "AMD0010", ACCESS_INTR_MASK },350	{ "AMDI0010", ACCESS_INTR_MASK },351	{ "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE },352	{ "AMDI0510", 0 },353	{ "APMC0D0F", 0 },354	{ "HISI02A1", 0 },355	{ "HISI02A2", 0 },356	{ "HISI02A3", 0 },357	{ "HYGO0010", ACCESS_INTR_MASK },358	{ "INT33C2", 0 },359	{ "INT33C3", 0 },360	{ "INT3432", 0 },361	{ "INT3433", 0 },362	{ "INTC10EF", 0 },363	{}364};365MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);366 367static const struct platform_device_id dw_i2c_platform_ids[] = {368	{ "i2c_designware" },369	{}370};371MODULE_DEVICE_TABLE(platform, dw_i2c_platform_ids);372 373static struct platform_driver dw_i2c_driver = {374	.probe = dw_i2c_plat_probe,375	.remove_new = dw_i2c_plat_remove,376	.driver		= {377		.name	= "i2c_designware",378		.of_match_table = dw_i2c_of_match,379		.acpi_match_table = dw_i2c_acpi_match,380		.pm	= pm_ptr(&i2c_dw_dev_pm_ops),381	},382	.id_table = dw_i2c_platform_ids,383};384 385static int __init dw_i2c_init_driver(void)386{387	return platform_driver_register(&dw_i2c_driver);388}389subsys_initcall(dw_i2c_init_driver);390 391static void __exit dw_i2c_exit_driver(void)392{393	platform_driver_unregister(&dw_i2c_driver);394}395module_exit(dw_i2c_exit_driver);396 397MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");398MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");399MODULE_LICENSE("GPL");400MODULE_IMPORT_NS(I2C_DW);401MODULE_IMPORT_NS(I2C_DW_COMMON);402