diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-11-10 07:57:34 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-11-10 07:57:34 -0500 |
commit | d5335b3dfc614fbb4ce2b352177f38521ec3ecdd (patch) | |
tree | 4bc6a8a5b291dd2bd81ef04a0be8e71e9efc0465 | |
parent | aa4330e15c26c5ef8dd184f515c0655db8c6df3a (diff) | |
parent | 247c554a14aa16ca08f4ed4d9eb39a2389f69d1d (diff) |
Merge branch 'i2c/for-current' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
"I2C has one bugfix (qcom-geni driver), one arch enablement (i2c-omap
driver, no code change), and a new driver (nvidia-gpu) this time"
* 'i2c/for-current' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux:
usb: typec: ucsi: add support for Cypress CCGx
i2c: nvidia-gpu: make pm_ops static
i2c: add i2c bus driver for NVIDIA GPU
i2c: qcom-geni: Fix runtime PM mismatch with child devices
MAINTAINERS: Add entry for i2c-omap driver
i2c: omap: Enable for ARCH_K3
dt-bindings: i2c: omap: Add new compatible for AM654 SoCs
-rw-r--r-- | Documentation/devicetree/bindings/i2c/i2c-omap.txt | 8 | ||||
-rw-r--r-- | Documentation/i2c/busses/i2c-nvidia-gpu | 18 | ||||
-rw-r--r-- | MAINTAINERS | 15 | ||||
-rw-r--r-- | drivers/i2c/busses/Kconfig | 11 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-nvidia-gpu.c | 368 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-qcom-geni.c | 15 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/Kconfig | 10 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/ucsi_ccg.c | 307 |
10 files changed, 745 insertions, 10 deletions
diff --git a/Documentation/devicetree/bindings/i2c/i2c-omap.txt b/Documentation/devicetree/bindings/i2c/i2c-omap.txt index 7e49839d4124..4b90ba9f31b7 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-omap.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-omap.txt | |||
@@ -1,8 +1,12 @@ | |||
1 | I2C for OMAP platforms | 1 | I2C for OMAP platforms |
2 | 2 | ||
3 | Required properties : | 3 | Required properties : |
4 | - compatible : Must be "ti,omap2420-i2c", "ti,omap2430-i2c", "ti,omap3-i2c" | 4 | - compatible : Must be |
5 | or "ti,omap4-i2c" | 5 | "ti,omap2420-i2c" for OMAP2420 SoCs |
6 | "ti,omap2430-i2c" for OMAP2430 SoCs | ||
7 | "ti,omap3-i2c" for OMAP3 SoCs | ||
8 | "ti,omap4-i2c" for OMAP4+ SoCs | ||
9 | "ti,am654-i2c", "ti,omap4-i2c" for AM654 SoCs | ||
6 | - ti,hwmods : Must be "i2c<n>", n being the instance number (1-based) | 10 | - ti,hwmods : Must be "i2c<n>", n being the instance number (1-based) |
7 | - #address-cells = <1>; | 11 | - #address-cells = <1>; |
8 | - #size-cells = <0>; | 12 | - #size-cells = <0>; |
diff --git a/Documentation/i2c/busses/i2c-nvidia-gpu b/Documentation/i2c/busses/i2c-nvidia-gpu new file mode 100644 index 000000000000..31884d2b2eb5 --- /dev/null +++ b/Documentation/i2c/busses/i2c-nvidia-gpu | |||
@@ -0,0 +1,18 @@ | |||
1 | Kernel driver i2c-nvidia-gpu | ||
2 | |||
3 | Datasheet: not publicly available. | ||
4 | |||
5 | Authors: | ||
6 | Ajay Gupta <ajayg@nvidia.com> | ||
7 | |||
8 | Description | ||
9 | ----------- | ||
10 | |||
11 | i2c-nvidia-gpu is a driver for I2C controller included in NVIDIA Turing | ||
12 | and later GPUs and it is used to communicate with Type-C controller on GPUs. | ||
13 | |||
14 | If your 'lspci -v' listing shows something like the following, | ||
15 | |||
16 | 01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) | ||
17 | |||
18 | then this driver should support the I2C controller of your GPU. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 95e56fd128a5..0abecc528dac 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -6861,6 +6861,13 @@ L: linux-acpi@vger.kernel.org | |||
6861 | S: Maintained | 6861 | S: Maintained |
6862 | F: drivers/i2c/i2c-core-acpi.c | 6862 | F: drivers/i2c/i2c-core-acpi.c |
6863 | 6863 | ||
6864 | I2C CONTROLLER DRIVER FOR NVIDIA GPU | ||
6865 | M: Ajay Gupta <ajayg@nvidia.com> | ||
6866 | L: linux-i2c@vger.kernel.org | ||
6867 | S: Maintained | ||
6868 | F: Documentation/i2c/busses/i2c-nvidia-gpu | ||
6869 | F: drivers/i2c/busses/i2c-nvidia-gpu.c | ||
6870 | |||
6864 | I2C MUXES | 6871 | I2C MUXES |
6865 | M: Peter Rosin <peda@axentia.se> | 6872 | M: Peter Rosin <peda@axentia.se> |
6866 | L: linux-i2c@vger.kernel.org | 6873 | L: linux-i2c@vger.kernel.org |
@@ -10784,6 +10791,14 @@ L: linux-omap@vger.kernel.org | |||
10784 | S: Maintained | 10791 | S: Maintained |
10785 | F: arch/arm/mach-omap2/omap_hwmod.* | 10792 | F: arch/arm/mach-omap2/omap_hwmod.* |
10786 | 10793 | ||
10794 | OMAP I2C DRIVER | ||
10795 | M: Vignesh R <vigneshr@ti.com> | ||
10796 | L: linux-omap@vger.kernel.org | ||
10797 | L: linux-i2c@vger.kernel.org | ||
10798 | S: Maintained | ||
10799 | F: Documentation/devicetree/bindings/i2c/i2c-omap.txt | ||
10800 | F: drivers/i2c/busses/i2c-omap.c | ||
10801 | |||
10787 | OMAP IMAGING SUBSYSTEM (OMAP3 ISP and OMAP4 ISS) | 10802 | OMAP IMAGING SUBSYSTEM (OMAP3 ISP and OMAP4 ISS) |
10788 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 10803 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
10789 | L: linux-media@vger.kernel.org | 10804 | L: linux-media@vger.kernel.org |
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 56ccb1ea7da5..f2c681971201 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig | |||
@@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 | |||
224 | This driver can also be built as a module. If so, the module | 224 | This driver can also be built as a module. If so, the module |
225 | will be called i2c-nforce2-s4985. | 225 | will be called i2c-nforce2-s4985. |
226 | 226 | ||
227 | config I2C_NVIDIA_GPU | ||
228 | tristate "NVIDIA GPU I2C controller" | ||
229 | depends on PCI | ||
230 | help | ||
231 | If you say yes to this option, support will be included for the | ||
232 | NVIDIA GPU I2C controller which is used to communicate with the GPU's | ||
233 | Type-C controller. This driver can also be built as a module called | ||
234 | i2c-nvidia-gpu. | ||
235 | |||
227 | config I2C_SIS5595 | 236 | config I2C_SIS5595 |
228 | tristate "SiS 5595" | 237 | tristate "SiS 5595" |
229 | depends on PCI | 238 | depends on PCI |
@@ -752,7 +761,7 @@ config I2C_OCORES | |||
752 | 761 | ||
753 | config I2C_OMAP | 762 | config I2C_OMAP |
754 | tristate "OMAP I2C adapter" | 763 | tristate "OMAP I2C adapter" |
755 | depends on ARCH_OMAP | 764 | depends on ARCH_OMAP || ARCH_K3 |
756 | default y if MACH_OMAP_H3 || MACH_OMAP_OSK | 765 | default y if MACH_OMAP_H3 || MACH_OMAP_OSK |
757 | help | 766 | help |
758 | If you say yes to this option, support will be included for the | 767 | If you say yes to this option, support will be included for the |
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af82b1c..5f0cb6915969 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile | |||
@@ -19,6 +19,7 @@ obj-$(CONFIG_I2C_ISCH) += i2c-isch.o | |||
19 | obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o | 19 | obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o |
20 | obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o | 20 | obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o |
21 | obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o | 21 | obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o |
22 | obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o | ||
22 | obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o | 23 | obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o |
23 | obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o | 24 | obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o |
24 | obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o | 25 | obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o |
diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c new file mode 100644 index 000000000000..8822357bca0c --- /dev/null +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c | |||
@@ -0,0 +1,368 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Nvidia GPU I2C controller Driver | ||
4 | * | ||
5 | * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. | ||
6 | * Author: Ajay Gupta <ajayg@nvidia.com> | ||
7 | */ | ||
8 | #include <linux/delay.h> | ||
9 | #include <linux/i2c.h> | ||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/pci.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/pm.h> | ||
15 | #include <linux/pm_runtime.h> | ||
16 | |||
17 | #include <asm/unaligned.h> | ||
18 | |||
19 | /* I2C definitions */ | ||
20 | #define I2C_MST_CNTL 0x00 | ||
21 | #define I2C_MST_CNTL_GEN_START BIT(0) | ||
22 | #define I2C_MST_CNTL_GEN_STOP BIT(1) | ||
23 | #define I2C_MST_CNTL_CMD_READ (1 << 2) | ||
24 | #define I2C_MST_CNTL_CMD_WRITE (2 << 2) | ||
25 | #define I2C_MST_CNTL_BURST_SIZE_SHIFT 6 | ||
26 | #define I2C_MST_CNTL_GEN_NACK BIT(28) | ||
27 | #define I2C_MST_CNTL_STATUS GENMASK(30, 29) | ||
28 | #define I2C_MST_CNTL_STATUS_OKAY (0 << 29) | ||
29 | #define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29) | ||
30 | #define I2C_MST_CNTL_STATUS_TIMEOUT (2 << 29) | ||
31 | #define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29) | ||
32 | #define I2C_MST_CNTL_CYCLE_TRIGGER BIT(31) | ||
33 | |||
34 | #define I2C_MST_ADDR 0x04 | ||
35 | |||
36 | #define I2C_MST_I2C0_TIMING 0x08 | ||
37 | #define I2C_MST_I2C0_TIMING_SCL_PERIOD_100KHZ 0x10e | ||
38 | #define I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT 16 | ||
39 | #define I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT_MAX 255 | ||
40 | #define I2C_MST_I2C0_TIMING_TIMEOUT_CHECK BIT(24) | ||
41 | |||
42 | #define I2C_MST_DATA 0x0c | ||
43 | |||
44 | #define I2C_MST_HYBRID_PADCTL 0x20 | ||
45 | #define I2C_MST_HYBRID_PADCTL_MODE_I2C BIT(0) | ||
46 | #define I2C_MST_HYBRID_PADCTL_I2C_SCL_INPUT_RCV BIT(14) | ||
47 | #define I2C_MST_HYBRID_PADCTL_I2C_SDA_INPUT_RCV BIT(15) | ||
48 | |||
49 | struct gpu_i2c_dev { | ||
50 | struct device *dev; | ||
51 | void __iomem *regs; | ||
52 | struct i2c_adapter adapter; | ||
53 | struct i2c_board_info *gpu_ccgx_ucsi; | ||
54 | }; | ||
55 | |||
56 | static void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd) | ||
57 | { | ||
58 | u32 val; | ||
59 | |||
60 | /* enable I2C */ | ||
61 | val = readl(i2cd->regs + I2C_MST_HYBRID_PADCTL); | ||
62 | val |= I2C_MST_HYBRID_PADCTL_MODE_I2C | | ||
63 | I2C_MST_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | | ||
64 | I2C_MST_HYBRID_PADCTL_I2C_SDA_INPUT_RCV; | ||
65 | writel(val, i2cd->regs + I2C_MST_HYBRID_PADCTL); | ||
66 | |||
67 | /* enable 100KHZ mode */ | ||
68 | val = I2C_MST_I2C0_TIMING_SCL_PERIOD_100KHZ; | ||
69 | val |= (I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT_MAX | ||
70 | << I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT); | ||
71 | val |= I2C_MST_I2C0_TIMING_TIMEOUT_CHECK; | ||
72 | writel(val, i2cd->regs + I2C_MST_I2C0_TIMING); | ||
73 | } | ||
74 | |||
75 | static int gpu_i2c_check_status(struct gpu_i2c_dev *i2cd) | ||
76 | { | ||
77 | unsigned long target = jiffies + msecs_to_jiffies(1000); | ||
78 | u32 val; | ||
79 | |||
80 | do { | ||
81 | val = readl(i2cd->regs + I2C_MST_CNTL); | ||
82 | if (!(val & I2C_MST_CNTL_CYCLE_TRIGGER)) | ||
83 | break; | ||
84 | if ((val & I2C_MST_CNTL_STATUS) != | ||
85 | I2C_MST_CNTL_STATUS_BUS_BUSY) | ||
86 | break; | ||
87 | usleep_range(500, 600); | ||
88 | } while (time_is_after_jiffies(target)); | ||
89 | |||
90 | if (time_is_before_jiffies(target)) { | ||
91 | dev_err(i2cd->dev, "i2c timeout error %x\n", val); | ||
92 | return -ETIME; | ||
93 | } | ||
94 | |||
95 | val = readl(i2cd->regs + I2C_MST_CNTL); | ||
96 | switch (val & I2C_MST_CNTL_STATUS) { | ||
97 | case I2C_MST_CNTL_STATUS_OKAY: | ||
98 | return 0; | ||
99 | case I2C_MST_CNTL_STATUS_NO_ACK: | ||
100 | return -EIO; | ||
101 | case I2C_MST_CNTL_STATUS_TIMEOUT: | ||
102 | return -ETIME; | ||
103 | default: | ||
104 | return 0; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | static int gpu_i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16 len) | ||
109 | { | ||
110 | int status; | ||
111 | u32 val; | ||
112 | |||
113 | val = I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_CMD_READ | | ||
114 | (len << I2C_MST_CNTL_BURST_SIZE_SHIFT) | | ||
115 | I2C_MST_CNTL_CYCLE_TRIGGER | I2C_MST_CNTL_GEN_NACK; | ||
116 | writel(val, i2cd->regs + I2C_MST_CNTL); | ||
117 | |||
118 | status = gpu_i2c_check_status(i2cd); | ||
119 | if (status < 0) | ||
120 | return status; | ||
121 | |||
122 | val = readl(i2cd->regs + I2C_MST_DATA); | ||
123 | switch (len) { | ||
124 | case 1: | ||
125 | data[0] = val; | ||
126 | break; | ||
127 | case 2: | ||
128 | put_unaligned_be16(val, data); | ||
129 | break; | ||
130 | case 3: | ||
131 | put_unaligned_be16(val >> 8, data); | ||
132 | data[2] = val; | ||
133 | break; | ||
134 | case 4: | ||
135 | put_unaligned_be32(val, data); | ||
136 | break; | ||
137 | default: | ||
138 | break; | ||
139 | } | ||
140 | return status; | ||
141 | } | ||
142 | |||
143 | static int gpu_i2c_start(struct gpu_i2c_dev *i2cd) | ||
144 | { | ||
145 | writel(I2C_MST_CNTL_GEN_START, i2cd->regs + I2C_MST_CNTL); | ||
146 | return gpu_i2c_check_status(i2cd); | ||
147 | } | ||
148 | |||
149 | static int gpu_i2c_stop(struct gpu_i2c_dev *i2cd) | ||
150 | { | ||
151 | writel(I2C_MST_CNTL_GEN_STOP, i2cd->regs + I2C_MST_CNTL); | ||
152 | return gpu_i2c_check_status(i2cd); | ||
153 | } | ||
154 | |||
155 | static int gpu_i2c_write(struct gpu_i2c_dev *i2cd, u8 data) | ||
156 | { | ||
157 | u32 val; | ||
158 | |||
159 | writel(data, i2cd->regs + I2C_MST_DATA); | ||
160 | |||
161 | val = I2C_MST_CNTL_CMD_WRITE | (1 << I2C_MST_CNTL_BURST_SIZE_SHIFT); | ||
162 | writel(val, i2cd->regs + I2C_MST_CNTL); | ||
163 | |||
164 | return gpu_i2c_check_status(i2cd); | ||
165 | } | ||
166 | |||
167 | static int gpu_i2c_master_xfer(struct i2c_adapter *adap, | ||
168 | struct i2c_msg *msgs, int num) | ||
169 | { | ||
170 | struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap); | ||
171 | int status, status2; | ||
172 | int i, j; | ||
173 | |||
174 | /* | ||
175 | * The controller supports maximum 4 byte read due to known | ||
176 | * limitation of sending STOP after every read. | ||
177 | */ | ||
178 | for (i = 0; i < num; i++) { | ||
179 | if (msgs[i].flags & I2C_M_RD) { | ||
180 | /* program client address before starting read */ | ||
181 | writel(msgs[i].addr, i2cd->regs + I2C_MST_ADDR); | ||
182 | /* gpu_i2c_read has implicit start */ | ||
183 | status = gpu_i2c_read(i2cd, msgs[i].buf, msgs[i].len); | ||
184 | if (status < 0) | ||
185 | goto stop; | ||
186 | } else { | ||
187 | u8 addr = i2c_8bit_addr_from_msg(msgs + i); | ||
188 | |||
189 | status = gpu_i2c_start(i2cd); | ||
190 | if (status < 0) { | ||
191 | if (i == 0) | ||
192 | return status; | ||
193 | goto stop; | ||
194 | } | ||
195 | |||
196 | status = gpu_i2c_write(i2cd, addr); | ||
197 | if (status < 0) | ||
198 | goto stop; | ||
199 | |||
200 | for (j = 0; j < msgs[i].len; j++) { | ||
201 | status = gpu_i2c_write(i2cd, msgs[i].buf[j]); | ||
202 | if (status < 0) | ||
203 | goto stop; | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | status = gpu_i2c_stop(i2cd); | ||
208 | if (status < 0) | ||
209 | return status; | ||
210 | |||
211 | return i; | ||
212 | stop: | ||
213 | status2 = gpu_i2c_stop(i2cd); | ||
214 | if (status2 < 0) | ||
215 | dev_err(i2cd->dev, "i2c stop failed %d\n", status2); | ||
216 | return status; | ||
217 | } | ||
218 | |||
219 | static const struct i2c_adapter_quirks gpu_i2c_quirks = { | ||
220 | .max_read_len = 4, | ||
221 | .flags = I2C_AQ_COMB_WRITE_THEN_READ, | ||
222 | }; | ||
223 | |||
224 | static u32 gpu_i2c_functionality(struct i2c_adapter *adap) | ||
225 | { | ||
226 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
227 | } | ||
228 | |||
229 | static const struct i2c_algorithm gpu_i2c_algorithm = { | ||
230 | .master_xfer = gpu_i2c_master_xfer, | ||
231 | .functionality = gpu_i2c_functionality, | ||
232 | }; | ||
233 | |||
234 | /* | ||
235 | * This driver is for Nvidia GPU cards with USB Type-C interface. | ||
236 | * We want to identify the cards using vendor ID and class code only | ||
237 | * to avoid dependency of adding product id for any new card which | ||
238 | * requires this driver. | ||
239 | * Currently there is no class code defined for UCSI device over PCI | ||
240 | * so using UNKNOWN class for now and it will be updated when UCSI | ||
241 | * over PCI gets a class code. | ||
242 | * There is no other NVIDIA cards with UNKNOWN class code. Even if the | ||
243 | * driver gets loaded for an undesired card then eventually i2c_read() | ||
244 | * (initiated from UCSI i2c_client) will timeout or UCSI commands will | ||
245 | * timeout. | ||
246 | */ | ||
247 | #define PCI_CLASS_SERIAL_UNKNOWN 0x0c80 | ||
248 | static const struct pci_device_id gpu_i2c_ids[] = { | ||
249 | { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, | ||
250 | PCI_CLASS_SERIAL_UNKNOWN << 8, 0xffffff00}, | ||
251 | { } | ||
252 | }; | ||
253 | MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); | ||
254 | |||
255 | static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) | ||
256 | { | ||
257 | struct i2c_client *ccgx_client; | ||
258 | |||
259 | i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev, | ||
260 | sizeof(*i2cd->gpu_ccgx_ucsi), | ||
261 | GFP_KERNEL); | ||
262 | if (!i2cd->gpu_ccgx_ucsi) | ||
263 | return -ENOMEM; | ||
264 | |||
265 | strlcpy(i2cd->gpu_ccgx_ucsi->type, "ccgx-ucsi", | ||
266 | sizeof(i2cd->gpu_ccgx_ucsi->type)); | ||
267 | i2cd->gpu_ccgx_ucsi->addr = 0x8; | ||
268 | i2cd->gpu_ccgx_ucsi->irq = irq; | ||
269 | ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); | ||
270 | if (!ccgx_client) | ||
271 | return -ENODEV; | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) | ||
277 | { | ||
278 | struct gpu_i2c_dev *i2cd; | ||
279 | int status; | ||
280 | |||
281 | i2cd = devm_kzalloc(&pdev->dev, sizeof(*i2cd), GFP_KERNEL); | ||
282 | if (!i2cd) | ||
283 | return -ENOMEM; | ||
284 | |||
285 | i2cd->dev = &pdev->dev; | ||
286 | dev_set_drvdata(&pdev->dev, i2cd); | ||
287 | |||
288 | status = pcim_enable_device(pdev); | ||
289 | if (status < 0) { | ||
290 | dev_err(&pdev->dev, "pcim_enable_device failed %d\n", status); | ||
291 | return status; | ||
292 | } | ||
293 | |||
294 | pci_set_master(pdev); | ||
295 | |||
296 | i2cd->regs = pcim_iomap(pdev, 0, 0); | ||
297 | if (!i2cd->regs) { | ||
298 | dev_err(&pdev->dev, "pcim_iomap failed\n"); | ||
299 | return -ENOMEM; | ||
300 | } | ||
301 | |||
302 | status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); | ||
303 | if (status < 0) { | ||
304 | dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", status); | ||
305 | return status; | ||
306 | } | ||
307 | |||
308 | gpu_enable_i2c_bus(i2cd); | ||
309 | |||
310 | i2c_set_adapdata(&i2cd->adapter, i2cd); | ||
311 | i2cd->adapter.owner = THIS_MODULE; | ||
312 | strlcpy(i2cd->adapter.name, "NVIDIA GPU I2C adapter", | ||
313 | sizeof(i2cd->adapter.name)); | ||
314 | i2cd->adapter.algo = &gpu_i2c_algorithm; | ||
315 | i2cd->adapter.quirks = &gpu_i2c_quirks; | ||
316 | i2cd->adapter.dev.parent = &pdev->dev; | ||
317 | status = i2c_add_adapter(&i2cd->adapter); | ||
318 | if (status < 0) | ||
319 | goto free_irq_vectors; | ||
320 | |||
321 | status = gpu_populate_client(i2cd, pdev->irq); | ||
322 | if (status < 0) { | ||
323 | dev_err(&pdev->dev, "gpu_populate_client failed %d\n", status); | ||
324 | goto del_adapter; | ||
325 | } | ||
326 | |||
327 | return 0; | ||
328 | |||
329 | del_adapter: | ||
330 | i2c_del_adapter(&i2cd->adapter); | ||
331 | free_irq_vectors: | ||
332 | pci_free_irq_vectors(pdev); | ||
333 | return status; | ||
334 | } | ||
335 | |||
336 | static void gpu_i2c_remove(struct pci_dev *pdev) | ||
337 | { | ||
338 | struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); | ||
339 | |||
340 | i2c_del_adapter(&i2cd->adapter); | ||
341 | pci_free_irq_vectors(pdev); | ||
342 | } | ||
343 | |||
344 | static int gpu_i2c_resume(struct device *dev) | ||
345 | { | ||
346 | struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); | ||
347 | |||
348 | gpu_enable_i2c_bus(i2cd); | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, NULL, gpu_i2c_resume, NULL); | ||
353 | |||
354 | static struct pci_driver gpu_i2c_driver = { | ||
355 | .name = "nvidia-gpu", | ||
356 | .id_table = gpu_i2c_ids, | ||
357 | .probe = gpu_i2c_probe, | ||
358 | .remove = gpu_i2c_remove, | ||
359 | .driver = { | ||
360 | .pm = &gpu_i2c_driver_pm, | ||
361 | }, | ||
362 | }; | ||
363 | |||
364 | module_pci_driver(gpu_i2c_driver); | ||
365 | |||
366 | MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>"); | ||
367 | MODULE_DESCRIPTION("Nvidia GPU I2C controller Driver"); | ||
368 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 527f55c8c4c7..db075bc0d952 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c | |||
@@ -571,18 +571,19 @@ static int geni_i2c_probe(struct platform_device *pdev) | |||
571 | 571 | ||
572 | dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth); | 572 | dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth); |
573 | 573 | ||
574 | ret = i2c_add_adapter(&gi2c->adap); | ||
575 | if (ret) { | ||
576 | dev_err(&pdev->dev, "Error adding i2c adapter %d\n", ret); | ||
577 | return ret; | ||
578 | } | ||
579 | |||
580 | gi2c->suspended = 1; | 574 | gi2c->suspended = 1; |
581 | pm_runtime_set_suspended(gi2c->se.dev); | 575 | pm_runtime_set_suspended(gi2c->se.dev); |
582 | pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY); | 576 | pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY); |
583 | pm_runtime_use_autosuspend(gi2c->se.dev); | 577 | pm_runtime_use_autosuspend(gi2c->se.dev); |
584 | pm_runtime_enable(gi2c->se.dev); | 578 | pm_runtime_enable(gi2c->se.dev); |
585 | 579 | ||
580 | ret = i2c_add_adapter(&gi2c->adap); | ||
581 | if (ret) { | ||
582 | dev_err(&pdev->dev, "Error adding i2c adapter %d\n", ret); | ||
583 | pm_runtime_disable(gi2c->se.dev); | ||
584 | return ret; | ||
585 | } | ||
586 | |||
586 | return 0; | 587 | return 0; |
587 | } | 588 | } |
588 | 589 | ||
@@ -590,8 +591,8 @@ static int geni_i2c_remove(struct platform_device *pdev) | |||
590 | { | 591 | { |
591 | struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev); | 592 | struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev); |
592 | 593 | ||
593 | pm_runtime_disable(gi2c->se.dev); | ||
594 | i2c_del_adapter(&gi2c->adap); | 594 | i2c_del_adapter(&gi2c->adap); |
595 | pm_runtime_disable(gi2c->se.dev); | ||
595 | return 0; | 596 | return 0; |
596 | } | 597 | } |
597 | 598 | ||
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index e36d6c73c4a4..78118883f96c 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig | |||
@@ -23,6 +23,16 @@ config TYPEC_UCSI | |||
23 | 23 | ||
24 | if TYPEC_UCSI | 24 | if TYPEC_UCSI |
25 | 25 | ||
26 | config UCSI_CCG | ||
27 | tristate "UCSI Interface Driver for Cypress CCGx" | ||
28 | depends on I2C | ||
29 | help | ||
30 | This driver enables UCSI support on platforms that expose a | ||
31 | Cypress CCGx Type-C controller over I2C interface. | ||
32 | |||
33 | To compile the driver as a module, choose M here: the module will be | ||
34 | called ucsi_ccg. | ||
35 | |||
26 | config UCSI_ACPI | 36 | config UCSI_ACPI |
27 | tristate "UCSI ACPI Interface Driver" | 37 | tristate "UCSI ACPI Interface Driver" |
28 | depends on ACPI | 38 | depends on ACPI |
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 7afbea512207..2f4900b26210 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile | |||
@@ -8,3 +8,5 @@ typec_ucsi-y := ucsi.o | |||
8 | typec_ucsi-$(CONFIG_TRACING) += trace.o | 8 | typec_ucsi-$(CONFIG_TRACING) += trace.o |
9 | 9 | ||
10 | obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o | 10 | obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o |
11 | |||
12 | obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o | ||
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c new file mode 100644 index 000000000000..de8a43bdff68 --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c | |||
@@ -0,0 +1,307 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * UCSI driver for Cypress CCGx Type-C controller | ||
4 | * | ||
5 | * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. | ||
6 | * Author: Ajay Gupta <ajayg@nvidia.com> | ||
7 | * | ||
8 | * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c | ||
9 | */ | ||
10 | #include <linux/acpi.h> | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/i2c.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/pci.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | |||
17 | #include <asm/unaligned.h> | ||
18 | #include "ucsi.h" | ||
19 | |||
20 | struct ucsi_ccg { | ||
21 | struct device *dev; | ||
22 | struct ucsi *ucsi; | ||
23 | struct ucsi_ppm ppm; | ||
24 | struct i2c_client *client; | ||
25 | }; | ||
26 | |||
27 | #define CCGX_RAB_INTR_REG 0x06 | ||
28 | #define CCGX_RAB_UCSI_CONTROL 0x39 | ||
29 | #define CCGX_RAB_UCSI_CONTROL_START BIT(0) | ||
30 | #define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) | ||
31 | #define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) | ||
32 | |||
33 | static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) | ||
34 | { | ||
35 | struct i2c_client *client = uc->client; | ||
36 | const struct i2c_adapter_quirks *quirks = client->adapter->quirks; | ||
37 | unsigned char buf[2]; | ||
38 | struct i2c_msg msgs[] = { | ||
39 | { | ||
40 | .addr = client->addr, | ||
41 | .flags = 0x0, | ||
42 | .len = sizeof(buf), | ||
43 | .buf = buf, | ||
44 | }, | ||
45 | { | ||
46 | .addr = client->addr, | ||
47 | .flags = I2C_M_RD, | ||
48 | .buf = data, | ||
49 | }, | ||
50 | }; | ||
51 | u32 rlen, rem_len = len, max_read_len = len; | ||
52 | int status; | ||
53 | |||
54 | /* check any max_read_len limitation on i2c adapter */ | ||
55 | if (quirks && quirks->max_read_len) | ||
56 | max_read_len = quirks->max_read_len; | ||
57 | |||
58 | while (rem_len > 0) { | ||
59 | msgs[1].buf = &data[len - rem_len]; | ||
60 | rlen = min_t(u16, rem_len, max_read_len); | ||
61 | msgs[1].len = rlen; | ||
62 | put_unaligned_le16(rab, buf); | ||
63 | status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | ||
64 | if (status < 0) { | ||
65 | dev_err(uc->dev, "i2c_transfer failed %d\n", status); | ||
66 | return status; | ||
67 | } | ||
68 | rab += rlen; | ||
69 | rem_len -= rlen; | ||
70 | } | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) | ||
76 | { | ||
77 | struct i2c_client *client = uc->client; | ||
78 | unsigned char *buf; | ||
79 | struct i2c_msg msgs[] = { | ||
80 | { | ||
81 | .addr = client->addr, | ||
82 | .flags = 0x0, | ||
83 | } | ||
84 | }; | ||
85 | int status; | ||
86 | |||
87 | buf = kzalloc(len + sizeof(rab), GFP_KERNEL); | ||
88 | if (!buf) | ||
89 | return -ENOMEM; | ||
90 | |||
91 | put_unaligned_le16(rab, buf); | ||
92 | memcpy(buf + sizeof(rab), data, len); | ||
93 | |||
94 | msgs[0].len = len + sizeof(rab); | ||
95 | msgs[0].buf = buf; | ||
96 | |||
97 | status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | ||
98 | if (status < 0) { | ||
99 | dev_err(uc->dev, "i2c_transfer failed %d\n", status); | ||
100 | kfree(buf); | ||
101 | return status; | ||
102 | } | ||
103 | |||
104 | kfree(buf); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int ucsi_ccg_init(struct ucsi_ccg *uc) | ||
109 | { | ||
110 | unsigned int count = 10; | ||
111 | u8 data; | ||
112 | int status; | ||
113 | |||
114 | data = CCGX_RAB_UCSI_CONTROL_STOP; | ||
115 | status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); | ||
116 | if (status < 0) | ||
117 | return status; | ||
118 | |||
119 | data = CCGX_RAB_UCSI_CONTROL_START; | ||
120 | status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); | ||
121 | if (status < 0) | ||
122 | return status; | ||
123 | |||
124 | /* | ||
125 | * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control | ||
126 | * register write will push response which must be cleared. | ||
127 | */ | ||
128 | do { | ||
129 | status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); | ||
130 | if (status < 0) | ||
131 | return status; | ||
132 | |||
133 | if (!data) | ||
134 | return 0; | ||
135 | |||
136 | status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); | ||
137 | if (status < 0) | ||
138 | return status; | ||
139 | |||
140 | usleep_range(10000, 11000); | ||
141 | } while (--count); | ||
142 | |||
143 | return -ETIMEDOUT; | ||
144 | } | ||
145 | |||
146 | static int ucsi_ccg_send_data(struct ucsi_ccg *uc) | ||
147 | { | ||
148 | u8 *ppm = (u8 *)uc->ppm.data; | ||
149 | int status; | ||
150 | u16 rab; | ||
151 | |||
152 | rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out)); | ||
153 | status = ccg_write(uc, rab, ppm + | ||
154 | offsetof(struct ucsi_data, message_out), | ||
155 | sizeof(uc->ppm.data->message_out)); | ||
156 | if (status < 0) | ||
157 | return status; | ||
158 | |||
159 | rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl)); | ||
160 | return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl), | ||
161 | sizeof(uc->ppm.data->ctrl)); | ||
162 | } | ||
163 | |||
164 | static int ucsi_ccg_recv_data(struct ucsi_ccg *uc) | ||
165 | { | ||
166 | u8 *ppm = (u8 *)uc->ppm.data; | ||
167 | int status; | ||
168 | u16 rab; | ||
169 | |||
170 | rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci)); | ||
171 | status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci), | ||
172 | sizeof(uc->ppm.data->cci)); | ||
173 | if (status < 0) | ||
174 | return status; | ||
175 | |||
176 | rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in)); | ||
177 | return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in), | ||
178 | sizeof(uc->ppm.data->message_in)); | ||
179 | } | ||
180 | |||
181 | static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc) | ||
182 | { | ||
183 | int status; | ||
184 | unsigned char data; | ||
185 | |||
186 | status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); | ||
187 | if (status < 0) | ||
188 | return status; | ||
189 | |||
190 | return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); | ||
191 | } | ||
192 | |||
193 | static int ucsi_ccg_sync(struct ucsi_ppm *ppm) | ||
194 | { | ||
195 | struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); | ||
196 | int status; | ||
197 | |||
198 | status = ucsi_ccg_recv_data(uc); | ||
199 | if (status < 0) | ||
200 | return status; | ||
201 | |||
202 | /* ack interrupt to allow next command to run */ | ||
203 | return ucsi_ccg_ack_interrupt(uc); | ||
204 | } | ||
205 | |||
206 | static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl) | ||
207 | { | ||
208 | struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); | ||
209 | |||
210 | ppm->data->ctrl.raw_cmd = ctrl->raw_cmd; | ||
211 | return ucsi_ccg_send_data(uc); | ||
212 | } | ||
213 | |||
214 | static irqreturn_t ccg_irq_handler(int irq, void *data) | ||
215 | { | ||
216 | struct ucsi_ccg *uc = data; | ||
217 | |||
218 | ucsi_notify(uc->ucsi); | ||
219 | |||
220 | return IRQ_HANDLED; | ||
221 | } | ||
222 | |||
223 | static int ucsi_ccg_probe(struct i2c_client *client, | ||
224 | const struct i2c_device_id *id) | ||
225 | { | ||
226 | struct device *dev = &client->dev; | ||
227 | struct ucsi_ccg *uc; | ||
228 | int status; | ||
229 | u16 rab; | ||
230 | |||
231 | uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); | ||
232 | if (!uc) | ||
233 | return -ENOMEM; | ||
234 | |||
235 | uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL); | ||
236 | if (!uc->ppm.data) | ||
237 | return -ENOMEM; | ||
238 | |||
239 | uc->ppm.cmd = ucsi_ccg_cmd; | ||
240 | uc->ppm.sync = ucsi_ccg_sync; | ||
241 | uc->dev = dev; | ||
242 | uc->client = client; | ||
243 | |||
244 | /* reset ccg device and initialize ucsi */ | ||
245 | status = ucsi_ccg_init(uc); | ||
246 | if (status < 0) { | ||
247 | dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status); | ||
248 | return status; | ||
249 | } | ||
250 | |||
251 | status = devm_request_threaded_irq(dev, client->irq, NULL, | ||
252 | ccg_irq_handler, | ||
253 | IRQF_ONESHOT | IRQF_TRIGGER_HIGH, | ||
254 | dev_name(dev), uc); | ||
255 | if (status < 0) { | ||
256 | dev_err(uc->dev, "request_threaded_irq failed - %d\n", status); | ||
257 | return status; | ||
258 | } | ||
259 | |||
260 | uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); | ||
261 | if (IS_ERR(uc->ucsi)) { | ||
262 | dev_err(uc->dev, "ucsi_register_ppm failed\n"); | ||
263 | return PTR_ERR(uc->ucsi); | ||
264 | } | ||
265 | |||
266 | rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version)); | ||
267 | status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) + | ||
268 | offsetof(struct ucsi_data, version), | ||
269 | sizeof(uc->ppm.data->version)); | ||
270 | if (status < 0) { | ||
271 | ucsi_unregister_ppm(uc->ucsi); | ||
272 | return status; | ||
273 | } | ||
274 | |||
275 | i2c_set_clientdata(client, uc); | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static int ucsi_ccg_remove(struct i2c_client *client) | ||
280 | { | ||
281 | struct ucsi_ccg *uc = i2c_get_clientdata(client); | ||
282 | |||
283 | ucsi_unregister_ppm(uc->ucsi); | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static const struct i2c_device_id ucsi_ccg_device_id[] = { | ||
289 | {"ccgx-ucsi", 0}, | ||
290 | {} | ||
291 | }; | ||
292 | MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); | ||
293 | |||
294 | static struct i2c_driver ucsi_ccg_driver = { | ||
295 | .driver = { | ||
296 | .name = "ucsi_ccg", | ||
297 | }, | ||
298 | .probe = ucsi_ccg_probe, | ||
299 | .remove = ucsi_ccg_remove, | ||
300 | .id_table = ucsi_ccg_device_id, | ||
301 | }; | ||
302 | |||
303 | module_i2c_driver(ucsi_ccg_driver); | ||
304 | |||
305 | MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>"); | ||
306 | MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller"); | ||
307 | MODULE_LICENSE("GPL v2"); | ||