File Index
Files and directories
Sign in
FILE

i2c-designware-platdrv.c

File preview with syntax highlighting.

linux-6.19.14/drivers/i2c/busses/i2c-designware-platdrv.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  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) / HZ_PER_KHZ;
 38}
 39
 40#ifdef CONFIG_OF
 41#define BT1_I2C_CTL			0x100
 42#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			0x104
 46#define BT1_I2C_DO			0x108
 47
 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 controller
 55	 * 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#else
 98static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
 99{
100	return -ENODEV;
101}
102#endif
103
104static int dw_i2c_get_parent_regmap(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	if (device_is_compatible(dev->dev, "intel,xe-i2c"))
127		return dw_i2c_get_parent_regmap(dev);
128
129	switch (dev->flags & MODEL_MASK) {
130	case MODEL_BAIKAL_BT1:
131		ret = bt1_i2c_request_regs(dev);
132		break;
133	case MODEL_WANGXUN_SP:
134		ret = dw_i2c_get_parent_regmap(dev);
135		break;
136	default:
137		dev->base = devm_platform_ioremap_resource(pdev, 0);
138		ret = PTR_ERR_OR_ZERO(dev->base);
139		break;
140	}
141
142	return ret;
143}
144
145static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
146	{
147		.ident = "Qtechnology QT5222",
148		.matches = {
149			DMI_MATCH(DMI_SYS_VENDOR, "Qtechnology"),
150			DMI_MATCH(DMI_PRODUCT_NAME, "QT5222"),
151		},
152	},
153	{ } /* terminate list */
154};
155
156static const struct i2c_dw_semaphore_callbacks i2c_dw_semaphore_cb_table[] = {
157#ifdef CONFIG_I2C_DESIGNWARE_BAYTRAIL
158	{
159		.probe = i2c_dw_baytrail_probe_lock_support,
160	},
161#endif
162#ifdef CONFIG_I2C_DESIGNWARE_AMDPSP
163	{
164		.probe = i2c_dw_amdpsp_probe_lock_support,
165	},
166#endif
167	{}
168};
169
170static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
171{
172	const struct i2c_dw_semaphore_callbacks *ptr;
173	int i = 0;
174	int ret;
175
176	dev->semaphore_idx = -1;
177
178	for (ptr = i2c_dw_semaphore_cb_table; ptr->probe; ptr++) {
179		ret = ptr->probe(dev);
180		if (ret) {
181			/*
182			 * If there is no semaphore device attached to this
183			 * controller, we shouldn't abort general i2c_controller
184			 * probe.
185			 */
186			if (ret != -ENODEV)
187				return ret;
188
189			i++;
190			continue;
191		}
192
193		dev->semaphore_idx = i;
194		break;
195	}
196
197	return 0;
198}
199
200static int dw_i2c_plat_probe(struct platform_device *pdev)
201{
202	u32 flags = (uintptr_t)device_get_match_data(&pdev->dev);
203	struct device *device = &pdev->dev;
204	struct i2c_adapter *adap;
205	struct dw_i2c_dev *dev;
206	int irq, ret;
207
208	irq = platform_get_irq_optional(pdev, 0);
209	if (irq == -ENXIO)
210		flags |= ACCESS_POLLING;
211	else if (irq < 0)
212		return irq;
213
214	dev = devm_kzalloc(device, sizeof(*dev), GFP_KERNEL);
215	if (!dev)
216		return -ENOMEM;
217
218	if (device_property_present(device, "wx,i2c-snps-model"))
219		flags = MODEL_WANGXUN_SP | ACCESS_POLLING;
220
221	dev->dev = device;
222	dev->irq = irq;
223	dev->flags = flags;
224	platform_set_drvdata(pdev, dev);
225
226	ret = dw_i2c_plat_request_regs(dev);
227	if (ret)
228		return ret;
229
230	dev->rst = devm_reset_control_get_optional_exclusive(device, NULL);
231	if (IS_ERR(dev->rst))
232		return dev_err_probe(device, PTR_ERR(dev->rst), "failed to acquire reset\n");
233
234	reset_control_deassert(dev->rst);
235
236	ret = i2c_dw_fw_parse_and_configure(dev);
237	if (ret)
238		goto exit_reset;
239
240	ret = i2c_dw_probe_lock_support(dev);
241	if (ret) {
242		dev_err_probe(device, ret, "failed to probe lock support\n");
243		goto exit_reset;
244	}
245
246	i2c_dw_configure(dev);
247
248	/* Optional interface clock */
249	dev->pclk = devm_clk_get_optional(device, "pclk");
250	if (IS_ERR(dev->pclk)) {
251		ret = dev_err_probe(device, PTR_ERR(dev->pclk), "failed to acquire pclk\n");
252		goto exit_reset;
253	}
254
255	dev->clk = devm_clk_get_optional(device, NULL);
256	if (IS_ERR(dev->clk)) {
257		ret = dev_err_probe(device, PTR_ERR(dev->clk), "failed to acquire clock\n");
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(device, DPM_FLAG_SMART_PREPARE);
285	else
286		dev_pm_set_driver_flags(device, DPM_FLAG_SMART_PREPARE | DPM_FLAG_SMART_SUSPEND);
287
288	device_enable_async_suspend(device);
289
290	/* The code below assumes runtime PM to be disabled. */
291	WARN_ON(pm_runtime_enabled(device));
292
293	pm_runtime_set_autosuspend_delay(device, 1000);
294	pm_runtime_use_autosuspend(device);
295	pm_runtime_set_active(device);
296
297	if (dev->shared_with_punit)
298		pm_runtime_get_noresume(device);
299
300	pm_runtime_enable(device);
301
302	ret = i2c_dw_probe(dev);
303	if (ret)
304		goto exit_probe;
305
306	return ret;
307
308exit_probe:
309	dw_i2c_plat_pm_cleanup(dev);
310	i2c_dw_prepare_clk(dev, false);
311exit_reset:
312	reset_control_assert(dev->rst);
313	return ret;
314}
315
316static void dw_i2c_plat_remove(struct platform_device *pdev)
317{
318	struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
319	struct device *device = &pdev->dev;
320
321	pm_runtime_get_sync(device);
322
323	i2c_del_adapter(&dev->adapter);
324
325	i2c_dw_disable(dev);
326
327	pm_runtime_dont_use_autosuspend(device);
328	pm_runtime_put_noidle(device);
329	dw_i2c_plat_pm_cleanup(dev);
330
331	i2c_dw_prepare_clk(dev, false);
332
333	reset_control_assert(dev->rst);
334}
335
336static const struct of_device_id dw_i2c_of_match[] = {
337	{ .compatible = "snps,designware-i2c", },
338	{ .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
339	{ .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
340	{}
341};
342MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
343
344static const struct acpi_device_id dw_i2c_acpi_match[] = {
345	{ "80860F41", ACCESS_NO_IRQ_SUSPEND },
346	{ "808622C1", ACCESS_NO_IRQ_SUSPEND },
347	{ "AMD0010", ACCESS_INTR_MASK },
348	{ "AMDI0010", ACCESS_INTR_MASK },
349	{ "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE },
350	{ "AMDI0510", 0 },
351	{ "APMC0D0F", 0 },
352	{ "FUJI200B", 0 },
353	{ "HISI02A1", 0 },
354	{ "HISI02A2", 0 },
355	{ "HISI02A3", 0 },
356	{ "HJMC3001", 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 = 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");
~/ Read-only file preview.