1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Based on Synopsys DesignWare I2C adapter driver.
4 *
5 * Copyright (C) 2025 Advanced Micro Devices, Inc.
6 */
7
8 #include <linux/module.h>
9 #include <linux/platform_device.h>
10 #include <linux/pm_domain.h>
11 #include <linux/pm_runtime.h>
12 #include <linux/soc/amd/isp4_misc.h>
13
14 #include "i2c-designware-core.h"
15
16 #define DRV_NAME "amd_isp_i2c_designware"
17 #define AMD_ISP_I2C_INPUT_CLK 100 /* Mhz */
18
amd_isp_dw_i2c_plat_pm_cleanup(struct dw_i2c_dev * i2c_dev)19 static void amd_isp_dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *i2c_dev)
20 {
21 pm_runtime_disable(i2c_dev->dev);
22 }
23
amd_isp_dw_i2c_get_clk_rate(struct dw_i2c_dev * i2c_dev)24 static inline u32 amd_isp_dw_i2c_get_clk_rate(struct dw_i2c_dev *i2c_dev)
25 {
26 return AMD_ISP_I2C_INPUT_CLK * 1000;
27 }
28
amd_isp_dw_i2c_plat_probe(struct platform_device * pdev)29 static int amd_isp_dw_i2c_plat_probe(struct platform_device *pdev)
30 {
31 struct dw_i2c_dev *isp_i2c_dev;
32 struct i2c_adapter *adap;
33 int ret;
34
35 isp_i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*isp_i2c_dev), GFP_KERNEL);
36 if (!isp_i2c_dev)
37 return -ENOMEM;
38 isp_i2c_dev->dev = &pdev->dev;
39
40 pdev->dev.init_name = DRV_NAME;
41
42 /*
43 * Use the polling mode to send/receive the data, because
44 * no IRQ connection from ISP I2C
45 */
46 isp_i2c_dev->flags |= ACCESS_POLLING;
47 platform_set_drvdata(pdev, isp_i2c_dev);
48
49 isp_i2c_dev->base = devm_platform_ioremap_resource(pdev, 0);
50 if (IS_ERR(isp_i2c_dev->base))
51 return dev_err_probe(&pdev->dev, PTR_ERR(isp_i2c_dev->base),
52 "failed to get IOMEM resource\n");
53
54 isp_i2c_dev->get_clk_rate_khz = amd_isp_dw_i2c_get_clk_rate;
55 ret = i2c_dw_fw_parse_and_configure(isp_i2c_dev);
56 if (ret)
57 return dev_err_probe(&pdev->dev, ret,
58 "failed to parse i2c dw fwnode and configure\n");
59
60 i2c_dw_configure(isp_i2c_dev);
61
62 adap = &isp_i2c_dev->adapter;
63 adap->owner = THIS_MODULE;
64 scnprintf(adap->name, sizeof(adap->name), AMDISP_I2C_ADAP_NAME);
65 ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
66 adap->dev.of_node = pdev->dev.of_node;
67 /* use dynamically allocated adapter id */
68 adap->nr = -1;
69
70 if (isp_i2c_dev->flags & ACCESS_NO_IRQ_SUSPEND)
71 dev_pm_set_driver_flags(&pdev->dev,
72 DPM_FLAG_SMART_PREPARE);
73 else
74 dev_pm_set_driver_flags(&pdev->dev,
75 DPM_FLAG_SMART_PREPARE |
76 DPM_FLAG_SMART_SUSPEND);
77
78 device_enable_async_suspend(&pdev->dev);
79
80 dev_pm_genpd_resume(&pdev->dev);
81 ret = i2c_dw_probe(isp_i2c_dev);
82 if (ret) {
83 dev_err_probe(&pdev->dev, ret, "i2c_dw_probe failed\n");
84 goto error_release_rpm;
85 }
86 dev_pm_genpd_suspend(&pdev->dev);
87 pm_runtime_set_suspended(&pdev->dev);
88 pm_runtime_enable(&pdev->dev);
89
90 return 0;
91
92 error_release_rpm:
93 amd_isp_dw_i2c_plat_pm_cleanup(isp_i2c_dev);
94 return ret;
95 }
96
amd_isp_dw_i2c_plat_remove(struct platform_device * pdev)97 static void amd_isp_dw_i2c_plat_remove(struct platform_device *pdev)
98 {
99 struct dw_i2c_dev *isp_i2c_dev = platform_get_drvdata(pdev);
100
101 pm_runtime_get_sync(&pdev->dev);
102
103 i2c_del_adapter(&isp_i2c_dev->adapter);
104
105 i2c_dw_disable(isp_i2c_dev);
106
107 pm_runtime_put_sync(&pdev->dev);
108 amd_isp_dw_i2c_plat_pm_cleanup(isp_i2c_dev);
109 }
110
amd_isp_dw_i2c_plat_prepare(struct device * dev)111 static int amd_isp_dw_i2c_plat_prepare(struct device *dev)
112 {
113 /*
114 * If the ACPI companion device object is present for this device, it
115 * may be accessed during suspend and resume of other devices via I2C
116 * operation regions, so tell the PM core and middle layers to avoid
117 * skipping system suspend/resume callbacks for it in that case.
118 */
119 return !has_acpi_companion(dev);
120 }
121
amd_isp_dw_i2c_plat_runtime_suspend(struct device * dev)122 static int amd_isp_dw_i2c_plat_runtime_suspend(struct device *dev)
123 {
124 struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
125
126 i2c_dw_disable(i_dev);
127 i2c_dw_prepare_clk(i_dev, false);
128
129 return 0;
130 }
131
amd_isp_dw_i2c_plat_suspend(struct device * dev)132 static int amd_isp_dw_i2c_plat_suspend(struct device *dev)
133 {
134 struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
135 int ret;
136
137 if (!i_dev)
138 return -ENODEV;
139
140 ret = amd_isp_dw_i2c_plat_runtime_suspend(dev);
141 if (!ret)
142 i2c_mark_adapter_suspended(&i_dev->adapter);
143
144 return ret;
145 }
146
amd_isp_dw_i2c_plat_runtime_resume(struct device * dev)147 static int amd_isp_dw_i2c_plat_runtime_resume(struct device *dev)
148 {
149 struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
150
151 if (!i_dev)
152 return -ENODEV;
153
154 i2c_dw_prepare_clk(i_dev, true);
155 i2c_dw_init(i_dev);
156
157 return 0;
158 }
159
amd_isp_dw_i2c_plat_resume(struct device * dev)160 static int amd_isp_dw_i2c_plat_resume(struct device *dev)
161 {
162 struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
163
164 amd_isp_dw_i2c_plat_runtime_resume(dev);
165 i2c_mark_adapter_resumed(&i_dev->adapter);
166
167 return 0;
168 }
169
170 static const struct dev_pm_ops amd_isp_dw_i2c_dev_pm_ops = {
171 .prepare = pm_sleep_ptr(amd_isp_dw_i2c_plat_prepare),
172 LATE_SYSTEM_SLEEP_PM_OPS(amd_isp_dw_i2c_plat_suspend, amd_isp_dw_i2c_plat_resume)
173 RUNTIME_PM_OPS(amd_isp_dw_i2c_plat_runtime_suspend, amd_isp_dw_i2c_plat_runtime_resume, NULL)
174 };
175
176 /* Work with hotplug and coldplug */
177 MODULE_ALIAS("platform:amd_isp_i2c_designware");
178
179 static struct platform_driver amd_isp_dw_i2c_driver = {
180 .probe = amd_isp_dw_i2c_plat_probe,
181 .remove = amd_isp_dw_i2c_plat_remove,
182 .driver = {
183 .name = DRV_NAME,
184 .pm = pm_ptr(&amd_isp_dw_i2c_dev_pm_ops),
185 },
186 };
187 module_platform_driver(amd_isp_dw_i2c_driver);
188
189 MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter in AMD ISP");
190 MODULE_IMPORT_NS("I2C_DW");
191 MODULE_IMPORT_NS("I2C_DW_COMMON");
192 MODULE_AUTHOR("Venkata Narendra Kumar Gutta <vengutta@amd.com>");
193 MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>");
194 MODULE_AUTHOR("Bin Du <bin.du@amd.com>");
195 MODULE_LICENSE("GPL");
196