RE: [PATCH v7 00/11] usb: musb: adding multi instance support
Hi, > On 03.08.2012 02:54, B, Ravi wrote: > >> On 02.08.2012 14:12, Ravi Babu wrote: > >>> This series of patches adds, > >>> a) Multi instances support in musb driver > >>> b) DT support for musb_dsps glue layer > >>> c) DT support for NOP transceiver > >>> > >>> AM33xx and TI81xx has dual musb controller and has two usb > >> PHY of same type. > >>> This patch series uses 'phandle' based API > >>> devm_usb_get_phy_by_phandle() to get the PHY of same type. This API > >>> support is being added by Kishon's patch discussed at [1] > >>> > >>> The series applies to linux-omap (master branch) > >>> + Vaibhav baseport patches on his tree at [3] > >>> + Kishon's multi phy patches on Felipe's branch 'xceiv' > >>> + Kishon's patch on phandle at [1] > >>> + AM33xx musb glue compile and bugfix patches at [4], > >> [5], [6] and [7] > >>> + Damodar's recent patch at [2] > >>> > >>> and have been tested on Beaglebone board. > > > > Have you applied the above patches before applying these patches. > > Somehow, I was missing some of Ajay's patches. I resolved that, and now the > series applied. > > However, I needed to add a phandle "usb0-phy = <&usb0_phy>" to the > usb_otg_hs DTSI block, otherwise devm_usb_get_phy_by_phandle() in > drivers/usb/musb/musb_dsps.c would fail. Is that correct? I can't seem to > find that in your patches. It's getting done in patch 11/11. > > With this addition, I see the following: > > [1.782180] musb-hdrc: version 6.0, ?dma?, otg (peripheral+host) > [1.809966] musb-hdrc musb-hdrc.0: MUSB HDRC host driver > [1.819068] musb-hdrc musb-hdrc.0: new USB bus registered, assigned > bus number 1 > [1.827970] usb usb1: New USB device found, idVendor=1d6b, > idProduct=0002 > [1.835184] usb usb1: New USB device strings: Mfr=3, Product=2, > SerialNumber=1 > [1.842818] usb usb1: Product: MUSB HDRC host driver > [1.848031] usb usb1: Manufacturer: Linux > 3.6.0-rc1-00038-g8a1ec8f-dirty musb-hcd > [1.855933] usb usb1: SerialNumber: musb-hdrc.0 > [1.866913] hub 1-0:1.0: USB hub found > [1.871192] hub 1-0:1.0: 1 port detected > [1.878106] musb-hdrc musb-hdrc.0: USB Host mode controller at > d08c using PIO, IRQ 18 > > ... but no USB functions. Also, every two seconds, the following message is > printed: > > [ 11.036608] musb_bus_suspend 2308: trying to suspend as a_wait_vrise > while active > [ 13.044811] musb_bus_suspend 2308: trying to suspend as a_wait_vrise > while active > [ 15.052196] musb_bus_suspend 2308: trying to suspend as a_wait_vrise > while active Do you see them even when you connect a device to port? Ajay > > > Anything obvious that I'm missing? > > > Thanks, > Daniel > -- > To unsubscribe from this list: send the line "unsubscribe linux-usb" in the > body of a message to majord...@vger.kernel.org More majordomo info at > http://vger.kernel.org/majordomo-info.html --- This email message is for the sole use of the intended recipient(s) and may contain confidential information. Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient, please contact the sender by reply email and destroy all copies of the original message. --- -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
XHCI: URB not cancelled during disconnect of a MSC device
Hi, I am using v3.5 kernel and running a test where I disconnect a SS MSC device while a big file is being written to it. I am also watching the lsusb output in parallel. Expectation is that SS MSC device should immediately disappear from lsusb output but sometime I see that SS MSC is listed for 30 seconds and then only disappears. Log is copied below. Looking further into the issue shows that in failing case an URB is not given back by XHCI driver and so SCSI layer unlinks it after 30second of timeout. This URB was actually given to XHCI host controller and a disconnect happens while packet transfer was in progress. I looked at XHCI driver and could not find request being aborted by driver (in xhci_free_dev() fn) before issuing disable slot or reset device command. What is expectation in this scenario? Is it XHCI driver responsibility to abort and giveback all the request (as in EHCI using endpoint_disable()) OR host controller hardware should abort and post an error completion event for each request? Why XHCI driver doesn't have an endpoint_disable() implemented as done for other controller? === Working case [ 3406.676989] Port Status Change Event for port 2 [ 3406.711530] get port status, actual port 1 status = 0x4202c0 [ 3406.711531] Get port status returned 0x4102c0 [ 3406.711629] clear port connect change, actual port 1 status = 0x4002c0 [ 3406.711664] clear port link state change, actual port 1 status = 0x2c0 [ 3406.721606] [E.f2e0b980.Transfer error on endpoint <=== URB submitted here but gets transfer error [ 3406.721742] Cleaning up stalled endpoint ring [ 3406.721744] Finding segment containing stopped TRB. [ 3406.721746] Finding endpoint context [ 3406.721747] Finding segment containing last TRB in TD. [ 3406.721749] Cycle state = 0x1 [ 3406.721750] New dequeue segment = f2073880 (virtual) [ 3406.721752] New dequeue pointer = 0x337be380 (DMA) [ 3406.721754] Queueing new dequeue state [ 3406.721756] Set TR Deq Ptr cmd, new deq seg = f2073880 (0x337be000 dma), new deq ptr = f37be380 (0x337be380 dma), new cycle = 1 [ 3406.721758]// Ding dong! [ 3406.721855] Giveback URB f2e0b980, len = 0, expected = 31, status = -71 <=== URB given back by XHCI driver = === Non Working case [ 2971.576389] Port Status Change Event for port 2 [ 2971.576487] [E.f2d0c480. <=== URB submitted but no error. [ 2971.585007] get port status, actual port 1 status = 0x4202c0 [ 2971.585079] Get port status returned 0x4102c0 [ 2971.585178] clear port connect change, actual port 1 status = 0x4002c0 [ 2971.585213] clear port link state change, actual port 1 status = 0x2c0 [ 2971.640030] get port status, actual port 1 status = 0x2d1 [ 2971.640031] Get port status returned 0x2d1 [ 2971.696029] get port status, actual port 1 status = 0x2d1 [ 2971.696031] Get port status returned 0x2d1 [ 2971.900031] get port status, actual port 1 status = 0x2d1 [ 2971.900034] Get port status returned 0x2d1 [ 2972.060480] Port Status Change Event for port 2 [ 2972.104039] get port status, actual port 1 status = 0x2802a0 [ 2972.104041] Get port status returned 0x3002a0 [ 2972.104079] clear port reset change, actual port 1 status = 0x802a0 [ 2972.104108] clear port warm(BH) reset change, actual port 1 status = 0x2a0 [ 2972.104138] clear port link state change, actual port 1 status = 0x2a0 [ 2972.104144] usb 6-2: USB disconnect, device number 6 <=== 30 seconds gap > [ 3002.080058] Cancel URB f2d0c480, dev 2, ep 0x81, starting at offset 0x337ad060 <== SCSI layer cancelling URB after 30 seconds [ 3002.080063]// Ding dong! [ 2972.060569] Stopped on Transfer TRB [ 3002.080817] Removing canceled TD starting at 0x337ad060 (dma). [ 3002.080820] Finding segment containing stopped TRB. [ 3002.080822] Finding endpoint context [ 3002.080823] Finding segment containing last TRB in TD. [ 3002.080825] Cycle state = 0x0 [ 3002.080827] New dequeue segment = f71628f0 (virtual) [ 3002.080828] New dequeue pointer = 0x337ad070 (DMA) [ 3002.080831] Set TR Deq Ptr cmd, new deq seg = f71628f0 (0x337ad000 dma), new deq ptr = f37ad070 (0x337ad070 dma), new cycle = 0 = Thanks Ajay -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: XHCI: URB not cancelled during disconnect of a MSC device
Hi, >> I am using v3.5 kernel and running a test where I disconnect >> a SS MSC device while a big file is being written to it. I am >> also watching the lsusb output in parallel. Expectation is >> that SS MSC device should immediately disappear from lsusb >> output but sometime I see that SS MSC is listed for 30 >> seconds and then only disappears. Log is copied below. >> >> Looking further into the issue shows that in failing case an >> URB is not given back by XHCI driver and so SCSI layer >> unlinks it after 30second of timeout. This URB was actually >> given to XHCI host controller and a disconnect happens while >> packet transfer was in progress. I looked at XHCI driver and >> could not find request being aborted by driver (in >> xhci_free_dev() fn) before issuing disable slot or reset >> device command. What is expectation in this scenario? Is it >> XHCI driver responsibility to abort and giveback all the >> request (as in EHCI using endpoint_disable()) OR host >> controller hardware should abort and post an error completion >> event for each request? Why XHCI driver doesn't have an >> endpoint_disable() implemented as done for other controller? >> > > I have faced similar scenario, check whether patch > http://marc.info/?l=linux-usb&m=134382746009339&w=2could could help, > Alan suggesting that hcd controller should release all URBs when device got > disconnected. It didn't help. Thanks, Ajay > > -RaviBabu > >> === Working case [ >> 3406.676989] Port Status Change Event for port 2 [ >> 3406.711530] get port status, actual port 1 status = 0x4202c0 >> [ 3406.711531] Get port status returned 0x4102c0 [ >> 3406.711629] clear port connect change, actual port 1 status >> = 0x4002c0 [ 3406.711664] clear port link state change, >> actual port 1 status = 0x2c0 [ 3406.721606] >> [E.f2e0b980.Transfer error on endpoint <=== URB submitted >> here but gets transfer error [ 3406.721742] Cleaning up >> stalled endpoint ring [ 3406.721744] Finding segment >> containing stopped TRB. >> [ 3406.721746] Finding endpoint context >> [ 3406.721747] Finding segment containing last TRB in TD. >> [ 3406.721749] Cycle state = 0x1 >> [ 3406.721750] New dequeue segment = f2073880 (virtual) [ >> 3406.721752] New dequeue pointer = 0x337be380 (DMA) [ >> 3406.721754] Queueing new dequeue state [ 3406.721756] Set TR >> Deq Ptr cmd, new deq seg = f2073880 (0x337be000 dma), new deq >> ptr = f37be380 (0x337be380 dma), new cycle = 1 [ >> 3406.721758]// Ding dong! >> [ 3406.721855] Giveback URB f2e0b980, len = 0, expected = 31, status = >> -71 <=== URB given back by XHCI driver >> = >> >> >> === Non Working case [ >> 2971.576389] Port Status Change Event for port 2 >> [ 2971.576487] [E.f2d0c480. <=== URB submitted but no error. >> [ 2971.585007] get port status, actual port 1 status = >> 0x4202c0 [ 2971.585079] Get port status returned 0x4102c0 [ >> 2971.585178] clear port connect change, actual port 1 status >> = 0x4002c0 [ 2971.585213] clear port link state change, >> actual port 1 status = 0x2c0 [ 2971.640030] get port status, >> actual port 1 status = 0x2d1 [ 2971.640031] Get port status >> returned 0x2d1 [ 2971.696029] get port status, actual port 1 >> status = 0x2d1 [ 2971.696031] Get port status returned 0x2d1 >> [ 2971.900031] get port status, actual port 1 status = 0x2d1 >> [ 2971.900034] Get port status returned 0x2d1 [ 2972.060480] >> Port Status Change Event for port 2 [ 2972.104039] get port >> status, actual port 1 status = 0x2802a0 [ 2972.104041] Get >> port status returned 0x3002a0 [ 2972.104079] clear port reset >> change, actual port 1 status = 0x802a0 [ 2972.104108] clear >> port warm(BH) reset change, actual port 1 status = 0x2a0 [ >> 2972.104138] clear port link state change, actual port 1 >> status = 0x2a0 [ 2972.104144] usb 6-2: USB disconnect, device number 6 >> >> <=== 30 seconds gap > >> >> [ 3002.080058] Cancel URB f2d0c480, dev 2, ep 0x81, starting at offset >> 0x337ad060 <== SCSI layer cancelling URB after 30 seconds >> [ 3002.080063]// Ding dong! >> [ 2972.060569] Stopped on Transfer TRB >> [ 3002.080817] Removing canceled TD starting at 0x337ad060 (dma). >> [ 3002.080820] Finding segment containing stopped TRB. >> [ 3002.080822] Finding endpoint context >> [ 3002.080823] Finding segment containing last TRB in TD. >> [ 3002.080825] Cycle state = 0x0 >> [ 3002.080827] New dequeue segment = f71628f0 (virtual) [ >> 3002.080828] New dequeue pointer = 0x337ad070 (DMA) [ >> 3002.080831] Set TR Deq Ptr cmd, new deq seg = f71628f0 >> (0x337ad000 dma), new deq ptr = f37ad070 (0x337ad070 dma), >> new cycle = 0 >> >> = >> >> Thanks >> Ajay >> -- >> To unsubscribe from this list: send the line "unsubscribe >> linux-usb" in the body of a message to >> majord...@vger.kernel.org More majordomo info at >> http://vger.kernel.org/majordomo-info.html >> -- T
Re: XHCI: URB not cancelled during disconnect of a MSC device
Hi Sarah On Thu, Sep 13, 2012 at 2:37 AM, Sarah Sharp wrote: > On Wed, Sep 12, 2012 at 03:36:39PM +0530, Ajay Gupta wrote: >> Hi, >> >> I am using v3.5 kernel and running a test where I disconnect a SS MSC >> device while a big file is being written to it. I am also watching the >> lsusb output in parallel. Expectation is that SS MSC device should >> immediately disappear from lsusb output but sometime I see that SS MSC >> is listed for 30 seconds and then only disappears. Log is copied >> below. >> >> Looking further into the issue shows that in failing case an URB is >> not given back by XHCI driver and so SCSI layer unlinks it after >> 30second of timeout. This URB was actually given to XHCI host >> controller and a disconnect happens while packet transfer was in >> progress. I looked at XHCI driver and could not find request being >> aborted by driver (in xhci_free_dev() fn) before issuing disable slot >> or reset device command. What is expectation in this scenario? > > The expectation is that the mass storage driver should have aborted any > URBs in its disconnect function before xhci_free_dev() is called. I > believe all USB drivers are required to do this. If that assumption > isn't true, then, yes, I need to revisit any place that xHCI rings get > freed. I think your assumption is correct and same has been discussed by Alan at http://marc.info/?l=linux-usb&m=134382746009339&w=2could The 30 seconds delay seems to be expected linux storage behavior as per discussion in above thread. I tested again with little modified version of code (copied below) suggested in above link and don't see any issue. I understand that below code is a pure hack and *not* a solution. - --- a/usb.c +++ b/usb.c @@ -834,6 +834,8 @@ static void quiesce_and_remove_host(struct us_data *us) if (test_bit(US_FLIDX_SCAN_PENDING, &us->dflags)) usb_autopm_put_interface_no_suspend(us->pusb_intf); + /* stop the current urbs when the device got disconnected */ + usb_stor_stop_transport(us); /* Removing the host will perform an orderly shutdown: caches * synchronized, disks spun down, etc. */ - Alan suggested to add logic inside host controller driver to unlink all URBs at disconnect. This is required as either SCSI or storage layer doesn't know if device is unplugged or unbounded. Another solution could be to add some kind of flag notifying to SCSI layer of device disconnect or unbound and so SCSI layer shouldn't wait for 30 seconds if it's disconnect. >> Is it >> XHCI driver responsibility to abort and giveback all the request (as >> in EHCI using endpoint_disable()) >> Why XHCI >> driver doesn't have an endpoint_disable() implemented as done for >> other controller? > > The xHCI driver just works differently than EHCI. It's not a > requirement to implement the endpoint disable function, AFAIK. From > what I remember, the endpoint disable function was supposed to be used > before switching alternate interface settings, but it was called too > late to be of much use to the xHCI bandwidth functions. So I created > new bandwidth functions and ignored the endpoint disable functions. > Maybe Alan can explain what the requirements about the endpoint disable > function are? endpoint_disbale is also called from usbcore layer in disconnect path. > >> OR host controller hardware should >> abort and post an error completion event for each request? > > If the host controller hardware was in the middle of a transfer when the > device was disconnected, and that caused the transfer to timeout, then, > yes, the host should return an event with a transfer error and halt the > event ring, as described in the xHCI 1.0 spec, section 4.10.2.3. Even > if the host wasn't strictly speaking in the middle of a transfer during > the disconnect, the endpoint ring should remain on the host's schedule > as long as the xHCI driver didn't issue a stop endpoint command. I > would expect that the transfer would timeout the next time it was > executed from the schedule. > > Basically, the host controller has to play dumb and hand back events > with error statuses for all TDs that continue to be schedulable after a > device disconnect. Eventually the USB core will notice the disconnect, > notify the driver, and it will cancel any outstanding URBs. > >> === Working case [...] >> dma), new deq ptr = f37ad070 (0x337ad070 dma), new cycle = 0 > > According to that
Re: XHCI: URB not cancelled during disconnect of a MSC device
Hi >> >> I am using v3.5 kernel and running a test where I disconnect a SS MSC >> >> device while a big file is being written to it. I am also watching the >> >> lsusb output in parallel. Expectation is that SS MSC device should >> >> immediately disappear from lsusb output but sometime I see that SS MSC >> >> is listed for 30 seconds and then only disappears. Log is copied >> >> below. >> >> >> >> Looking further into the issue shows that in failing case an URB is >> >> not given back by XHCI driver and so SCSI layer unlinks it after >> >> 30second of timeout. This URB was actually given to XHCI host >> >> controller and a disconnect happens while packet transfer was in >> >> progress. I looked at XHCI driver and could not find request being >> >> aborted by driver (in xhci_free_dev() fn) before issuing disable slot >> >> or reset device command. What is expectation in this scenario? >> > >> > The expectation is that the mass storage driver should have aborted any >> > URBs in its disconnect function before xhci_free_dev() is called. I >> > believe all USB drivers are required to do this. If that assumption >> > isn't true, then, yes, I need to revisit any place that xHCI rings get >> > freed. > > It's not that simple. > >> I think your assumption is correct and same has been discussed by Alan at >> http://marc.info/?l=linux-usb&m=134382746009339&w=2could > > As discussed in that thread, the real requirement is that the host > controller should complete URBs with an error if the device has > disconnected (and therefore isn't sending any handshake packets). Sarah, Do you plan to add this support in XHCI driver? > >> Alan suggested to add logic inside host controller driver to unlink >> all URBs at disconnect. > > No, that's not what I suggested. I said that transfers should be > aborted when the controller hardware detects that the device fails to > send handshake packets. Yes, I meant the same. > >> This is required as either SCSI or storage layer doesn't know if >> device is unplugged or >> unbounded. >> >> Another solution could be to add some kind of flag notifying to SCSI >> layer of device >> disconnect or unbound and so SCSI layer shouldn't wait for 30 seconds if it's >> disconnect. > > Both of those solutions are inappropriate. Aborting URBs from within HCD after device disconnect is appropriate. right? Ajay > >> >> Is it >> >> XHCI driver responsibility to abort and giveback all the request (as >> >> in EHCI using endpoint_disable()) >> >> Why XHCI >> >> driver doesn't have an endpoint_disable() implemented as done for >> >> other controller? >> > >> > The xHCI driver just works differently than EHCI. It's not a >> > requirement to implement the endpoint disable function, AFAIK. From >> > what I remember, the endpoint disable function was supposed to be used >> > before switching alternate interface settings, but it was called too >> > late to be of much use to the xHCI bandwidth functions. So I created >> > new bandwidth functions and ignored the endpoint disable functions. >> > Maybe Alan can explain what the requirements about the endpoint disable >> > function are? > > THe endpoint_disable function doesn't have to do anything as far as > usbcore is concerned. It is a callback; it gives the HCD a chance to > deallocate any internal data structures associated with the endpoint. > > When endpoint_disable is called there shouldn't be any URBs queued for > the endpoint. The core guarantees this by preventing new URBs from > being accepted, unlinking all existing URBs, and then waiting for them > to complete. > >> endpoint_disbale is also called from usbcore layer in disconnect path. >> >> > >> >> OR host controller hardware should >> >> abort and post an error completion event for each request? >> > >> > If the host controller hardware was in the middle of a transfer when the >> > device was disconnected, and that caused the transfer to timeout, then, >> > yes, the host should return an event with a transfer error and halt the >> > event ring, as described in the xHCI 1.0 spec, section 4.10.2.3. Even >> > if the host wasn't strictly speaking in the middle of a transfer during >> > the disconnect, the endpoint ring should remain on the host's schedule >> > as long as the xHCI driver didn't issue a stop endpoint command. I >> > would expect that the transfer would timeout the next time it was >> > executed from the schedule. > > If by "timeout" you mean fail under the "3 strikes" rule, then yes, > this is what should happen. > > Alan Stern > -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: XHCI: URB not cancelled during disconnect of a MSC device
Hi, > Ajay, which host controller are you seeing the disconnect lags on? Can you > send me the output of `sudo lspci -vvv -n`? I was on leave last week so will get the data at earliest. Ajay > > Sarah Sharp > -- > To unsubscribe from this list: send the line "unsubscribe linux-usb" in the > body of a message to majord...@vger.kernel.org More majordomo info at > http://vger.kernel.org/majordomo-info.html --- This email message is for the sole use of the intended recipient(s) and may contain confidential information. Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient, please contact the sender by reply email and destroy all copies of the original message. --- -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [PATCH v2 0/2] usb: typec: ucsi: Support for DP alt mode
Hi Heikki, > -Original Message- > From: linux-usb-ow...@vger.kernel.org On > Behalf Of Heikki Krogerus > Sent: Wednesday, March 20, 2019 4:45 AM > To: Ajay Gupta ; Michael Hsu > Cc: linux-usb@vger.kernel.org > Subject: [PATCH v2 0/2] usb: typec: ucsi: Support for DP alt mode > > Hi, > > This is the second version of my proposal to add alt mode handling to ucsi > driver. > The problem in the first version was that I was not using the port alt mode > that > was associated with the partner alt mode (and vise versa), but I was instead > searching the alt mode objects in every case separately. > > So in this version I'm now always using the already associated alternate mode > partner in every case, and as far as the UCSI driver (or any other USB Type-C > driver) is concerned, that is all that it needs to do. The problem of actually > associating the correct alt mode partner object belongs to the Type-C > subsystem > core code, and any improvements to that code are out side of the scope of this > series. > > I've prepared a patch that allows the alt mode drivers to find the correct > port alt > mode for the partner alt modes. That should cover SVIDs that define multiple > modes (note, DisplayPort Alt mode is not one of those). But as I mentioned > above, that is not related to this series. Thanks for the new set. It looks good to me. I tested it with UCSI CCG on NVIDIA GPU and it works. Tested-By: Ajay Gupta Thanks Ajay > nvpublic > You can check the v1 from here: > https://www.spinics.net/lists/linux-usb/msg176703.html > > thanks, > > Heikki Krogerus (2): > usb: typec: ucsi: Preliminary support for alternate modes > usb: typec: ucsi: Support for DisplayPort alt mode > > drivers/usb/typec/ucsi/Makefile | 15 +- > drivers/usb/typec/ucsi/displayport.c | 297 ++ > drivers/usb/typec/ucsi/trace.c | 12 + > drivers/usb/typec/ucsi/trace.h | 26 ++ > drivers/usb/typec/ucsi/ucsi.c| 366 ++- > drivers/usb/typec/ucsi/ucsi.h| 93 +++ > 6 files changed, 738 insertions(+), 71 deletions(-) create mode 100644 > drivers/usb/typec/ucsi/displayport.c > > -- > 2.20.1
[PATCH 2/2] usb: typec: Add driver for NVIDIA Alt Modes
From: Ajay Gupta Latest NVIDIA GPUs support VirtualLink device. Since USBIF has not assigned a Standard ID (SID) for VirtualLink so using NVIDA VID 0x955 as SVID. Signed-off-by: Ajay Gupta --- drivers/usb/typec/altmodes/Kconfig | 10 +++ drivers/usb/typec/altmodes/Makefile | 2 ++ drivers/usb/typec/altmodes/nvidia.c | 44 + drivers/usb/typec/ucsi/ucsi.c | 4 ++- include/linux/usb/typec_dp.h| 5 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/typec/altmodes/nvidia.c diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig index ef2226eb7a33..187690fd1a5b 100644 --- a/drivers/usb/typec/altmodes/Kconfig +++ b/drivers/usb/typec/altmodes/Kconfig @@ -12,4 +12,14 @@ config TYPEC_DP_ALTMODE To compile this driver as a module, choose M here: the module will be called typec_displayport. +config TYPEC_NVIDIA_ALTMODE + tristate "NVIDIA Alternate Mode driver" + depends on TYPEC_DP_ALTMODE + help + Latest NVIDIA GPUs support VirtualLink devices. Select this + to enable support for VirtualLink devices with NVIDIA GPUs. + + To compile this driver as a module, choose M here: the + module will be called typec_displayport. + endmenu diff --git a/drivers/usb/typec/altmodes/Makefile b/drivers/usb/typec/altmodes/Makefile index eda8456f1c92..45717548b396 100644 --- a/drivers/usb/typec/altmodes/Makefile +++ b/drivers/usb/typec/altmodes/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_TYPEC_DP_ALTMODE) += typec_displayport.o typec_displayport-y:= displayport.o +obj-$(CONFIG_TYPEC_NVIDIA_ALTMODE) += typec_nvidia.o +typec_nvidia-y := nvidia.o diff --git a/drivers/usb/typec/altmodes/nvidia.c b/drivers/usb/typec/altmodes/nvidia.c new file mode 100644 index ..c36769736405 --- /dev/null +++ b/drivers/usb/typec/altmodes/nvidia.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 NVIDIA Corporation. All rights reserved. + * + * NVIDIA USB Type-C Alt Mode Driver + */ +#include +#include +#include +#include "displayport.h" + +static int nvidia_altmode_probe(struct typec_altmode *alt) +{ + if (alt->svid == USB_TYPEC_NVIDIA_VLINK_SID) + return dp_altmode_probe(alt); + else + return -ENOTSUPP; +} + +static void nvidia_altmode_remove(struct typec_altmode *alt) +{ + if (alt->svid == USB_TYPEC_NVIDIA_VLINK_SID) + dp_altmode_remove(alt); +} + +static const struct typec_device_id nvidia_typec_id[] = { + { USB_TYPEC_NVIDIA_VLINK_SID, TYPEC_ANY_MODE }, + { }, +}; +MODULE_DEVICE_TABLE(typec, nvidia_typec_id); + +static struct typec_altmode_driver nvidia_altmode_driver = { + .id_table = nvidia_typec_id, + .probe = nvidia_altmode_probe, + .remove = nvidia_altmode_remove, + .driver = { + .name = "typec_nvidia", + .owner = THIS_MODULE, + }, +}; +module_typec_altmode_driver(nvidia_altmode_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("NVIDIA USB Type-C Alt Mode Driver"); diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 46feae074d8f..a8c8e66621ec 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -306,6 +306,7 @@ static int ucsi_register_altmode(struct ucsi_connector *con, switch (desc->svid) { case USB_TYPEC_DP_SID: + case USB_TYPEC_NVIDIA_VLINK_SID: alt = ucsi_register_displayport(con, override, i, desc); break; default: @@ -424,7 +425,8 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) while (adev[i]) { if (recipient == UCSI_RECIPIENT_SOP && - adev[i]->svid == USB_TYPEC_DP_SID) { + (adev[i]->svid == USB_TYPEC_DP_SID || + adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID)) { pdev = typec_altmode_get_partner(adev[i]); ucsi_displayport_remove_partner((void *)pdev); } diff --git a/include/linux/usb/typec_dp.h b/include/linux/usb/typec_dp.h index 7fa12ef8d09a..fc4c7edb2e8a 100644 --- a/include/linux/usb/typec_dp.h +++ b/include/linux/usb/typec_dp.h @@ -5,6 +5,11 @@ #include #define USB_TYPEC_DP_SID 0xff01 +/* USB IF has not assigned a Standard ID (SID) for VirtualLink, + * so the manufacturers of VirtualLink adapters use their Vendor + * IDs as the SVID. + */ +#define USB_TYPEC_NVIDIA_VLINK_SID 0x955 /* NVIDIA VirtualLink */ #define USB_TYPEC_DP_MODE 1 /* -- 2.17.1
[PATCH 1/2] usb: typec: displayport: Export probe and remove functions
From: Heikki Krogerus VirtualLink standard extends the DisplayPort Alt Mode by utilizing also the USB 2 pins on the USB Type-C connector. It uses the same messages as DisplayPort, but not the DP SVID. At the time of writing, USB IF has not assigned a Standard ID (SID) for VirtualLink, so the manufacturers of VirtualLink adapters use their Vendor IDs as the SVID. Since the SVID specific communication is exactly the same as with DisplayPort alternate mode, there is no need to implement separate driver for VirtualLink. We'll handle the current VirtualLink adapters with probe drivers, and once there is SVID assigned for it, we add it to the displayport alt mode driver. To support probing drivers, exporting the probe and remove functions, and also changing the DP_HEADER helper macro to use the SVID of the alternate mode device instead of the DisplayPort alt mode SVID. [Fixed two warnings from checkpatch.pl script -Ajay] Signed-off-by: Heikki Krogerus Signed-off-by: Ajay Gupta Tested-by: Ajay Gupta --- drivers/usb/typec/altmodes/displayport.c | 12 +++- drivers/usb/typec/altmodes/displayport.h | 8 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 drivers/usb/typec/altmodes/displayport.h diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 610d790bc9be..bbf317c838d3 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -14,7 +14,7 @@ #include #include -#define DP_HEADER(cmd) (VDO(USB_TYPEC_DP_SID, 1, cmd) | \ +#define DP_HEADER(_dp, cmd)(VDO((_dp)->alt->svid, 1, cmd) | \ VDO_OPOS(USB_TYPEC_DP_MODE)) enum { @@ -155,7 +155,7 @@ static int dp_altmode_configured(struct dp_altmode *dp) static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) { - u32 header = DP_HEADER(DP_CMD_CONFIGURE); + u32 header = DP_HEADER(dp, DP_CMD_CONFIGURE); int ret; ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data); @@ -193,7 +193,7 @@ static void dp_altmode_work(struct work_struct *work) dev_err(&dp->alt->dev, "failed to enter mode\n"); break; case DP_STATE_UPDATE: - header = DP_HEADER(DP_CMD_STATUS_UPDATE); + header = DP_HEADER(dp, DP_CMD_STATUS_UPDATE); vdo = 1; ret = typec_altmode_vdm(dp->alt, header, &vdo, 2); if (ret) @@ -507,7 +507,7 @@ static const struct attribute_group dp_altmode_group = { .attrs = dp_altmode_attrs, }; -static int dp_altmode_probe(struct typec_altmode *alt) +int dp_altmode_probe(struct typec_altmode *alt) { const struct typec_altmode *port = typec_altmode_get_partner(alt); struct dp_altmode *dp; @@ -545,14 +545,16 @@ static int dp_altmode_probe(struct typec_altmode *alt) return 0; } +EXPORT_SYMBOL_GPL(dp_altmode_probe); -static void dp_altmode_remove(struct typec_altmode *alt) +void dp_altmode_remove(struct typec_altmode *alt) { struct dp_altmode *dp = typec_altmode_get_drvdata(alt); sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group); cancel_work_sync(&dp->work); } +EXPORT_SYMBOL_GPL(dp_altmode_remove); static const struct typec_device_id dp_typec_id[] = { { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE }, diff --git a/drivers/usb/typec/altmodes/displayport.h b/drivers/usb/typec/altmodes/displayport.h new file mode 100644 index ..e120364da9fd --- /dev/null +++ b/drivers/usb/typec/altmodes/displayport.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE) +int dp_altmode_probe(struct typec_altmode *alt); +void dp_altmode_remove(struct typec_altmode *alt); +#else +int dp_altmode_probe(struct typec_altmode *alt) { return -ENOTSUPP; } +void dp_altmode_remove(struct typec_altmode *alt) { } +#endif /* CONFIG_TYPEC_DP_ALTMODE */ -- 2.17.1
[PATCH 0/2] Add VirtualLink display altmode support
Hi Heikki, Please help review these two changes which adds support for VirtualLink display altmode devices using NVIDIA GPU. You had shared the first patch during our offline discussion. The second patch adds NVIDIA display altmode driver. This set is created against latest Linus's kernel and patches at [1] forwarded to Greg from you. [1] https://marc.info/?l=linux-usb&m=155488414908414&w=2 Thanks Ajay Ajay Gupta (1): usb: typec: Add driver for NVIDIA Alt Modes Heikki Krogerus (1): usb: typec: displayport: Export probe and remove functions drivers/usb/typec/altmodes/Kconfig | 10 ++ drivers/usb/typec/altmodes/Makefile | 2 ++ drivers/usb/typec/altmodes/displayport.c | 12 --- drivers/usb/typec/altmodes/displayport.h | 8 + drivers/usb/typec/altmodes/nvidia.c | 44 drivers/usb/typec/ucsi/ucsi.c| 4 ++- include/linux/usb/typec_dp.h | 5 +++ 7 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 drivers/usb/typec/altmodes/displayport.h create mode 100644 drivers/usb/typec/altmodes/nvidia.c -- 2.17.1
[PATCH v6 1/2] usb: typec: ucsi: ccg: add get_fw_info function
Function is to get the details of ccg firmware and device version. It will be useful in debugging and also during firmware update. Signed-off-by: Ajay Gupta Signed-off-by: Heikki Krogerus --- Changes from v5 to v6 - None drivers/usb/typec/ucsi/ucsi_ccg.c | 66 ++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index de8a43bdff68..3884fb41c72e 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -17,15 +17,54 @@ #include #include "ucsi.h" +enum enum_fw_mode { + BOOT, /* bootloader */ + FW1,/* FW partition-1 (contains secondary fw) */ + FW2,/* FW partition-2 (contains primary fw) */ + FW_INVALID, +}; + +struct ccg_dev_info { +#define CCG_DEVINFO_FWMODE_SHIFT (0) +#define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) +#define CCG_DEVINFO_PDPORTS_SHIFT (2) +#define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT) + u8 mode; + u8 bl_mode; + __le16 silicon_id; + __le16 bl_last_row; +} __packed; + +struct version_format { + __le16 build; + u8 patch; + u8 ver; +#define CCG_VERSION_MIN_SHIFT (0) +#define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) +#define CCG_VERSION_MAJ_SHIFT (4) +#define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) +} __packed; + +struct version_info { + struct version_format base; + struct version_format app; +}; + struct ucsi_ccg { struct device *dev; struct ucsi *ucsi; struct ucsi_ppm ppm; struct i2c_client *client; + struct ccg_dev_info info; + /* version info for boot, primary and secondary */ + struct version_info version[FW2 + 1]; }; -#define CCGX_RAB_INTR_REG 0x06 -#define CCGX_RAB_UCSI_CONTROL 0x39 +#define CCGX_RAB_DEVICE_MODE 0x +#define CCGX_RAB_INTR_REG 0x0006 +#define CCGX_RAB_READ_ALL_VER 0x0010 +#define CCGX_RAB_READ_FW2_VER 0x0020 +#define CCGX_RAB_UCSI_CONTROL 0x0039 #define CCGX_RAB_UCSI_CONTROL_STARTBIT(0) #define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) #define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) @@ -220,6 +259,23 @@ static irqreturn_t ccg_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static int get_fw_info(struct ucsi_ccg *uc) +{ + int err; + + err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version), + sizeof(uc->version)); + if (err < 0) + return err; + + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), + sizeof(uc->info)); + if (err < 0) + return err; + + return 0; +} + static int ucsi_ccg_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -248,6 +304,12 @@ static int ucsi_ccg_probe(struct i2c_client *client, return status; } + status = get_fw_info(uc); + if (status < 0) { + dev_err(uc->dev, "get_fw_info failed - %d\n", status); + return status; + } + status = devm_request_threaded_irq(dev, client->irq, NULL, ccg_irq_handler, IRQF_ONESHOT | IRQF_TRIGGER_HIGH, -- 2.17.1
[PATCH v6 0/2] Add support for firmware update on Cypres CCGx
Hi Heikki These changes adds support for updating firmware on Cypress CCGx controller. New version (v6) fixes sparse warning as reported at [1]. I have tested them on NVIDIA GPU card. Firmware binary is already merged. Details are at [2]. Please help review this set. Thanks Ajay [1] https://marc.info/?l=linux-usb&m=155491396220133&w=2 [2] https://marc.info/?l=linux-usb&m=155006182508289&w=2 Ajay Gupta (2): usb: typec: ucsi: ccg: add get_fw_info function usb: typec: ucsi: ccg: add firmware flashing support drivers/usb/typec/ucsi/ucsi_ccg.c | 887 +- 1 file changed, 877 insertions(+), 10 deletions(-) -- 2.17.1
[PATCH v6 2/2] usb: typec: ucsi: ccg: add firmware flashing support
CCGx has two copies of the firmware in addition to the bootloader. If the device is running FW1, FW2 can be updated with the new version. Dual firmware mode allows the CCG device to stay in a PD contract and support USB PD and Type-C functionality while a firmware update is in progress. First we read the currently flashed firmware version of both primary and secondary firmware and then compare it with version of firmware file to determine if flashing is required. Command framework is added to support sending commands to CCGx controller. We wait for response after sending the command and then read the response from RAB_RESPONSE register. Below commands are supported, - ENTER_FLASHING - RESET - PDPORT_ENABLE - JUMP_TO_BOOT - FLASH_ROW_RW - VALIDATE_FW Command specific mutex lock is also added to sync between driver and user threads. PD port number information is added which is required while sending PD_PORT_ENABLE command Signed-off-by: Ajay Gupta --- Changes from v5 to v6 - Fixed below sparse warnings "restricted __le16 degrades to integer" drivers/usb/typec/ucsi/ucsi_ccg.c | 831 +- 1 file changed, 818 insertions(+), 13 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 3884fb41c72e..a899d4c29125 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -9,6 +9,7 @@ */ #include #include +#include #include #include #include @@ -24,6 +25,73 @@ enum enum_fw_mode { FW_INVALID, }; +#define CCGX_RAB_DEVICE_MODE 0x +#define CCGX_RAB_INTR_REG 0x0006 +#define DEV_INT BIT(0) +#define PORT0_INT BIT(1) +#define PORT1_INT BIT(2) +#define UCSI_READ_INT BIT(7) +#define CCGX_RAB_JUMP_TO_BOOT 0x0007 +#define TO_BOOT 'J' +#define TO_ALT_FW 'A' +#define CCGX_RAB_RESET_REQ 0x0008 +#define RESET_SIG 'R' +#define CMD_RESET_I2C 0x0 +#define CMD_RESET_DEV 0x1 +#define CCGX_RAB_ENTER_FLASHING0x000A +#define FLASH_ENTER_SIG 'P' +#define CCGX_RAB_VALIDATE_FW 0x000B +#define CCGX_RAB_FLASH_ROW_RW 0x000C +#define FLASH_SIG 'F' +#define FLASH_RD_CMD 0x0 +#define FLASH_WR_CMD 0x1 +#define FLASH_FWCT1_WR_CMD0x2 +#define FLASH_FWCT2_WR_CMD0x3 +#define FLASH_FWCT_SIG_WR_CMD 0x4 +#define CCGX_RAB_READ_ALL_VER 0x0010 +#define CCGX_RAB_READ_FW2_VER 0x0020 +#define CCGX_RAB_UCSI_CONTROL 0x0039 +#define CCGX_RAB_UCSI_CONTROL_STARTBIT(0) +#define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) +#define REG_FLASH_RW_MEM0x0200 +#define DEV_REG_IDXCCGX_RAB_DEVICE_MODE +#define CCGX_RAB_PDPORT_ENABLE 0x002C +#define PDPORT_1 BIT(0) +#define PDPORT_2 BIT(1) +#define CCGX_RAB_RESPONSE 0x007E +#define ASYNC_EVENT BIT(7) + +/* CCGx events & async msg codes */ +#define RESET_COMPLETE 0x80 +#define EVENT_INDEXRESET_COMPLETE +#define PORT_CONNECT_DET 0x84 +#define PORT_DISCONNECT_DET0x85 +#define ROLE_SWAP_COMPELETE0x87 + +/* ccg firmware */ +#define CYACD_LINE_SIZE 527 +#define CCG4_ROW_SIZE 256 +#define FW1_METADATA_ROW0x1FF +#define FW2_METADATA_ROW0x1FE +#define FW_CFG_TABLE_SIG_SIZE 256 + +static int secondary_fw_min_ver = 41; + +enum enum_flash_mode { + SECONDARY_BL, /* update secondary using bootloader */ + PRIMARY,/* update primary using secondary */ + SECONDARY, /* update secondary using primary */ + FLASH_NOT_NEEDED, /* update not required */ + FLASH_INVALID, +}; + +static const char * const ccg_fw_names[] = { + "ccg_boot.cyacd", + "ccg_primary.cyacd", + "ccg_secondary.cyacd" +}; + struct ccg_dev_info { #define CCG_DEVINFO_FWMODE_SHIFT (0) #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) @@ -50,6 +118,50 @@ struct version_info { struct version_format app; }; +struct fw_config_table { + u32 identity; + u16 table_size; + u8 fwct_version; + u8 is_key_change; + u8 guid[16]; + struct version_format base; + struct version_format ap
RE: [PATCH 2/4] usb: typec: ucsi: ccg: add firmware flashing support
Hi Heikki, > -Original Message- > From: Heikki Krogerus > Sent: Thursday, April 11, 2019 12:40 AM > To: Ajay Gupta > Cc: kbuild test robot ; kbuild-...@01.org; Greg Kroah-Hartman > ; linux-usb@vger.kernel.org > Subject: Re: [PATCH 2/4] usb: typec: ucsi: ccg: add firmware flashing support > > Hi Ajay, > > On Thu, Apr 11, 2019 at 12:31:45AM +0800, kbuild test robot wrote: > > Hi Heikki, > > > > I love your patch! Perhaps something to improve: > > > > [auto build test WARNING on usb/usb-testing] [also build test WARNING > > on v5.1-rc4 next-20190410] [if your patch is applied to the wrong git > > tree, please drop us a note to help improve the system] > > > > url:https://github.com/0day-ci/linux/commits/Heikki-Krogerus/usb-typec- > ucsi-Remaining-changes-for-v5-2/20190410-221455 > > base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb- > testing > > reproduce: > > # apt-get install sparse > > make ARCH=x86_64 allmodconfig > > make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' > > > > > > sparse warnings: (new ones prefixed by >>) > > > >drivers/usb/typec/ucsi/ucsi_ccg.c:212:24: sparse: expression using > sizeof(void) > >drivers/usb/typec/ucsi/ucsi_ccg.c:212:24: sparse: expression using > > sizeof(void) > > >> drivers/usb/typec/ucsi/ucsi_ccg.c:690:16: sparse: restricted __le16 > > >> degrades to integer > >drivers/usb/typec/ucsi/ucsi_ccg.c:698:24: sparse: restricted __le16 > > degrades > to integer > >drivers/usb/typec/ucsi/ucsi_ccg.c:735:26: sparse: restricted __le16 > > degrades > to integer > >drivers/usb/typec/ucsi/ucsi_ccg.c:737:33: sparse: restricted __le16 > > degrades > to integer > >drivers/usb/typec/ucsi/ucsi_ccg.c:777:37: sparse: restricted __le16 > > degrades to integer > > > > vim +690 drivers/usb/typec/ucsi/ucsi_ccg.c > > > >680 > >681 static bool ccg_check_vendor_version(struct ucsi_ccg *uc, > >682 struct version_format *app, > >683 struct fw_config_table > > *fw_cfg) > >684 { > >685 struct device *dev = uc->dev; > >686 > >687 /* Check if the fw build is for supported vendors. > >688 * Add all supported vendors here. > >689 */ > > > 690 if (app->build != (('n' << 8) | 'v')) { > > How about a macro for these? > > #define CCG_VERSION_BUILD (__le16)(...) Sure, I have fixed them and posted version-6 of patches at https://marc.info/?l=linux-usb&m=155501300619846&w=2 Please help review. Thanks > nvpublic > > >691 dev_info(dev, "current fw is not from supported > vendor\n"); > >692 return false; > >693 } > >694 > >695 /* Check if the new fw build is for supported vendors > >696 * Add all supported vendors here. > >697 */ > >698 if (fw_cfg->app.build != (('n' << 8) | 'v')) { > >699 dev_info(dev, "new fw is not from supported > vendor\n"); > >700 return false; > >701 } > >702 return true; > >703 } > >704 > > thanks, > > -- > heikki
RE: [PATCH v6 2/2] usb: typec: ucsi: ccg: add firmware flashing support
Hi Heikki, > -Original Message- > From: linux-usb-ow...@vger.kernel.org On > Behalf Of Heikki Krogerus > Sent: Friday, April 12, 2019 8:02 AM > To: Ajay Gupta > Cc: linux-usb@vger.kernel.org; Ajay Gupta > Subject: Re: [PATCH v6 2/2] usb: typec: ucsi: ccg: add firmware flashing > support > > Hi Ajay, > > On Thu, Apr 11, 2019 at 01:03:10PM -0700, Ajay Gupta wrote: > > +#define CCG_FW_VERSION_NVIDIA(b) (le16_to_cpu(b) != (('n' << 8) | > 'v')) > > Does that remove the sparce warning? Yes. I tested it with steps given at https://marc.info/?l=linux-usb&m=155491396220133&w=2 > Now that I see that this version is specific to NVIDIA, > I think we are going to > want to get this detail from a device property. I'll prepare an example for > you > right now so you can see how it can be done. This version supports NVIDIA specific FW signature which is used to test it. See 'n' and 'v' letters in it. Other vendors will have their own signature and can just add macros to handle it. There is also a comment " Add all supported vendors here " for this in function ccg_check_vendor_version() Thanks > nvpublic > > +static bool ccg_check_vendor_version(struct ucsi_ccg *uc, > > +struct version_format *app, > > +struct fw_config_table *fw_cfg) { > > + struct device *dev = uc->dev; > > + > > + /* Check if the fw build is for supported vendors. > > +* Add all supported vendors here. > > +*/ > > + if (CCG_FW_VERSION_NVIDIA(app->build)) { > > + dev_info(dev, "current fw is not from supported vendor\n"); > > + return false; > > + } > > + > > + /* Check if the new fw build is for supported vendors > > +* Add all supported vendors here. > > +*/ > > + if (CCG_FW_VERSION_NVIDIA(fw_cfg->app.build)) { > > + dev_info(dev, "new fw is not from supported vendor\n"); > > + return false; > > + } > > + return true; > > +} > > Br, > > -- > heikki
[PATCH v7 0/3] Add support for firmware update on Cypres CCGx
Hi Heikki These changes add support for updating firmware on Cypress CCGx controller. New version (v7) fixes comments from you by reading fw build information from device property. First patch in same as posted with v6. There is no change in it. Second patch is from you and adds fw_build device property in i2c-nvidia-gpu driver. I have updated the commit message, added a comment and also used 'n' and 'v' to be more clear than using hex code. I think it should go with usb tree since it enables a feature in usb driver. Third patch "usb: typec: ucsi: ccg: add firmware flashing support" has the change at [1] (from you) squashed into it. I have updated it to fix two sparse warning and not fail the driver if fw build info in not provided. Instead just fail fw flashing only. I have tested them on NVIDIA GPU card. Firmware binary is already merged. Details are at [2]. Please help review this set. Thanks Ajay [1] https://marc.info/?l=linux-usb&m=155508466310059&w=2 [2] https://marc.info/?l=linux-usb&m=155006182508289&w=2 Ajay Gupta (2): usb: typec: ucsi: ccg: add get_fw_info function usb: typec: ucsi: ccg: add firmware flashing support Heikki Krogerus (1): i2c: nvidia-gpu: Supply CCGx driver the fw build info drivers/i2c/busses/i2c-nvidia-gpu.c | 7 + drivers/usb/typec/ucsi/ucsi_ccg.c | 895 +++- 2 files changed, 892 insertions(+), 10 deletions(-) -- 2.17.1
[PATCH v7 1/3] usb: typec: ucsi: ccg: add get_fw_info function
Function is to get the details of ccg firmware and device version. It will be useful in debugging and also during firmware update. Signed-off-by: Ajay Gupta Signed-off-by: Heikki Krogerus --- Changes from v6 to v7 - None drivers/usb/typec/ucsi/ucsi_ccg.c | 66 ++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index de8a43bdff68..3884fb41c72e 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -17,15 +17,54 @@ #include #include "ucsi.h" +enum enum_fw_mode { + BOOT, /* bootloader */ + FW1,/* FW partition-1 (contains secondary fw) */ + FW2,/* FW partition-2 (contains primary fw) */ + FW_INVALID, +}; + +struct ccg_dev_info { +#define CCG_DEVINFO_FWMODE_SHIFT (0) +#define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) +#define CCG_DEVINFO_PDPORTS_SHIFT (2) +#define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT) + u8 mode; + u8 bl_mode; + __le16 silicon_id; + __le16 bl_last_row; +} __packed; + +struct version_format { + __le16 build; + u8 patch; + u8 ver; +#define CCG_VERSION_MIN_SHIFT (0) +#define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) +#define CCG_VERSION_MAJ_SHIFT (4) +#define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) +} __packed; + +struct version_info { + struct version_format base; + struct version_format app; +}; + struct ucsi_ccg { struct device *dev; struct ucsi *ucsi; struct ucsi_ppm ppm; struct i2c_client *client; + struct ccg_dev_info info; + /* version info for boot, primary and secondary */ + struct version_info version[FW2 + 1]; }; -#define CCGX_RAB_INTR_REG 0x06 -#define CCGX_RAB_UCSI_CONTROL 0x39 +#define CCGX_RAB_DEVICE_MODE 0x +#define CCGX_RAB_INTR_REG 0x0006 +#define CCGX_RAB_READ_ALL_VER 0x0010 +#define CCGX_RAB_READ_FW2_VER 0x0020 +#define CCGX_RAB_UCSI_CONTROL 0x0039 #define CCGX_RAB_UCSI_CONTROL_STARTBIT(0) #define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) #define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) @@ -220,6 +259,23 @@ static irqreturn_t ccg_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static int get_fw_info(struct ucsi_ccg *uc) +{ + int err; + + err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version), + sizeof(uc->version)); + if (err < 0) + return err; + + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), + sizeof(uc->info)); + if (err < 0) + return err; + + return 0; +} + static int ucsi_ccg_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -248,6 +304,12 @@ static int ucsi_ccg_probe(struct i2c_client *client, return status; } + status = get_fw_info(uc); + if (status < 0) { + dev_err(uc->dev, "get_fw_info failed - %d\n", status); + return status; + } + status = devm_request_threaded_irq(dev, client->irq, NULL, ccg_irq_handler, IRQF_ONESHOT | IRQF_TRIGGER_HIGH, -- 2.17.1
[PATCH v7 2/3] i2c: nvidia-gpu: Supply CCGx driver the fw build info
From: Heikki Krogerus Adding device property "ccgx,firmware-build" for the CCGx device, so the CCGx driver knows which firmware binary to use for a specific vendor. Signed-off-by: Heikki Krogerus Signed-off-by: Ajay Gupta --- drivers/i2c/busses/i2c-nvidia-gpu.c | 7 +++ 1 file changed, 7 insertions(+) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 4e67d5ed480e..1c8f708f212b 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -253,6 +253,12 @@ static const struct pci_device_id gpu_i2c_ids[] = { }; MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); +static const struct property_entry ccgx_props[] = { + /* Use FW built for NVIDIA (nv) only */ + PROPERTY_ENTRY_U16("ccgx,firmware-build", ('n' << 8) | 'v'), + { } +}; + static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) { struct i2c_client *ccgx_client; @@ -267,6 +273,7 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) sizeof(i2cd->gpu_ccgx_ucsi->type)); i2cd->gpu_ccgx_ucsi->addr = 0x8; i2cd->gpu_ccgx_ucsi->irq = irq; + i2cd->gpu_ccgx_ucsi->properties = ccgx_props; ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); if (!ccgx_client) return -ENODEV; -- 2.17.1
[PATCH v7 3/3] usb: typec: ucsi: ccg: add firmware flashing support
CCGx has two copies of the firmware in addition to the bootloader. If the device is running FW1, FW2 can be updated with the new version. Dual firmware mode allows the CCG device to stay in a PD contract and support USB PD and Type-C functionality while a firmware update is in progress. First we read the currently flashed firmware version of both primary and secondary firmware and then compare it with version of firmware file to determine if flashing is required. Command framework is added to support sending commands to CCGx controller. We wait for response after sending the command and then read the response from RAB_RESPONSE register. Below commands are supported, - ENTER_FLASHING - RESET - PDPORT_ENABLE - JUMP_TO_BOOT - FLASH_ROW_RW - VALIDATE_FW Command specific mutex lock is also added to sync between driver and user threads. PD port number information is added which is required while sending PD_PORT_ENABLE command Signed-off-by: Ajay Gupta --- Changes from v6 -> v7 - Read fw_build from device property - remove unused level ccg_check_fw_version() drivers/usb/typec/ucsi/ucsi_ccg.c | 839 +- 1 file changed, 826 insertions(+), 13 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 3884fb41c72e..44cadb5d18d3 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -9,6 +9,7 @@ */ #include #include +#include #include #include #include @@ -24,6 +25,73 @@ enum enum_fw_mode { FW_INVALID, }; +#define CCGX_RAB_DEVICE_MODE 0x +#define CCGX_RAB_INTR_REG 0x0006 +#define DEV_INT BIT(0) +#define PORT0_INT BIT(1) +#define PORT1_INT BIT(2) +#define UCSI_READ_INT BIT(7) +#define CCGX_RAB_JUMP_TO_BOOT 0x0007 +#define TO_BOOT 'J' +#define TO_ALT_FW 'A' +#define CCGX_RAB_RESET_REQ 0x0008 +#define RESET_SIG 'R' +#define CMD_RESET_I2C 0x0 +#define CMD_RESET_DEV 0x1 +#define CCGX_RAB_ENTER_FLASHING0x000A +#define FLASH_ENTER_SIG 'P' +#define CCGX_RAB_VALIDATE_FW 0x000B +#define CCGX_RAB_FLASH_ROW_RW 0x000C +#define FLASH_SIG 'F' +#define FLASH_RD_CMD 0x0 +#define FLASH_WR_CMD 0x1 +#define FLASH_FWCT1_WR_CMD0x2 +#define FLASH_FWCT2_WR_CMD0x3 +#define FLASH_FWCT_SIG_WR_CMD 0x4 +#define CCGX_RAB_READ_ALL_VER 0x0010 +#define CCGX_RAB_READ_FW2_VER 0x0020 +#define CCGX_RAB_UCSI_CONTROL 0x0039 +#define CCGX_RAB_UCSI_CONTROL_STARTBIT(0) +#define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) +#define REG_FLASH_RW_MEM0x0200 +#define DEV_REG_IDXCCGX_RAB_DEVICE_MODE +#define CCGX_RAB_PDPORT_ENABLE 0x002C +#define PDPORT_1 BIT(0) +#define PDPORT_2 BIT(1) +#define CCGX_RAB_RESPONSE 0x007E +#define ASYNC_EVENT BIT(7) + +/* CCGx events & async msg codes */ +#define RESET_COMPLETE 0x80 +#define EVENT_INDEXRESET_COMPLETE +#define PORT_CONNECT_DET 0x84 +#define PORT_DISCONNECT_DET0x85 +#define ROLE_SWAP_COMPELETE0x87 + +/* ccg firmware */ +#define CYACD_LINE_SIZE 527 +#define CCG4_ROW_SIZE 256 +#define FW1_METADATA_ROW0x1FF +#define FW2_METADATA_ROW0x1FE +#define FW_CFG_TABLE_SIG_SIZE 256 + +static int secondary_fw_min_ver = 41; + +enum enum_flash_mode { + SECONDARY_BL, /* update secondary using bootloader */ + PRIMARY,/* update primary using secondary */ + SECONDARY, /* update secondary using primary */ + FLASH_NOT_NEEDED, /* update not required */ + FLASH_INVALID, +}; + +static const char * const ccg_fw_names[] = { + "ccg_boot.cyacd", + "ccg_primary.cyacd", + "ccg_secondary.cyacd" +}; + struct ccg_dev_info { #define CCG_DEVINFO_FWMODE_SHIFT (0) #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) @@ -50,6 +118,50 @@ struct version_info { struct version_format app; }; +struct fw_config_table { + u32 identity; + u16 table_size; + u8 fwct_version; + u8 is_key_change; + u8 guid[16]; + struct version_format base; + struct version_for
RE: [PATCH v2 6/7] usb: typec: displayport: Export probe and remove functions
Hi Heikki, > -Original Message- > From: linux-usb-ow...@vger.kernel.org On > Behalf Of Heikki Krogerus > Sent: Monday, April 15, 2019 5:10 AM > To: Greg Kroah-Hartman > Cc: Ajay Gupta ; linux-usb@vger.kernel.org > Subject: [PATCH v2 6/7] usb: typec: displayport: Export probe and remove > functions > > From: Ajay Gupta > > VirtualLink standard extends the DisplayPort Alt Mode by utilizing also the > USB 2 > pins on the USB Type-C connector. > It uses the same messages as DisplayPort, but not the DP SVID. At the time of > writing, USB IF has not assigned a Standard ID (SID) for VirtualLink, so the > manufacturers of VirtualLink adapters use their Vendor IDs as the SVID. > > Since the SVID specific communication is exactly the same as with DisplayPort > alternate mode, there is no need to implement separate driver for VirtualLink. > We'll handle the current VirtualLink adapters with probe drivers, and once > there > is SVID assigned for it, we add it to the displayport alt mode driver. > > To support probing drivers, exporting the probe and remove functions, and also > changing the DP_HEADER helper macro to use the SVID of the alternate mode > device instead of the DisplayPort alt mode SVID. > > Suggested-by: Heikki Krogerus > Signed-off-by: Ajay Gupta > Signed-off-by: Heikki Krogerus > --- > drivers/usb/typec/altmodes/displayport.c | 12 +++- > 1 file changed, 7 insertions(+), 5 deletions(-) Looks like you missed adding displayport.h file. It is available in original patch below, https://marc.info/?l=linux-usb&m=155492587224379&w=2 Thanks > nvpublic > diff --git a/drivers/usb/typec/altmodes/displayport.c > b/drivers/usb/typec/altmodes/displayport.c > index 1b2afeb1eeb6..4092248a5936 100644 > --- a/drivers/usb/typec/altmodes/displayport.c > +++ b/drivers/usb/typec/altmodes/displayport.c > @@ -14,7 +14,7 @@ > #include > #include > > -#define DP_HEADER(cmd) (VDO(USB_TYPEC_DP_SID, 1, > cmd) | \ > +#define DP_HEADER(_dp, cmd) (VDO((_dp)->alt->svid, 1, cmd) | \ >VDO_OPOS(USB_TYPEC_DP_MODE)) > > enum { > @@ -155,7 +155,7 @@ static int dp_altmode_configured(struct dp_altmode > *dp) > > static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) { > - u32 header = DP_HEADER(DP_CMD_CONFIGURE); > + u32 header = DP_HEADER(dp, DP_CMD_CONFIGURE); > int ret; > > ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data); > @@ -193,7 +193,7 @@ static void dp_altmode_work(struct work_struct *work) > dev_err(&dp->alt->dev, "failed to enter mode\n"); > break; > case DP_STATE_UPDATE: > - header = DP_HEADER(DP_CMD_STATUS_UPDATE); > + header = DP_HEADER(dp, DP_CMD_STATUS_UPDATE); > vdo = 1; > ret = typec_altmode_vdm(dp->alt, header, &vdo, 2); > if (ret) > @@ -507,7 +507,7 @@ static const struct attribute_group dp_altmode_group = > { > .attrs = dp_altmode_attrs, > }; > > -static int dp_altmode_probe(struct typec_altmode *alt) > +int dp_altmode_probe(struct typec_altmode *alt) > { > const struct typec_altmode *port = typec_altmode_get_partner(alt); > struct dp_altmode *dp; > @@ -545,14 +545,16 @@ static int dp_altmode_probe(struct typec_altmode > *alt) > > return 0; > } > +EXPORT_SYMBOL_GPL(dp_altmode_probe); > > -static void dp_altmode_remove(struct typec_altmode *alt) > +void dp_altmode_remove(struct typec_altmode *alt) > { > struct dp_altmode *dp = typec_altmode_get_drvdata(alt); > > sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group); > cancel_work_sync(&dp->work); > } > +EXPORT_SYMBOL_GPL(dp_altmode_remove); > > static const struct typec_device_id dp_typec_id[] = { > { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE }, > -- > 2.20.1
[PATCH v8 0/3] Add support for firmware update on Cypres CCGx
Hi Heikki These changes add support for updating firmware on Cypress CCGx controller. New version (v8) fixes comments from Greg at [1]. I have tested them on NVIDIA GPU card. Firmware binary is already merged. Details are at [2]. Please help review this set. Thanks Ajay [1] https://marc.info/?l=linux-usb&m=15554931927&w=2 [2] https://marc.info/?l=linux-usb&m=155006182508289&w=2 Ajay Gupta (2): usb: typec: ucsi: ccg: add get_fw_info function usb: typec: ucsi: ccg: add firmware flashing support Heikki Krogerus (1): i2c: nvidia-gpu: Supply CCGx driver the fw build info drivers/i2c/busses/i2c-nvidia-gpu.c | 7 + drivers/usb/typec/ucsi/ucsi_ccg.c | 885 +++- 2 files changed, 882 insertions(+), 10 deletions(-) -- 2.17.1
[PATCH v8 1/3] usb: typec: ucsi: ccg: add get_fw_info function
From: Ajay Gupta Function is to get the details of ccg firmware and device version. It will be useful in debugging and also during firmware update. Signed-off-by: Ajay Gupta Signed-off-by: Heikki Krogerus --- Changes from v7 -> v8 - None drivers/usb/typec/ucsi/ucsi_ccg.c | 66 ++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index de8a43bdff68..3884fb41c72e 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -17,15 +17,54 @@ #include #include "ucsi.h" +enum enum_fw_mode { + BOOT, /* bootloader */ + FW1,/* FW partition-1 (contains secondary fw) */ + FW2,/* FW partition-2 (contains primary fw) */ + FW_INVALID, +}; + +struct ccg_dev_info { +#define CCG_DEVINFO_FWMODE_SHIFT (0) +#define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) +#define CCG_DEVINFO_PDPORTS_SHIFT (2) +#define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT) + u8 mode; + u8 bl_mode; + __le16 silicon_id; + __le16 bl_last_row; +} __packed; + +struct version_format { + __le16 build; + u8 patch; + u8 ver; +#define CCG_VERSION_MIN_SHIFT (0) +#define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) +#define CCG_VERSION_MAJ_SHIFT (4) +#define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) +} __packed; + +struct version_info { + struct version_format base; + struct version_format app; +}; + struct ucsi_ccg { struct device *dev; struct ucsi *ucsi; struct ucsi_ppm ppm; struct i2c_client *client; + struct ccg_dev_info info; + /* version info for boot, primary and secondary */ + struct version_info version[FW2 + 1]; }; -#define CCGX_RAB_INTR_REG 0x06 -#define CCGX_RAB_UCSI_CONTROL 0x39 +#define CCGX_RAB_DEVICE_MODE 0x +#define CCGX_RAB_INTR_REG 0x0006 +#define CCGX_RAB_READ_ALL_VER 0x0010 +#define CCGX_RAB_READ_FW2_VER 0x0020 +#define CCGX_RAB_UCSI_CONTROL 0x0039 #define CCGX_RAB_UCSI_CONTROL_STARTBIT(0) #define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) #define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) @@ -220,6 +259,23 @@ static irqreturn_t ccg_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static int get_fw_info(struct ucsi_ccg *uc) +{ + int err; + + err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version), + sizeof(uc->version)); + if (err < 0) + return err; + + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), + sizeof(uc->info)); + if (err < 0) + return err; + + return 0; +} + static int ucsi_ccg_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -248,6 +304,12 @@ static int ucsi_ccg_probe(struct i2c_client *client, return status; } + status = get_fw_info(uc); + if (status < 0) { + dev_err(uc->dev, "get_fw_info failed - %d\n", status); + return status; + } + status = devm_request_threaded_irq(dev, client->irq, NULL, ccg_irq_handler, IRQF_ONESHOT | IRQF_TRIGGER_HIGH, -- 2.17.1
[PATCH v8 2/3] i2c: nvidia-gpu: Supply CCGx driver the fw build info
From: Ajay Gupta Adding device property "ccgx,firmware-build" for the CCGx device, so the CCGx driver knows which firmware binary to use for a specific vendor. Suggested-by: Heikki Krogerus Signed-off-by: Ajay Gupta Signed-off-by: Heikki Krogerus --- Changes from v7->v8 - None drivers/i2c/busses/i2c-nvidia-gpu.c | 7 +++ 1 file changed, 7 insertions(+) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 4e67d5ed480e..1c8f708f212b 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -253,6 +253,12 @@ static const struct pci_device_id gpu_i2c_ids[] = { }; MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); +static const struct property_entry ccgx_props[] = { + /* Use FW built for NVIDIA (nv) only */ + PROPERTY_ENTRY_U16("ccgx,firmware-build", ('n' << 8) | 'v'), + { } +}; + static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) { struct i2c_client *ccgx_client; @@ -267,6 +273,7 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) sizeof(i2cd->gpu_ccgx_ucsi->type)); i2cd->gpu_ccgx_ucsi->addr = 0x8; i2cd->gpu_ccgx_ucsi->irq = irq; + i2cd->gpu_ccgx_ucsi->properties = ccgx_props; ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); if (!ccgx_client) return -ENODEV; -- 2.17.1
[PATCH v8 3/3] usb: typec: ucsi: ccg: add firmware flashing support
From: Ajay Gupta CCGx has two copies of the firmware in addition to the bootloader. If the device is running FW1, FW2 can be updated with the new version. Dual firmware mode allows the CCG device to stay in a PD contract and support USB PD and Type-C functionality while a firmware update is in progress. First we read the currently flashed firmware version of both primary and secondary firmware and then compare it with version of firmware file to determine if flashing is required. Command framework is added to support sending commands to CCGx controller. We wait for response after sending the command and then read the response from RAB_RESPONSE register. Below commands are supported, - ENTER_FLASHING - RESET - PDPORT_ENABLE - JUMP_TO_BOOT - FLASH_ROW_RW - VALIDATE_FW Command specific mutex lock is also added to sync between driver and user threads. PD port number information is added which is required while sending PD_PORT_ENABLE command Signed-off-by: Ajay Gupta Signed-off-by: Heikki Krogerus --- Changes from v7->v8 - Fixed comments from Greg by dropping do_flash_show() drivers/usb/typec/ucsi/ucsi_ccg.c | 829 +- 1 file changed, 816 insertions(+), 13 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 3884fb41c72e..22ae0501eeb5 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -9,6 +9,7 @@ */ #include #include +#include #include #include #include @@ -24,6 +25,73 @@ enum enum_fw_mode { FW_INVALID, }; +#define CCGX_RAB_DEVICE_MODE 0x +#define CCGX_RAB_INTR_REG 0x0006 +#define DEV_INT BIT(0) +#define PORT0_INT BIT(1) +#define PORT1_INT BIT(2) +#define UCSI_READ_INT BIT(7) +#define CCGX_RAB_JUMP_TO_BOOT 0x0007 +#define TO_BOOT 'J' +#define TO_ALT_FW 'A' +#define CCGX_RAB_RESET_REQ 0x0008 +#define RESET_SIG 'R' +#define CMD_RESET_I2C 0x0 +#define CMD_RESET_DEV 0x1 +#define CCGX_RAB_ENTER_FLASHING0x000A +#define FLASH_ENTER_SIG 'P' +#define CCGX_RAB_VALIDATE_FW 0x000B +#define CCGX_RAB_FLASH_ROW_RW 0x000C +#define FLASH_SIG 'F' +#define FLASH_RD_CMD 0x0 +#define FLASH_WR_CMD 0x1 +#define FLASH_FWCT1_WR_CMD0x2 +#define FLASH_FWCT2_WR_CMD0x3 +#define FLASH_FWCT_SIG_WR_CMD 0x4 +#define CCGX_RAB_READ_ALL_VER 0x0010 +#define CCGX_RAB_READ_FW2_VER 0x0020 +#define CCGX_RAB_UCSI_CONTROL 0x0039 +#define CCGX_RAB_UCSI_CONTROL_STARTBIT(0) +#define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) +#define REG_FLASH_RW_MEM0x0200 +#define DEV_REG_IDXCCGX_RAB_DEVICE_MODE +#define CCGX_RAB_PDPORT_ENABLE 0x002C +#define PDPORT_1 BIT(0) +#define PDPORT_2 BIT(1) +#define CCGX_RAB_RESPONSE 0x007E +#define ASYNC_EVENT BIT(7) + +/* CCGx events & async msg codes */ +#define RESET_COMPLETE 0x80 +#define EVENT_INDEXRESET_COMPLETE +#define PORT_CONNECT_DET 0x84 +#define PORT_DISCONNECT_DET0x85 +#define ROLE_SWAP_COMPELETE0x87 + +/* ccg firmware */ +#define CYACD_LINE_SIZE 527 +#define CCG4_ROW_SIZE 256 +#define FW1_METADATA_ROW0x1FF +#define FW2_METADATA_ROW0x1FE +#define FW_CFG_TABLE_SIG_SIZE 256 + +static int secondary_fw_min_ver = 41; + +enum enum_flash_mode { + SECONDARY_BL, /* update secondary using bootloader */ + PRIMARY,/* update primary using secondary */ + SECONDARY, /* update secondary using primary */ + FLASH_NOT_NEEDED, /* update not required */ + FLASH_INVALID, +}; + +static const char * const ccg_fw_names[] = { + "ccg_boot.cyacd", + "ccg_primary.cyacd", + "ccg_secondary.cyacd" +}; + struct ccg_dev_info { #define CCG_DEVINFO_FWMODE_SHIFT (0) #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) @@ -50,6 +118,50 @@ struct version_info { struct version_format app; }; +struct fw_config_table { + u32 identity; + u16 table_size; + u8 fwct_version; + u8 is_key_change; + u8 guid[16]; + struct version_format base; + st
RE: [PATCH -next] usb: typec: ucsi: ccg: fix missing unlock on error in ccg_cmd_write_flash_row()
Hi Wei > -Original Message- > From: Wei Yongjun > Sent: Monday, April 29, 2019 5:27 AM > To: Heikki Krogerus ; Greg Kroah-Hartman > ; Ajay Gupta ; Wolfram Sang > > Cc: Wei Yongjun ; linux-usb@vger.kernel.org; > kernel-janit...@vger.kernel.org > Subject: [PATCH -next] usb: typec: ucsi: ccg: fix missing unlock on error in > ccg_cmd_write_flash_row() > > Add the missing unlock before return from function ccg_cmd_write_flash_row() > in the error handling case. Thanks for fixing this. The change looks good. > nvpublic > > Fixes: 5c9ae5a87573 ("usb: typec: ucsi: ccg: add firmware flashing support") > Signed-off-by: Wei Yongjun > --- > drivers/usb/typec/ucsi/ucsi_ccg.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c > b/drivers/usb/typec/ucsi/ucsi_ccg.c > index 4632b91a04a6..9d46aa9e4e35 100644 > --- a/drivers/usb/typec/ucsi/ucsi_ccg.c > +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c > @@ -631,6 +631,7 @@ ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row, > ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2); > if (ret != CCG4_ROW_SIZE + 2) { > dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret); > + mutex_unlock(&uc->lock); > return ret < 0 ? ret : -EIO; > } > >
[PATCH 2/4] usb: typec: ucsi: ccg: enable runtime pm support
From: Ajay Gupta The change enables runtime pm support to UCSI CCG driver. ucsi_send_command() is used in resume path and so exported ucsi_send_command() symbol in ucsi.c for modular build. Signed-off-by: Ajay Gupta --- drivers/usb/typec/ucsi/ucsi.c | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 60 +++ 2 files changed, 61 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 7850b851cecd..e9454134d399 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -206,6 +206,7 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, return ret; } +EXPORT_SYMBOL_GPL(ucsi_send_command); /* -- */ diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 9d46aa9e4e35..cc7094ecda2d 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include "ucsi.h" @@ -210,6 +212,7 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; rlen = min_t(u16, rem_len, max_read_len); @@ -218,12 +221,14 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); return status; } rab += rlen; rem_len -= rlen; } + pm_runtime_put_sync(uc->dev); return 0; } @@ -249,13 +254,16 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); kfree(buf); return status; } + pm_runtime_put_sync(uc->dev); kfree(buf); return 0; } @@ -1134,6 +1142,10 @@ static int ucsi_ccg_probe(struct i2c_client *client, if (status) dev_err(uc->dev, "cannot create sysfs group: %d\n", status); + pm_runtime_set_active(uc->dev); + pm_runtime_enable(uc->dev); + pm_runtime_idle(uc->dev); + return 0; } @@ -1143,6 +1155,7 @@ static int ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->work); ucsi_unregister_ppm(uc->ucsi); + pm_runtime_disable(uc->dev); free_irq(uc->irq, uc); sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group); @@ -1155,9 +1168,56 @@ static const struct i2c_device_id ucsi_ccg_device_id[] = { }; MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); +static int ucsi_ccg_suspend(struct device *dev) +{ + return 0; +} + +static int ucsi_ccg_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ucsi_ccg *uc = i2c_get_clientdata(client); + struct ucsi *ucsi = uc->ucsi; + struct ucsi_control c; + int ret; + + /* restore UCSI notification enable mask */ + UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL); + ret = ucsi_send_command(ucsi, &c, NULL, 0); + if (ret < 0) { + dev_err(uc->dev, "%s: failed to set notification enable - %d\n", + __func__, ret); + } + return 0; +} + +static int ucsi_ccg_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int ucsi_ccg_runtime_resume(struct device *dev) +{ + return 0; +} + +static int ucsi_ccg_runtime_idle(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops ucsi_ccg_pm = { + .suspend = ucsi_ccg_suspend, + .resume = ucsi_ccg_resume, + .runtime_suspend = ucsi_ccg_runtime_suspend, + .runtime_resume = ucsi_ccg_runtime_resume, + .runtime_idle = ucsi_ccg_runtime_idle, +}; + static struct i2c_driver ucsi_ccg_driver = { .driver = { .name = "ucsi_ccg", + .pm = &ucsi_ccg_pm, }, .probe = ucsi_ccg_probe, .remove = ucsi_ccg_remove, -- 2.17.1
[PATCH 1/4] i2c: nvidia-gpu: add runtime pm support
From: Ajay Gupta Enable runtime pm support with autosuspend delay of three second. This is to make sure I2C client device Cypress CCGx has completed all transaction. Signed-off-by: Ajay Gupta --- drivers/i2c/busses/i2c-nvidia-gpu.c | 24 +--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 1c8f708f212b..9d347583f8dc 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -175,6 +175,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, * The controller supports maximum 4 byte read due to known * limitation of sending STOP after every read. */ + pm_runtime_get_sync(i2cd->dev); for (i = 0; i < num; i++) { if (msgs[i].flags & I2C_M_RD) { /* program client address before starting read */ @@ -189,7 +190,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, status = gpu_i2c_start(i2cd); if (status < 0) { if (i == 0) - return status; + goto exit; goto stop; } @@ -206,13 +207,18 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, } status = gpu_i2c_stop(i2cd); if (status < 0) - return status; + goto exit; + pm_runtime_mark_last_busy(i2cd->dev); + pm_runtime_put_autosuspend(i2cd->dev); return i; stop: status2 = gpu_i2c_stop(i2cd); if (status2 < 0) dev_err(i2cd->dev, "i2c stop failed %d\n", status2); +exit: + pm_runtime_mark_last_busy(i2cd->dev); + pm_runtime_put_autosuspend(i2cd->dev); return status; } @@ -332,6 +338,11 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto del_adapter; } + pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_allow(&pdev->dev); + return 0; del_adapter: @@ -345,10 +356,16 @@ static void gpu_i2c_remove(struct pci_dev *pdev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); + pm_runtime_get_noresume(i2cd->dev); i2c_del_adapter(&i2cd->adapter); pci_free_irq_vectors(pdev); } +static int gpu_i2c_suspend(struct device *dev) +{ + return 0; +} + static __maybe_unused int gpu_i2c_resume(struct device *dev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); @@ -357,7 +374,8 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) return 0; } -static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, NULL, gpu_i2c_resume, NULL); +static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, gpu_i2c_suspend, gpu_i2c_resume, + NULL); static struct pci_driver gpu_i2c_driver = { .name = "nvidia-gpu", -- 2.17.1
[PATCH 4/4] usb: typec: ucsi: ccg: add runtime pm workaround
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware verson 3.1.10 (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. Many of these cards may get latest kernel but may not get latest fixed firmware so a workaround is required to check for any connector change event. The workaround is that i2c bus driver will call pm_request_resume() to runtime resume ucsi_ccg driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta --- drivers/usb/typec/ucsi/ucsi_ccg.c | 78 +-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index cc7094ecda2d..3457e112fdbc 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -109,6 +109,8 @@ struct version_format { __le16 build; u8 patch; u8 ver; +#define CCG_VERSION_PATCH(x) ((x) << 16) +#define CCG_VERSION(x) ((x) << 24) #define CCG_VERSION_MIN_SHIFT (0) #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) #define CCG_VERSION_MAJ_SHIFT (4) @@ -172,6 +174,7 @@ struct ucsi_ccg { struct ccg_dev_info info; /* version info for boot, primary and secondary */ struct version_info version[FW2 + 1]; + u32 fw_version; /* CCG HPI communication flags */ unsigned long flags; #define RESET_PENDING 0 @@ -185,6 +188,10 @@ struct ucsi_ccg { /* fw build with vendor information */ u16 fw_build; + bool run_isr; /* flag to call ISR routine during resume */ + struct work_struct pm_work; + u16 old_fw_build; + u32 old_fw_version; }; static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) @@ -212,6 +219,17 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + if (uc->old_fw_build == uc->fw_build && + uc->fw_version <= uc->old_fw_version) { + mutex_lock(&uc->lock); + /* do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; @@ -254,6 +272,17 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + if (uc->old_fw_build == uc->fw_build && + uc->fw_version <= uc->old_fw_version) { + mutex_lock(&uc->lock); + /* do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { @@ -383,6 +412,13 @@ static irqreturn_t ccg_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static void ccg_pm_workaround_work(struct work_struct *pm_work) +{ + struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work); + + ucsi_notify(uc->ucsi); +} + static int get_fw_info(struct ucsi_ccg *uc) { int err; @@ -392,6 +428,9 @@ static int get_fw_info(struct ucsi_ccg *uc) if (err < 0) return err; + uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) | + CCG_VERSION_PATCH(uc->version[FW2].app.patch); + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), sizeof(uc->info)); if (err < 0) @@ -740,11 +779,12 @@ static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name, } /* compare input version with FWCT version */ - cur_version = le16_to_cpu(app->build) | app->patch << 16 | - app->ver << 24; + cur_version = le16_to_cpu(app->build) | CCG_VERSION_PATCH(app->patch) | + CCG_VERSION(app->ver); - new_version = le16_to_cpu(fw_cfg.app.build) | fw_cfg.app.patch << 16 | - fw_cfg.app.ver << 24; + new_version = le16_to_cpu(fw_cfg.app.build) | + CCG_VERSION_PATCH(fw_cfg.app.patch) | +
[PATCH 0/4] usb: typec: ucsi: ccg: add runtime pm support
Hi Heikki These patches add support for runtime power management for UCSI CCGx driver. I have tested them with NVIDIA GPU card which has i2c interface to talk to CCG controller. I have added runtime pm support for the i2c bus driver as well. First two patches add support for runtime pm in i2c bus driver and UCSI CCGx driver. Last two patches add workaround for an old version of ccg firmware which has known issue of not missing interrupt when a device is connected to runtime resume the ccg controller. The workaround is needed because if a GPU card doesn't get new firmware but gets new kernel then also it should continue to work. Thanks Ajay Ajay Gupta (4): i2c: nvidia-gpu: add runtime pm support usb: typec: ucsi: ccg: enable runtime pm support i2c: nvidia-gpu: resume ccgx i2c client usb: typec: ucsi: ccg: add runtime pm workaround drivers/i2c/busses/i2c-nvidia-gpu.c | 37 ++-- drivers/usb/typec/ucsi/ucsi.c | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 138 +++- 3 files changed, 165 insertions(+), 11 deletions(-) -- 2.17.1
[PATCH 3/4] i2c: nvidia-gpu: resume ccgx i2c client
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. Many of these cards may get latest kernel but may not get latest fixed firmware so a workaround to check for any connector change event. The workaround is to request runtime resume of i2c client which is UCSI Cypress CCGx driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta --- drivers/i2c/busses/i2c-nvidia-gpu.c | 13 + 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 9d347583f8dc..2f72135a547b 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -51,6 +51,7 @@ struct gpu_i2c_dev { void __iomem *regs; struct i2c_adapter adapter; struct i2c_board_info *gpu_ccgx_ucsi; + struct i2c_client *ccgx_client; }; static void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd) @@ -267,8 +268,6 @@ static const struct property_entry ccgx_props[] = { static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) { - struct i2c_client *ccgx_client; - i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev, sizeof(*i2cd->gpu_ccgx_ucsi), GFP_KERNEL); @@ -280,8 +279,8 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) i2cd->gpu_ccgx_ucsi->addr = 0x8; i2cd->gpu_ccgx_ucsi->irq = irq; i2cd->gpu_ccgx_ucsi->properties = ccgx_props; - ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); - if (!ccgx_client) + i2cd->ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); + if (!i2cd->ccgx_client) return -ENODEV; return 0; @@ -371,6 +370,12 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); gpu_enable_i2c_bus(i2cd); + /* runtime resume ccgx client so that it can see for any +* connector change event. Old ccg firmware has known +* issue of not triggering interrupt when a device is +* connected to runtime resume the controller. +*/ + pm_request_resume(&i2cd->ccgx_client->dev); return 0; } -- 2.17.1
RE: [PATCH 1/4] i2c: nvidia-gpu: add runtime pm support
Hi Wolfram, > -Original Message- > From: Wolfram Sang > Sent: Sunday, May 19, 2019 7:47 AM > To: Ajay Gupta > Cc: heikki.kroge...@linux.intel.com; linux-usb@vger.kernel.org; linux- > i...@vger.kernel.org; Ajay Gupta > Subject: Re: [PATCH 1/4] i2c: nvidia-gpu: add runtime pm support > > > diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c > > b/drivers/i2c/busses/i2c-nvidia-gpu.c > > index 1c8f708f212b..9d347583f8dc 100644 > > --- a/drivers/i2c/busses/i2c-nvidia-gpu.c > > +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c > > @@ -175,6 +175,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter > *adap, > > * The controller supports maximum 4 byte read due to known > > * limitation of sending STOP after every read. > > */ > > + pm_runtime_get_sync(i2cd->dev); > > for (i = 0; i < num; i++) { > > if (msgs[i].flags & I2C_M_RD) { > > /* program client address before starting read */ @@ - > 189,7 +190,7 > > @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, > > status = gpu_i2c_start(i2cd); > > if (status < 0) { > > if (i == 0) > > - return status; > > + goto exit; > > goto stop; > > Hmm, goto here, goto there... OK, the code didn't have a good flow even before > this patch. [...] > > > } > > > > @@ -206,13 +207,18 @@ static int gpu_i2c_master_xfer(struct i2c_adapter > *adap, > > } > > status = gpu_i2c_stop(i2cd); > > if (status < 0) > > - return status; > > + goto exit; > > > > + pm_runtime_mark_last_busy(i2cd->dev); > > + pm_runtime_put_autosuspend(i2cd->dev); > > return i; > > stop: > > status2 = gpu_i2c_stop(i2cd); > > if (status2 < 0) > > dev_err(i2cd->dev, "i2c stop failed %d\n", status2); > > +exit: > > + pm_runtime_mark_last_busy(i2cd->dev); > > + pm_runtime_put_autosuspend(i2cd->dev); > > return status; > > } > > I am not nacking this, yet the use of goto here seems too much for my taste. > If > you could try refactoring the whole code (dunno, maybe using a flag when to > use stop?), I'd appreciate this. Ok, I will add a local variable to make it more readable. Thanks Ajay > nvpublic
RE: [PATCH 3/4] i2c: nvidia-gpu: resume ccgx i2c client
Hi Wolfram, > -Original Message- > From: Wolfram Sang > Sent: Sunday, May 19, 2019 7:49 AM > To: Ajay Gupta > Cc: heikki.kroge...@linux.intel.com; linux-usb@vger.kernel.org; linux- > i...@vger.kernel.org; Ajay Gupta > Subject: Re: [PATCH 3/4] i2c: nvidia-gpu: resume ccgx i2c client > > > > + /* runtime resume ccgx client so that it can see for any > > +* connector change event. Old ccg firmware has known > > +* issue of not triggering interrupt when a device is > > +* connected to runtime resume the controller. > > +*/ > > Check coding style for comments. Sure, will fix. > > BTW how do you suggest this gets upstream? Through the usb-tree? Would be > fine with me. Are there any dependencies? The cover-letter doesn't mention it. I would prefer it to go through usb-tree since usb ucsi_ccg driver is the main driver getting runtime pm functionality with the series. I will add this information in cover letter too. Thanks Ajay >nvpublic
[PATCH v2 4/5] i2c: nvidia-gpu: resume ccgx i2c client
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware version 3.1.10 (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. If any GPU card gets latest kernel with runtime pm support but does not get latest fixed firmware then also it should continue to work and therefore a workaround is required to check for any connector change event The workaround is to request runtime resume of i2c client which is UCSI Cypress CCGx driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta --- Changes from v1->v2: Fixed comment from Wolfram by fixing comment style. drivers/i2c/busses/i2c-nvidia-gpu.c | 14 ++ 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index af445590041b..6826740fd0f5 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -51,6 +51,7 @@ struct gpu_i2c_dev { void __iomem *regs; struct i2c_adapter adapter; struct i2c_board_info *gpu_ccgx_ucsi; + struct i2c_client *ccgx_client; }; static void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd) @@ -271,8 +272,6 @@ static const struct property_entry ccgx_props[] = { static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) { - struct i2c_client *ccgx_client; - i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev, sizeof(*i2cd->gpu_ccgx_ucsi), GFP_KERNEL); @@ -284,8 +283,8 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) i2cd->gpu_ccgx_ucsi->addr = 0x8; i2cd->gpu_ccgx_ucsi->irq = irq; i2cd->gpu_ccgx_ucsi->properties = ccgx_props; - ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); - if (!ccgx_client) + i2cd->ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); + if (!i2cd->ccgx_client) return -ENODEV; return 0; @@ -375,6 +374,13 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); gpu_enable_i2c_bus(i2cd); + /* +* Runtime resume ccgx client so that it can see for any +* connector change event. Old ccg firmware has known +* issue of not triggering interrupt when a device is +* connected to runtime resume the controller. +*/ + pm_request_resume(&i2cd->ccgx_client->dev); return 0; } -- 2.17.1
[PATCH v2 2/5] i2c: nvidia-gpu: add runtime pm support
From: Ajay Gupta Enable runtime pm support with autosuspend delay of three second. This is to make sure I2C client device Cypress CCGx has completed all transaction. Signed-off-by: Ajay Gupta --- Changes from v1->v2: None drivers/i2c/busses/i2c-nvidia-gpu.c | 19 ++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 2d9561ec2320..af445590041b 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -176,6 +176,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, * The controller supports maximum 4 byte read due to known * limitation of sending STOP after every read. */ + pm_runtime_get_sync(i2cd->dev); for (i = 0; i < num; i++) { if (msgs[i].flags & I2C_M_RD) { /* program client address before starting read */ @@ -211,6 +212,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, goto exit; } + pm_runtime_mark_last_busy(i2cd->dev); + pm_runtime_put_autosuspend(i2cd->dev); return i; exit: if (send_stop) { @@ -218,6 +221,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, if (status2 < 0) dev_err(i2cd->dev, "i2c stop failed %d\n", status2); } + pm_runtime_mark_last_busy(i2cd->dev); + pm_runtime_put_autosuspend(i2cd->dev); return status; } @@ -337,6 +342,11 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto del_adapter; } + pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_allow(&pdev->dev); + return 0; del_adapter: @@ -350,10 +360,16 @@ static void gpu_i2c_remove(struct pci_dev *pdev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); + pm_runtime_get_noresume(i2cd->dev); i2c_del_adapter(&i2cd->adapter); pci_free_irq_vectors(pdev); } +static int gpu_i2c_suspend(struct device *dev) +{ + return 0; +} + static __maybe_unused int gpu_i2c_resume(struct device *dev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); @@ -362,7 +378,8 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) return 0; } -static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, NULL, gpu_i2c_resume, NULL); +static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, gpu_i2c_suspend, gpu_i2c_resume, + NULL); static struct pci_driver gpu_i2c_driver = { .name = "nvidia-gpu", -- 2.17.1
[PATCH v2 0/5] usb: typec: ucsi: ccg: add runtime pm support
Hi Heikki and Wolfram These patches add support for runtime power management for UCSI CCGx driver. I have tested them with NVIDIA GPU card which has i2c interface to talk to CCG controller. I have added runtime pm support for the i2c bus driver as well. First patch refactors master_xfer() of i2c driver fixing comment from Wolfram. Second and third patch add support for runtime pm in i2c bus driver and UCSI CCGx driver. Last two patches add workaround for an old version of ccg firmware which has known issue of missing interrupt when a device is connected to runtime resume the ccg controller. The workaround is needed because if a GPU card doesn't get new firmware but gets new kernel then also it should continue to work. The workaround is to request runtime resume of i2c client which is UCSI Cypress CCGx driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. All the five patches should go together since ucsi_ccg driver is dependent on i2c driver for runtime pm wokaround. I would prefer it to go through usb-tree since usb ucsi_ccg driver is the main driver getting runtime pm functionality with the series. Thanks Ajay Ajay Gupta (5): i2c: nvidia-gpu: refactor master_xfer i2c: nvidia-gpu: add runtime pm support usb: typec: ucsi: ccg: enable runtime pm support i2c: nvidia-gpu: resume ccgx i2c client usb: typec: ucsi: ccg: add runtime pm workaround drivers/i2c/busses/i2c-nvidia-gpu.c | 60 drivers/usb/typec/ucsi/ucsi.c | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 140 +++- 3 files changed, 181 insertions(+), 20 deletions(-) -- 2.17.1
[PATCH v2 5/5] usb: typec: ucsi: ccg: add runtime pm workaround
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware version 3.1.10 (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. If any GPU card gets latest kernel with runtime pm support but does not get latest fixed firmware then also it should continue to work and therefore a workaround is required to check for any connector change event. The workaround is that i2c bus driver will call pm_request_resume() to runtime resume ucsi_ccg driver. CCG driver will call the ISR for any connector change event for NVIDIA GPU card and only if it has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta --- Changes from v1->v2: - Fixed comment style - Added new macros for NVIDIA old FW builds instead of structure variable for them drivers/usb/typec/ucsi/ucsi_ccg.c | 80 +-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index cc7094ecda2d..0b9f60861497 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -109,12 +109,21 @@ struct version_format { __le16 build; u8 patch; u8 ver; +#define CCG_VERSION_PATCH(x) ((x) << 16) +#define CCG_VERSION(x) ((x) << 24) #define CCG_VERSION_MIN_SHIFT (0) #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) #define CCG_VERSION_MAJ_SHIFT (4) #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) } __packed; +/* + * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue + * of missing interrupt when a device is connected for runtime resume + */ +#define CCG_FW_BUILD_NVIDIA(('n' << 8) | 'v') +#define CCG_OLD_FW_VERSION (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10)) + struct version_info { struct version_format base; struct version_format app; @@ -172,6 +181,7 @@ struct ucsi_ccg { struct ccg_dev_info info; /* version info for boot, primary and secondary */ struct version_info version[FW2 + 1]; + u32 fw_version; /* CCG HPI communication flags */ unsigned long flags; #define RESET_PENDING 0 @@ -185,6 +195,8 @@ struct ucsi_ccg { /* fw build with vendor information */ u16 fw_build; + bool run_isr; /* flag to call ISR routine during resume */ + struct work_struct pm_work; }; static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) @@ -212,6 +224,18 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + if (uc->fw_build == CCG_FW_BUILD_NVIDIA && + uc->fw_version <= CCG_OLD_FW_VERSION) { + mutex_lock(&uc->lock); + /* +* Do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; @@ -254,6 +278,18 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + if (uc->fw_build == CCG_FW_BUILD_NVIDIA && + uc->fw_version <= CCG_OLD_FW_VERSION) { + mutex_lock(&uc->lock); + /* +* Do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { @@ -383,6 +419,13 @@ static irqreturn_t ccg_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static void ccg_pm_workaround_work(struct work_struct *pm_work) +{ + struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work); + + ucsi_notify(uc->ucsi); +} + static int get_fw_info(struct ucsi_ccg *uc) { int err; @@ -392,6 +435,9 @@ static int get_fw_info(struct ucsi_ccg *uc) if (err < 0) return err; + uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) | + CCG_VERSION_PATCH(uc->version[FW2].app.patch); + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), sizeof(uc->info)); if (err < 0) @
[PATCH v2 1/5] i2c: nvidia-gpu: refactor master_xfer
From: Ajay Gupta Added a local variable "send_stop" to simplify "goto" statements. The "send_stop" handles below two case 1) When first i2c start fails and so i2c stop is not sent before exiting 2) When i2c stop failed after all transfers and we do not need to send another stop before exiting. Signed-off-by: Ajay Gupta --- drivers/i2c/busses/i2c-nvidia-gpu.c | 27 --- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 1c8f708f212b..2d9561ec2320 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -169,6 +169,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, { struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap); int status, status2; + bool send_stop = true; int i, j; /* @@ -182,37 +183,41 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, /* gpu_i2c_read has implicit start */ status = gpu_i2c_read(i2cd, msgs[i].buf, msgs[i].len); if (status < 0) - goto stop; + goto exit; } else { u8 addr = i2c_8bit_addr_from_msg(msgs + i); status = gpu_i2c_start(i2cd); if (status < 0) { if (i == 0) - return status; - goto stop; + send_stop = false; + goto exit; } status = gpu_i2c_write(i2cd, addr); if (status < 0) - goto stop; + goto exit; for (j = 0; j < msgs[i].len; j++) { status = gpu_i2c_write(i2cd, msgs[i].buf[j]); if (status < 0) - goto stop; + goto exit; } } } status = gpu_i2c_stop(i2cd); - if (status < 0) - return status; + if (status < 0) { + send_stop = false; + goto exit; + } return i; -stop: - status2 = gpu_i2c_stop(i2cd); - if (status2 < 0) - dev_err(i2cd->dev, "i2c stop failed %d\n", status2); +exit: + if (send_stop) { + status2 = gpu_i2c_stop(i2cd); + if (status2 < 0) + dev_err(i2cd->dev, "i2c stop failed %d\n", status2); + } return status; } -- 2.17.1
[PATCH v2 3/5] usb: typec: ucsi: ccg: enable runtime pm support
From: Ajay Gupta The change enables runtime pm support to UCSI CCG driver. ucsi_send_command() is used in resume path and so exported ucsi_send_command() symbol in ucsi.c for modular build. Signed-off-by: Ajay Gupta --- Changes from v1->v2 : None drivers/usb/typec/ucsi/ucsi.c | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 60 +++ 2 files changed, 61 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 7850b851cecd..e9454134d399 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -206,6 +206,7 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, return ret; } +EXPORT_SYMBOL_GPL(ucsi_send_command); /* -- */ diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 9d46aa9e4e35..cc7094ecda2d 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include "ucsi.h" @@ -210,6 +212,7 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; rlen = min_t(u16, rem_len, max_read_len); @@ -218,12 +221,14 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); return status; } rab += rlen; rem_len -= rlen; } + pm_runtime_put_sync(uc->dev); return 0; } @@ -249,13 +254,16 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); kfree(buf); return status; } + pm_runtime_put_sync(uc->dev); kfree(buf); return 0; } @@ -1134,6 +1142,10 @@ static int ucsi_ccg_probe(struct i2c_client *client, if (status) dev_err(uc->dev, "cannot create sysfs group: %d\n", status); + pm_runtime_set_active(uc->dev); + pm_runtime_enable(uc->dev); + pm_runtime_idle(uc->dev); + return 0; } @@ -1143,6 +1155,7 @@ static int ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->work); ucsi_unregister_ppm(uc->ucsi); + pm_runtime_disable(uc->dev); free_irq(uc->irq, uc); sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group); @@ -1155,9 +1168,56 @@ static const struct i2c_device_id ucsi_ccg_device_id[] = { }; MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); +static int ucsi_ccg_suspend(struct device *dev) +{ + return 0; +} + +static int ucsi_ccg_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ucsi_ccg *uc = i2c_get_clientdata(client); + struct ucsi *ucsi = uc->ucsi; + struct ucsi_control c; + int ret; + + /* restore UCSI notification enable mask */ + UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL); + ret = ucsi_send_command(ucsi, &c, NULL, 0); + if (ret < 0) { + dev_err(uc->dev, "%s: failed to set notification enable - %d\n", + __func__, ret); + } + return 0; +} + +static int ucsi_ccg_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int ucsi_ccg_runtime_resume(struct device *dev) +{ + return 0; +} + +static int ucsi_ccg_runtime_idle(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops ucsi_ccg_pm = { + .suspend = ucsi_ccg_suspend, + .resume = ucsi_ccg_resume, + .runtime_suspend = ucsi_ccg_runtime_suspend, + .runtime_resume = ucsi_ccg_runtime_resume, + .runtime_idle = ucsi_ccg_runtime_idle, +}; + static struct i2c_driver ucsi_ccg_driver = { .driver = { .name = "ucsi_ccg", + .pm = &ucsi_ccg_pm, }, .probe = ucsi_ccg_probe, .remove = ucsi_ccg_remove, -- 2.17.1
RE: [PATCH v2 3/5] usb: typec: ucsi: ccg: enable runtime pm support
Hi Heikki > > +static int ucsi_ccg_resume(struct device *dev) { > > + struct i2c_client *client = to_i2c_client(dev); > > + struct ucsi_ccg *uc = i2c_get_clientdata(client); > > + struct ucsi *ucsi = uc->ucsi; > > + struct ucsi_control c; > > + int ret; > > + > > + /* restore UCSI notification enable mask */ > > + UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL); > > + ret = ucsi_send_command(ucsi, &c, NULL, 0); > > + if (ret < 0) { > > + dev_err(uc->dev, "%s: failed to set notification enable - %d\n", > > + __func__, ret); > > + } > > + return 0; > > +} > > I would prefer that we did this for all methods in ucsi.c, not just ccgx. > Could you > add resume callback to struct ucsi_ppm, and then call it here. struct ucsi_ppm currently have .sync() and .cmd() callback which is implemented by ucsi_ccg and ucsi_acpi and invoked by usci.c. Is it okay to add a callback in this structure and implement inside ucsi.c and invoke from ucsi_ccg and ucsi_acpi? OR we can just add a function in ucsi.c and export it and use it from ucsi_ccg and ucsi_acpi? > > > +static int ucsi_ccg_runtime_suspend(struct device *dev) { > > + return 0; > > +} > > + > > +static int ucsi_ccg_runtime_resume(struct device *dev) { > > + return 0; > > +} > > + > > +static int ucsi_ccg_runtime_idle(struct device *dev) { > > + return 0; > > +} > > Oh yes, and do you really need to supply all of those stubs? We can drop ucsi_ccg_runtime_idle() but we do need ucsi_ccg_runtime_suspend() and ucsi_ccg_runtime_resume() for runtime pm functionality. Thanks Ajay > nvpublic > > + > > +static const struct dev_pm_ops ucsi_ccg_pm = { > > + .suspend = ucsi_ccg_suspend, > > + .resume = ucsi_ccg_resume, > > + .runtime_suspend = ucsi_ccg_runtime_suspend, > > + .runtime_resume = ucsi_ccg_runtime_resume, > > + .runtime_idle = ucsi_ccg_runtime_idle, }; > > + > > static struct i2c_driver ucsi_ccg_driver = { > > .driver = { > > .name = "ucsi_ccg", > > + .pm = &ucsi_ccg_pm, > > }, > > .probe = ucsi_ccg_probe, > > .remove = ucsi_ccg_remove, > > thanks, > > -- > heikki
[PATCH v3 1/5] i2c: nvidia-gpu: refactor master_xfer
From: Ajay Gupta Added a local variable "send_stop" to simplify "goto" statements. The "send_stop" handles below two case 1) When first i2c start fails and so i2c stop is not sent before exiting 2) When i2c stop failed after all transfers and we do not need to send another stop before exiting. Signed-off-by: Ajay Gupta --- Changes from v2->v3: None drivers/i2c/busses/i2c-nvidia-gpu.c | 27 --- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 1c8f708f212b..2d9561ec2320 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -169,6 +169,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, { struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap); int status, status2; + bool send_stop = true; int i, j; /* @@ -182,37 +183,41 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, /* gpu_i2c_read has implicit start */ status = gpu_i2c_read(i2cd, msgs[i].buf, msgs[i].len); if (status < 0) - goto stop; + goto exit; } else { u8 addr = i2c_8bit_addr_from_msg(msgs + i); status = gpu_i2c_start(i2cd); if (status < 0) { if (i == 0) - return status; - goto stop; + send_stop = false; + goto exit; } status = gpu_i2c_write(i2cd, addr); if (status < 0) - goto stop; + goto exit; for (j = 0; j < msgs[i].len; j++) { status = gpu_i2c_write(i2cd, msgs[i].buf[j]); if (status < 0) - goto stop; + goto exit; } } } status = gpu_i2c_stop(i2cd); - if (status < 0) - return status; + if (status < 0) { + send_stop = false; + goto exit; + } return i; -stop: - status2 = gpu_i2c_stop(i2cd); - if (status2 < 0) - dev_err(i2cd->dev, "i2c stop failed %d\n", status2); +exit: + if (send_stop) { + status2 = gpu_i2c_stop(i2cd); + if (status2 < 0) + dev_err(i2cd->dev, "i2c stop failed %d\n", status2); + } return status; } -- 2.17.1
[PATCH v3 4/5] i2c: nvidia-gpu: resume ccgx i2c client
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware version 3.1.10 (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. If any GPU card gets latest kernel with runtime pm support but does not get latest fixed firmware then also it should continue to work and therefore a workaround is required to check for any connector change event The workaround is to request runtime resume of i2c client which is UCSI Cypress CCGx driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta --- Changes from v2->v3: None drivers/i2c/busses/i2c-nvidia-gpu.c | 14 ++ 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 28fee85135ac..957b39c9760f 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -51,6 +51,7 @@ struct gpu_i2c_dev { void __iomem *regs; struct i2c_adapter adapter; struct i2c_board_info *gpu_ccgx_ucsi; + struct i2c_client *ccgx_client; }; static void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd) @@ -271,8 +272,6 @@ static const struct property_entry ccgx_props[] = { static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) { - struct i2c_client *ccgx_client; - i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev, sizeof(*i2cd->gpu_ccgx_ucsi), GFP_KERNEL); @@ -284,8 +283,8 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) i2cd->gpu_ccgx_ucsi->addr = 0x8; i2cd->gpu_ccgx_ucsi->irq = irq; i2cd->gpu_ccgx_ucsi->properties = ccgx_props; - ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); - if (!ccgx_client) + i2cd->ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); + if (!i2cd->ccgx_client) return -ENODEV; return 0; @@ -375,6 +374,13 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); gpu_enable_i2c_bus(i2cd); + /* +* Runtime resume ccgx client so that it can see for any +* connector change event. Old ccg firmware has known +* issue of not triggering interrupt when a device is +* connected to runtime resume the controller. +*/ + pm_request_resume(&i2cd->ccgx_client->dev); return 0; } -- 2.17.1
[PATCH v3 3/5] usb: typec: ucsi: ccg: enable runtime pm support
From: Ajay Gupta The change enables runtime pm support to UCSI CCG driver. Added ucsi_resume() function to enable notification after system reusme. Exported both ucsi_resume() and ucsi_send_command() symbols in ucsi.c for modular build. Signed-off-by: Ajay Gupta --- Changes from v2->v3 : None - Fixed comments from Heikki by adding ucsi_resume() in ucsi.c and used it from ucsi_ccg.c - Also removed ucsi_ccg_runtime_idle() and ucsi_ccg_suspend() which were stubs and not needed. - We still need stub fucntion ucsi_ccg_runtime_suspend for runtime pm functionality drivers/usb/typec/ucsi/ucsi.c | 10 drivers/usb/typec/ucsi/ucsi.h | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 38 +++ 3 files changed, 49 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 7850b851cecd..ba288b964dc8 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -206,7 +206,17 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, return ret; } +EXPORT_SYMBOL_GPL(ucsi_send_command); +int ucsi_resume(struct ucsi *ucsi) +{ + struct ucsi_control ctrl; + + /* Restore UCSI notification enable mask after system resume */ + UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL); + return ucsi_send_command(ucsi, &ctrl, NULL, 0); +} +EXPORT_SYMBOL_GPL(ucsi_resume); /* -- */ void ucsi_altmode_update_active(struct ucsi_connector *con) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 1e2981aef629..de87d0b8319d 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -430,6 +430,7 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, void *retval, size_t size); void ucsi_altmode_update_active(struct ucsi_connector *con); +int ucsi_resume(struct ucsi *ucsi); #if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE) struct typec_altmode * diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 9d46aa9e4e35..b15bc6c29c46 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include "ucsi.h" @@ -210,6 +212,7 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; rlen = min_t(u16, rem_len, max_read_len); @@ -218,12 +221,14 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); return status; } rab += rlen; rem_len -= rlen; } + pm_runtime_put_sync(uc->dev); return 0; } @@ -249,13 +254,16 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); kfree(buf); return status; } + pm_runtime_put_sync(uc->dev); kfree(buf); return 0; } @@ -1134,6 +1142,10 @@ static int ucsi_ccg_probe(struct i2c_client *client, if (status) dev_err(uc->dev, "cannot create sysfs group: %d\n", status); + pm_runtime_set_active(uc->dev); + pm_runtime_enable(uc->dev); + pm_runtime_idle(uc->dev); + return 0; } @@ -1143,6 +1155,7 @@ static int ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->work); ucsi_unregister_ppm(uc->ucsi); + pm_runtime_disable(uc->dev); free_irq(uc->irq, uc); sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group); @@ -1155,9 +1168,34 @@ static const struct i2c_device_id ucsi_ccg_device_id[] = { }; MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); +static int ucsi_ccg_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ucsi_ccg *uc = i2c_get_clientdata(client); + + return ucsi_resume(uc->ucsi); +} + +static int ucsi_ccg_runtime_
[PATCH v3 0/5] usb: typec: ucsi: ccg: add runtime pm support
Hi Heikki and Wolfram These patches add support for runtime power management for UCSI CCGx driver. I have tested them with NVIDIA GPU card which has i2c interface to talk to CCG controller. I have added runtime pm support for the i2c bus driver as well. Third version (v3) of patches fix comments from Heikki on adding common function in ucsi.c which can be used by both ucsi_ccg and ucsi_acpi. First patch refactors master_xfer() of i2c driver fixing comment from Wolfram. Second and third patch add support for runtime pm in i2c bus driver and UCSI CCGx driver. Last two patches add workaround for an old version of ccg firmware which has known issue of missing interrupt when a device is connected to runtime resume the ccg controller. The workaround is needed because if a GPU card doesn't get new firmware but gets new kernel then also it should continue to work. The workaround is to request runtime resume of i2c client which is UCSI Cypress CCGx driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. All the five patches should go together since ucsi_ccg driver is dependent on i2c driver for runtime pm wokaround. I would prefer it to go through usb-tree since usb ucsi_ccg driver is the main driver getting runtime pm functionality with the series. Thanks Ajay Ajay Gupta (5): i2c: nvidia-gpu: refactor master_xfer i2c: nvidia-gpu: add runtime pm support usb: typec: ucsi: ccg: enable runtime pm support i2c: nvidia-gpu: resume ccgx i2c client usb: typec: ucsi: ccg: add runtime pm workaround drivers/i2c/busses/i2c-nvidia-gpu.c | 60 ++ drivers/usb/typec/ucsi/ucsi.c | 10 +++ drivers/usb/typec/ucsi/ucsi.h | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 118 +++- 4 files changed, 169 insertions(+), 20 deletions(-) -- 2.17.1
[PATCH v3 2/5] i2c: nvidia-gpu: add runtime pm support
From: Ajay Gupta Enable runtime pm support with autosuspend delay of three second. This is to make sure I2C client device Cypress CCGx has completed all transaction. Signed-off-by: Ajay Gupta --- Changes from v1->v2: - Added __maybe_unused in gpu_i2c_suspend to avoid warning when CONFIG_PM is disabled. drivers/i2c/busses/i2c-nvidia-gpu.c | 19 ++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 2d9561ec2320..28fee85135ac 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -176,6 +176,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, * The controller supports maximum 4 byte read due to known * limitation of sending STOP after every read. */ + pm_runtime_get_sync(i2cd->dev); for (i = 0; i < num; i++) { if (msgs[i].flags & I2C_M_RD) { /* program client address before starting read */ @@ -211,6 +212,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, goto exit; } + pm_runtime_mark_last_busy(i2cd->dev); + pm_runtime_put_autosuspend(i2cd->dev); return i; exit: if (send_stop) { @@ -218,6 +221,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, if (status2 < 0) dev_err(i2cd->dev, "i2c stop failed %d\n", status2); } + pm_runtime_mark_last_busy(i2cd->dev); + pm_runtime_put_autosuspend(i2cd->dev); return status; } @@ -337,6 +342,11 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto del_adapter; } + pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_allow(&pdev->dev); + return 0; del_adapter: @@ -350,10 +360,16 @@ static void gpu_i2c_remove(struct pci_dev *pdev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); + pm_runtime_get_noresume(i2cd->dev); i2c_del_adapter(&i2cd->adapter); pci_free_irq_vectors(pdev); } +static __maybe_unused int gpu_i2c_suspend(struct device *dev) +{ + return 0; +} + static __maybe_unused int gpu_i2c_resume(struct device *dev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); @@ -362,7 +378,8 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) return 0; } -static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, NULL, gpu_i2c_resume, NULL); +static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, gpu_i2c_suspend, gpu_i2c_resume, + NULL); static struct pci_driver gpu_i2c_driver = { .name = "nvidia-gpu", -- 2.17.1
[PATCH v3 5/5] usb: typec: ucsi: ccg: add runtime pm workaround
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware version 3.1.10 (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. If any GPU card gets latest kernel with runtime pm support but does not get latest fixed firmware then also it should continue to work and therefore a workaround is required to check for any connector change event. The workaround is that i2c bus driver will call pm_request_resume() to runtime resume ucsi_ccg driver. CCG driver will call the ISR for any connector change event for NVIDIA GPU card and only if it has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta --- Changes from v2->v3: None drivers/usb/typec/ucsi/ucsi_ccg.c | 80 +-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index b15bc6c29c46..a5b81c011148 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -109,12 +109,21 @@ struct version_format { __le16 build; u8 patch; u8 ver; +#define CCG_VERSION_PATCH(x) ((x) << 16) +#define CCG_VERSION(x) ((x) << 24) #define CCG_VERSION_MIN_SHIFT (0) #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) #define CCG_VERSION_MAJ_SHIFT (4) #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) } __packed; +/* + * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue + * of missing interrupt when a device is connected for runtime resume + */ +#define CCG_FW_BUILD_NVIDIA(('n' << 8) | 'v') +#define CCG_OLD_FW_VERSION (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10)) + struct version_info { struct version_format base; struct version_format app; @@ -172,6 +181,7 @@ struct ucsi_ccg { struct ccg_dev_info info; /* version info for boot, primary and secondary */ struct version_info version[FW2 + 1]; + u32 fw_version; /* CCG HPI communication flags */ unsigned long flags; #define RESET_PENDING 0 @@ -185,6 +195,8 @@ struct ucsi_ccg { /* fw build with vendor information */ u16 fw_build; + bool run_isr; /* flag to call ISR routine during resume */ + struct work_struct pm_work; }; static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) @@ -212,6 +224,18 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + if (uc->fw_build == CCG_FW_BUILD_NVIDIA && + uc->fw_version <= CCG_OLD_FW_VERSION) { + mutex_lock(&uc->lock); + /* +* Do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; @@ -254,6 +278,18 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + if (uc->fw_build == CCG_FW_BUILD_NVIDIA && + uc->fw_version <= CCG_OLD_FW_VERSION) { + mutex_lock(&uc->lock); + /* +* Do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { @@ -383,6 +419,13 @@ static irqreturn_t ccg_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static void ccg_pm_workaround_work(struct work_struct *pm_work) +{ + struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work); + + ucsi_notify(uc->ucsi); +} + static int get_fw_info(struct ucsi_ccg *uc) { int err; @@ -392,6 +435,9 @@ static int get_fw_info(struct ucsi_ccg *uc) if (err < 0) return err; + uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) | + CCG_VERSION_PATCH(uc->version[FW2].app.patch); + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), sizeof(uc->info)); if (err < 0) @@ -740,11 +786,12 @@ static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name, } /* compa
RE: [PATCH v3 2/5] i2c: nvidia-gpu: add runtime pm support
Hi Wolfram, > -Original Message- > From: linux-usb-ow...@vger.kernel.org > On Behalf Of Wolfram Sang > Sent: Saturday, May 25, 2019 12:57 PM > To: Ajay Gupta > Cc: heikki.kroge...@linux.intel.com; linux-usb@vger.kernel.org; linux- > i...@vger.kernel.org; Ajay Gupta > Subject: Re: [PATCH v3 2/5] i2c: nvidia-gpu: add runtime pm support > > > > @@ -211,6 +212,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter > *adap, > > goto exit; > > } > > > > + pm_runtime_mark_last_busy(i2cd->dev); > > + pm_runtime_put_autosuspend(i2cd->dev); > > return i; > > exit: > > if (send_stop) { > > @@ -218,6 +221,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter > *adap, > > if (status2 < 0) > > dev_err(i2cd->dev, "i2c stop failed %d\n", status2); > > } > > + pm_runtime_mark_last_busy(i2cd->dev); > > + pm_runtime_put_autosuspend(i2cd->dev); > > Maybe another worthwhile refactorization possible here? The exit code path > and 'all good' code path look very similar. > This can be done incrementally, though. I think for now it is okay. Thanks for suggestion. Yes , it is possible to refactor a bit further here. > > +static __maybe_unused int gpu_i2c_suspend(struct device *dev) { > > + return 0; > > +} > > Why do we need this? I2C function of PCI bus remains in D0 (lspci output) without this function. Following document also seems to insist on this. " For this to work, the device's driver has to provide a pm->runtime_suspend() callback " Documentation/power/pci.txt "First, a PCI device is put into a low-power state, or suspended, with the help of pm_schedule_suspend() or pm_runtime_suspend() which for PCI devices call pci_pm_runtime_suspend() to do the actual job. For this to work, the device's driver has to provide a pm->runtime_suspend() callback (see below), which is run by pci_pm_runtime_suspend() as the first action. If the driver's callback returns successfully, the device's standard configuration registers are saved, the device is prepared to generate wakeup signals and, finally, it is put into the target low-power state." Thanks Ajay > nvpublic
RE: [PATCH v3 2/5] i2c: nvidia-gpu: add runtime pm support
Hi Wolfram, > > -Original Message- > > From: linux-usb-ow...@vger.kernel.org ow...@vger.kernel.org> > > On Behalf Of Wolfram Sang > > Sent: Saturday, May 25, 2019 12:57 PM > > To: Ajay Gupta > > Cc: heikki.kroge...@linux.intel.com; linux-usb@vger.kernel.org; linux- > > i...@vger.kernel.org; Ajay Gupta > > Subject: Re: [PATCH v3 2/5] i2c: nvidia-gpu: add runtime pm support > > > > > > > @@ -211,6 +212,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter > > *adap, > > > goto exit; > > > } > > > > > > + pm_runtime_mark_last_busy(i2cd->dev); > > > + pm_runtime_put_autosuspend(i2cd->dev); > > > return i; > > > exit: > > > if (send_stop) { > > > @@ -218,6 +221,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter > > *adap, > > > if (status2 < 0) > > > dev_err(i2cd->dev, "i2c stop failed %d\n", status2); > > > } > > > + pm_runtime_mark_last_busy(i2cd->dev); > > > + pm_runtime_put_autosuspend(i2cd->dev); > > > > Maybe another worthwhile refactorization possible here? The exit code > path > > and 'all good' code path look very similar. > > This can be done incrementally, though. I think for now it is okay. > Thanks for suggestion. Yes , it is possible to refactor a bit further here. > > > > +static __maybe_unused int gpu_i2c_suspend(struct device *dev) { > > > + return 0; > > > +} > > > > Why do we need this? > I2C function of PCI bus remains in D0 (lspci output) without this function. Do you still see any issue with gpu_i2c_suspend()? Thanks Ajay > nvpublic > > Following document also seems to insist on this. > " For this to work, the device's driver has to provide a > pm->runtime_suspend() callback " > > Documentation/power/pci.txt > "First, a PCI device is put into a low-power state, or suspended, with the > help > of pm_schedule_suspend() or pm_runtime_suspend() which for PCI devices > call > pci_pm_runtime_suspend() to do the actual job. For this to work, the > device's > driver has to provide a pm->runtime_suspend() callback (see below), which > is > run by pci_pm_runtime_suspend() as the first action. If the driver's callback > returns successfully, the device's standard configuration registers are saved, > the device is prepared to generate wakeup signals and, finally, it is put into > the target low-power state." > > Thanks > Ajay > > nvpublic
[PATCH v4 0/5] usb: typec: ucsi: ccg: add runtime pm support
Hi Heikki and Wolfram These patches add support for runtime power management for UCSI CCGx driver. I have tested them with NVIDIA GPU card which has i2c interface to talk to CCG controller. I have added runtime pm support for the i2c bus driver as well. Third version (v4) of patches fix comments from Wolfram on further refactoring master_xfer() fucntion in i2c-nvidia-gpuc.c file. Also I have added comment on why we need a stub gpu_i2c_suspend() for runtime pm to work correctly. First patch refactors master_xfer() of i2c driver fixing comment from Wolfram. Second and third patch add support for runtime pm in i2c bus driver and UCSI CCGx driver. Last two patches add workaround for an old version of ccg firmware which has known issue of missing interrupt when a device is connected to runtime resume the ccg controller. The workaround is needed because if a GPU card doesn't get new firmware but gets new kernel then also it should continue to work. The workaround is to request runtime resume of i2c client which is UCSI Cypress CCGx driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. All the five patches should go together since ucsi_ccg driver is dependent on i2c driver for runtime pm wokaround. I would prefer it to go through usb-tree since usb ucsi_ccg driver is the main driver getting runtime pm functionality with the series. Thanks Ajay Ajay Gupta (5): i2c: nvidia-gpu: refactor master_xfer i2c: nvidia-gpu: add runtime pm support usb: typec: ucsi: ccg: enable runtime pm support i2c: nvidia-gpu: resume ccgx i2c client usb: typec: ucsi: ccg: add runtime pm workaround drivers/i2c/busses/i2c-nvidia-gpu.c | 71 + drivers/usb/typec/ucsi/ucsi.c | 10 +++ drivers/usb/typec/ucsi/ucsi.h | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 118 +++- 4 files changed, 179 insertions(+), 21 deletions(-) -- 2.17.1
[PATCH v4 4/5] i2c: nvidia-gpu: resume ccgx i2c client
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware version 3.1.10 (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. If any GPU card gets latest kernel with runtime pm support but does not get latest fixed firmware then also it should continue to work and therefore a workaround is required to check for any connector change event The workaround is to request runtime resume of i2c client which is UCSI Cypress CCGx driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta --- Changes from v3->v4: None drivers/i2c/busses/i2c-nvidia-gpu.c | 14 ++ 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index ad1006e72a03..17a7cdf1dea0 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -51,6 +51,7 @@ struct gpu_i2c_dev { void __iomem *regs; struct i2c_adapter adapter; struct i2c_board_info *gpu_ccgx_ucsi; + struct i2c_client *ccgx_client; }; static void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd) @@ -270,8 +271,6 @@ static const struct property_entry ccgx_props[] = { static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) { - struct i2c_client *ccgx_client; - i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev, sizeof(*i2cd->gpu_ccgx_ucsi), GFP_KERNEL); @@ -283,8 +282,8 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) i2cd->gpu_ccgx_ucsi->addr = 0x8; i2cd->gpu_ccgx_ucsi->irq = irq; i2cd->gpu_ccgx_ucsi->properties = ccgx_props; - ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); - if (!ccgx_client) + i2cd->ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); + if (!i2cd->ccgx_client) return -ENODEV; return 0; @@ -384,6 +383,13 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); gpu_enable_i2c_bus(i2cd); + /* +* Runtime resume ccgx client so that it can see for any +* connector change event. Old ccg firmware has known +* issue of not triggering interrupt when a device is +* connected to runtime resume the controller. +*/ + pm_request_resume(&i2cd->ccgx_client->dev); return 0; } -- 2.17.1
[PATCH v4 5/5] usb: typec: ucsi: ccg: add runtime pm workaround
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware version 3.1.10 (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. If any GPU card gets latest kernel with runtime pm support but does not get latest fixed firmware then also it should continue to work and therefore a workaround is required to check for any connector change event. The workaround is that i2c bus driver will call pm_request_resume() to runtime resume ucsi_ccg driver. CCG driver will call the ISR for any connector change event for NVIDIA GPU card and only if it has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta --- Changes from v3->v4: None drivers/usb/typec/ucsi/ucsi_ccg.c | 80 +-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index b15bc6c29c46..a5b81c011148 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -109,12 +109,21 @@ struct version_format { __le16 build; u8 patch; u8 ver; +#define CCG_VERSION_PATCH(x) ((x) << 16) +#define CCG_VERSION(x) ((x) << 24) #define CCG_VERSION_MIN_SHIFT (0) #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) #define CCG_VERSION_MAJ_SHIFT (4) #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) } __packed; +/* + * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue + * of missing interrupt when a device is connected for runtime resume + */ +#define CCG_FW_BUILD_NVIDIA(('n' << 8) | 'v') +#define CCG_OLD_FW_VERSION (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10)) + struct version_info { struct version_format base; struct version_format app; @@ -172,6 +181,7 @@ struct ucsi_ccg { struct ccg_dev_info info; /* version info for boot, primary and secondary */ struct version_info version[FW2 + 1]; + u32 fw_version; /* CCG HPI communication flags */ unsigned long flags; #define RESET_PENDING 0 @@ -185,6 +195,8 @@ struct ucsi_ccg { /* fw build with vendor information */ u16 fw_build; + bool run_isr; /* flag to call ISR routine during resume */ + struct work_struct pm_work; }; static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) @@ -212,6 +224,18 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + if (uc->fw_build == CCG_FW_BUILD_NVIDIA && + uc->fw_version <= CCG_OLD_FW_VERSION) { + mutex_lock(&uc->lock); + /* +* Do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; @@ -254,6 +278,18 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + if (uc->fw_build == CCG_FW_BUILD_NVIDIA && + uc->fw_version <= CCG_OLD_FW_VERSION) { + mutex_lock(&uc->lock); + /* +* Do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { @@ -383,6 +419,13 @@ static irqreturn_t ccg_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static void ccg_pm_workaround_work(struct work_struct *pm_work) +{ + struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work); + + ucsi_notify(uc->ucsi); +} + static int get_fw_info(struct ucsi_ccg *uc) { int err; @@ -392,6 +435,9 @@ static int get_fw_info(struct ucsi_ccg *uc) if (err < 0) return err; + uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) | + CCG_VERSION_PATCH(uc->version[FW2].app.patch); + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), sizeof(uc->info)); if (err < 0) @@ -740,11 +786,12 @@ static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name, } /* compa
[PATCH v4 1/5] i2c: nvidia-gpu: refactor master_xfer
From: Ajay Gupta Added a local variable "send_stop" to simplify "goto" statements. The "send_stop" handles below two case 1) When first i2c start fails and so i2c stop is not sent before exiting 2) When i2c stop failed after all transfers and we do not need to send another stop before exiting. Signed-off-by: Ajay Gupta --- Changes from v3->v4: - Further refactor master_xfer based on Wolfram's comment. drivers/i2c/busses/i2c-nvidia-gpu.c | 30 + 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 1c8f708f212b..f1771beb75ea 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -169,6 +169,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, { struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap); int status, status2; + bool send_stop = true; int i, j; /* @@ -182,37 +183,42 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, /* gpu_i2c_read has implicit start */ status = gpu_i2c_read(i2cd, msgs[i].buf, msgs[i].len); if (status < 0) - goto stop; + goto exit; } else { u8 addr = i2c_8bit_addr_from_msg(msgs + i); status = gpu_i2c_start(i2cd); if (status < 0) { if (i == 0) - return status; - goto stop; + send_stop = false; + goto exit; } status = gpu_i2c_write(i2cd, addr); if (status < 0) - goto stop; + goto exit; for (j = 0; j < msgs[i].len; j++) { status = gpu_i2c_write(i2cd, msgs[i].buf[j]); if (status < 0) - goto stop; + goto exit; } } } status = gpu_i2c_stop(i2cd); - if (status < 0) - return status; + if (status < 0) { + send_stop = false; + goto exit; + } - return i; -stop: - status2 = gpu_i2c_stop(i2cd); - if (status2 < 0) - dev_err(i2cd->dev, "i2c stop failed %d\n", status2); + send_stop = false; + status = i; +exit: + if (send_stop) { + status2 = gpu_i2c_stop(i2cd); + if (status2 < 0) + dev_err(i2cd->dev, "i2c stop failed %d\n", status2); + } return status; } -- 2.17.1
[PATCH v4 2/5] i2c: nvidia-gpu: add runtime pm support
From: Ajay Gupta Enable runtime pm support with autosuspend delay of three second. This is to make sure I2C client device Cypress CCGx has completed all transaction. Signed-off-by: Ajay Gupta --- Changes from v3->v4: - Added comment on why stub gpu_i2c_suspend() is needed for runtime pm to work correctly. drivers/i2c/busses/i2c-nvidia-gpu.c | 27 ++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index f1771beb75ea..ad1006e72a03 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -176,6 +176,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, * The controller supports maximum 4 byte read due to known * limitation of sending STOP after every read. */ + pm_runtime_get_sync(i2cd->dev); for (i = 0; i < num; i++) { if (msgs[i].flags & I2C_M_RD) { /* program client address before starting read */ @@ -219,6 +220,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, if (status2 < 0) dev_err(i2cd->dev, "i2c stop failed %d\n", status2); } + pm_runtime_mark_last_busy(i2cd->dev); + pm_runtime_put_autosuspend(i2cd->dev); return status; } @@ -338,6 +341,11 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto del_adapter; } + pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_allow(&pdev->dev); + return 0; del_adapter: @@ -351,10 +359,26 @@ static void gpu_i2c_remove(struct pci_dev *pdev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); + pm_runtime_get_noresume(i2cd->dev); i2c_del_adapter(&i2cd->adapter); pci_free_irq_vectors(pdev); } +/* + * We need gpu_i2c_suspend() even if it is stub, for runtime pm to work + * correctly. Without it, lspci shows runtime pm status as "D0" for the card. + * Documentation/power/pci.txt also insists for driver to provide this: + * "First, a PCI device is put into a low-power state, or suspended, with the + * help of pm_schedule_suspend() or pm_runtime_suspend() which for PCI devices + * call pci_pm_runtime_suspend() to do the actual job. For this to work, the + * device's driver has to provide a pm->runtime_suspend() callback (see below), + * which is run by pci_pm_runtime_suspend() as the first action." + */ +static __maybe_unused int gpu_i2c_suspend(struct device *dev) +{ + return 0; +} + static __maybe_unused int gpu_i2c_resume(struct device *dev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); @@ -363,7 +387,8 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) return 0; } -static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, NULL, gpu_i2c_resume, NULL); +static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, gpu_i2c_suspend, gpu_i2c_resume, + NULL); static struct pci_driver gpu_i2c_driver = { .name = "nvidia-gpu", -- 2.17.1
[PATCH v4 3/5] usb: typec: ucsi: ccg: enable runtime pm support
From: Ajay Gupta The change enables runtime pm support to UCSI CCG driver. Added ucsi_resume() function to enable notification after system reusme. Exported both ucsi_resume() and ucsi_send_command() symbols in ucsi.c for modular build. Signed-off-by: Ajay Gupta --- Changes from v3->v4 : None drivers/usb/typec/ucsi/ucsi.c | 10 drivers/usb/typec/ucsi/ucsi.h | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 38 +++ 3 files changed, 49 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 7850b851cecd..ba288b964dc8 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -206,7 +206,17 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, return ret; } +EXPORT_SYMBOL_GPL(ucsi_send_command); +int ucsi_resume(struct ucsi *ucsi) +{ + struct ucsi_control ctrl; + + /* Restore UCSI notification enable mask after system resume */ + UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL); + return ucsi_send_command(ucsi, &ctrl, NULL, 0); +} +EXPORT_SYMBOL_GPL(ucsi_resume); /* -- */ void ucsi_altmode_update_active(struct ucsi_connector *con) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 1e2981aef629..de87d0b8319d 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -430,6 +430,7 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, void *retval, size_t size); void ucsi_altmode_update_active(struct ucsi_connector *con); +int ucsi_resume(struct ucsi *ucsi); #if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE) struct typec_altmode * diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 9d46aa9e4e35..b15bc6c29c46 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include "ucsi.h" @@ -210,6 +212,7 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; rlen = min_t(u16, rem_len, max_read_len); @@ -218,12 +221,14 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); return status; } rab += rlen; rem_len -= rlen; } + pm_runtime_put_sync(uc->dev); return 0; } @@ -249,13 +254,16 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); kfree(buf); return status; } + pm_runtime_put_sync(uc->dev); kfree(buf); return 0; } @@ -1134,6 +1142,10 @@ static int ucsi_ccg_probe(struct i2c_client *client, if (status) dev_err(uc->dev, "cannot create sysfs group: %d\n", status); + pm_runtime_set_active(uc->dev); + pm_runtime_enable(uc->dev); + pm_runtime_idle(uc->dev); + return 0; } @@ -1143,6 +1155,7 @@ static int ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->work); ucsi_unregister_ppm(uc->ucsi); + pm_runtime_disable(uc->dev); free_irq(uc->irq, uc); sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group); @@ -1155,9 +1168,34 @@ static const struct i2c_device_id ucsi_ccg_device_id[] = { }; MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); +static int ucsi_ccg_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ucsi_ccg *uc = i2c_get_clientdata(client); + + return ucsi_resume(uc->ucsi); +} + +static int ucsi_ccg_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int ucsi_ccg_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops ucsi_ccg_pm = { + .resume = ucsi_ccg_resume, + .runtime_suspend = ucsi_ccg_runtime_suspend, + .runtime_resume = ucsi_ccg_runtime_r
RE: [PATCH v4 3/5] usb: typec: ucsi: ccg: enable runtime pm support
Hi Heikki and Wolfram, > -Original Message- > From: linux-i2c-ow...@vger.kernel.org > On Behalf Of Wolfram Sang > Sent: Friday, June 7, 2019 1:27 AM > To: Heikki Krogerus > Cc: Ajay Gupta ; linux-usb@vger.kernel.org; linux- > i...@vger.kernel.org; Ajay Gupta > Subject: Re: [PATCH v4 3/5] usb: typec: ucsi: ccg: enable runtime pm support > > On Fri, Jun 07, 2019 at 11:25:10AM +0300, Heikki Krogerus wrote: > > On Mon, Jun 03, 2019 at 10:05:43AM -0700, Ajay Gupta wrote: > > > From: Ajay Gupta > > > > > > The change enables runtime pm support to UCSI CCG driver. > > > Added ucsi_resume() function to enable notification after system > > > reusme. Exported both ucsi_resume() and ucsi_send_command() > symbols > > > in ucsi.c for modular build. > > > > > > Signed-off-by: Ajay Gupta > > > > Was the idea that Wolfram picks these? In that case: > > Cover letter says your tree, will check the i2c patches now. Either tree is fine with me. There are 3 (out of 5) I2C related patches in the set so better they go through I2C tree. Thanks Ajay > nvpublic
RE: [PATCH v4 1/5] i2c: nvidia-gpu: refactor master_xfer
Hi Wolfram, > -Original Message- > From: Wolfram Sang > Sent: Friday, June 7, 2019 1:33 AM > To: Ajay Gupta > Cc: heikki.kroge...@linux.intel.com; linux-usb@vger.kernel.org; linux- > i...@vger.kernel.org; Ajay Gupta > Subject: Re: [PATCH v4 1/5] i2c: nvidia-gpu: refactor master_xfer > > > Changes from v3->v4: > > - Further refactor master_xfer based on Wolfram's comment. > > Yay, looks even better. One thing to improve, though. > > > status = gpu_i2c_stop(i2cd); > > send_stop = false; > > > - if (status < 0) > > - return status; > > + if (status < 0) { > > + send_stop = false; > > and skip it here... > > > + goto exit; > > + } > > > > - return i; > > -stop: > > - status2 = gpu_i2c_stop(i2cd); > > - if (status2 < 0) > > - dev_err(i2cd->dev, "i2c stop failed %d\n", status2); > > + send_stop = false; > > ... and here. Sure, will fix. Thanks >nvpublic
RE: [PATCH v4 2/5] i2c: nvidia-gpu: add runtime pm support
Hi Wolfram, > -Original Message- > From: linux-i2c-ow...@vger.kernel.org > On Behalf Of Wolfram Sang > Sent: Friday, June 7, 2019 1:33 AM > To: Ajay Gupta > Cc: heikki.kroge...@linux.intel.com; linux-usb@vger.kernel.org; linux- > i...@vger.kernel.org; Ajay Gupta > Subject: Re: [PATCH v4 2/5] i2c: nvidia-gpu: add runtime pm support > > > > + pm_runtime_mark_last_busy(i2cd->dev); > > + pm_runtime_put_autosuspend(i2cd->dev); > > Much better to have this only once! > > > +/* > > + * We need gpu_i2c_suspend() even if it is stub, for runtime pm to > > +work > > + * correctly. Without it, lspci shows runtime pm status as "D0" for the > card. > > + * Documentation/power/pci.txt also insists for driver to provide this: > > I'd think the comment up to here is enough and the rest can go. However, I > leave this decision to you. Sure, will fix it. Thanks > nvpublic
[PATCH v5 1/5] i2c: nvidia-gpu: refactor master_xfer
From: Ajay Gupta Added a local variable "send_stop" to simplify "goto" statements. The "send_stop" handles below two case 1) When first i2c start fails and so i2c stop is not sent before exiting 2) When i2c stop failed after all transfers and we do not need to send another stop before exiting. Signed-off-by: Ajay Gupta --- Changes from v4->v5: - Further refactor master_xfer based on Wolfram's comment. drivers/i2c/busses/i2c-nvidia-gpu.c | 28 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 1c8f708f212b..7678a460bf9a 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -169,6 +169,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, { struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap); int status, status2; + bool send_stop = true; int i, j; /* @@ -182,37 +183,40 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, /* gpu_i2c_read has implicit start */ status = gpu_i2c_read(i2cd, msgs[i].buf, msgs[i].len); if (status < 0) - goto stop; + goto exit; } else { u8 addr = i2c_8bit_addr_from_msg(msgs + i); status = gpu_i2c_start(i2cd); if (status < 0) { if (i == 0) - return status; - goto stop; + send_stop = false; + goto exit; } status = gpu_i2c_write(i2cd, addr); if (status < 0) - goto stop; + goto exit; for (j = 0; j < msgs[i].len; j++) { status = gpu_i2c_write(i2cd, msgs[i].buf[j]); if (status < 0) - goto stop; + goto exit; } } } + send_stop = false; status = gpu_i2c_stop(i2cd); if (status < 0) - return status; - - return i; -stop: - status2 = gpu_i2c_stop(i2cd); - if (status2 < 0) - dev_err(i2cd->dev, "i2c stop failed %d\n", status2); + goto exit; + + status = i; +exit: + if (send_stop) { + status2 = gpu_i2c_stop(i2cd); + if (status2 < 0) + dev_err(i2cd->dev, "i2c stop failed %d\n", status2); + } return status; } -- 2.17.1
[PATCH v5 0/5] usb: typec: ucsi: ccg: add runtime pm support
Hi Heikki and Wolfram The latest set (v5) fix comments from Wolfram on further refactoring master_xfer() function in i2c-nvidia-gpuc.c file and removing extra comments in patch 2/5. Patches can go through either usb or i2c tree but since there are 3 out of 5 patches from i2c so may be better they go through i2c. They all should go together. Thanks Ajay v4 These patches add support for runtime power management for UCSI CCGx driver. I have tested them with NVIDIA GPU card which has i2c interface to talk to CCG controller. I have added runtime pm support for the i2c bus driver as well. Fourth version (v4) of patches fix comments from Wolfram on further refactoring master_xfer() function in i2c-nvidia-gpuc.c file. Also I have added comment on why we need a stub gpu_i2c_suspend() for runtime pm to work correctly. First patch refactors master_xfer() of i2c driver fixing comment from Wolfram. Second and third patch add support for runtime pm in i2c bus driver and UCSI CCGx driver. Last two patches add workaround for an old version of ccg firmware which has known issue of missing interrupt when a device is connected to runtime resume the ccg controller. The workaround is needed because if a GPU card doesn't get new firmware but gets new kernel then also it should continue to work. The workaround is to request runtime resume of i2c client which is UCSI Cypress CCGx driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. All the five patches should go together since ucsi_ccg driver is dependent on i2c driver for runtime pm wokaround. I would prefer it to go through usb-tree since usb ucsi_ccg driver is the main driver getting runtime pm functionality with the series. Thanks Ajay Ajay Gupta (5): i2c: nvidia-gpu: refactor master_xfer i2c: nvidia-gpu: add runtime pm support usb: typec: ucsi: ccg: enable runtime pm support i2c: nvidia-gpu: resume ccgx i2c client usb: typec: ucsi: ccg: add runtime pm workaround drivers/i2c/busses/i2c-nvidia-gpu.c | 64 +++ drivers/usb/typec/ucsi/ucsi.c | 10 +++ drivers/usb/typec/ucsi/ucsi.h | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 118 +++- 4 files changed, 172 insertions(+), 21 deletions(-) -- 2.17.1
[PATCH v5 4/5] i2c: nvidia-gpu: resume ccgx i2c client
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware version 3.1.10 (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. If any GPU card gets latest kernel with runtime pm support but does not get latest fixed firmware then also it should continue to work and therefore a workaround is required to check for any connector change event The workaround is to request runtime resume of i2c client which is UCSI Cypress CCGx driver. CCG driver will call the ISR for any connector change event only if NVIDIA GPU has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta --- Changes from v4->v5: None drivers/i2c/busses/i2c-nvidia-gpu.c | 14 ++ 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 364244ffb5bf..cfc76b5de726 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -51,6 +51,7 @@ struct gpu_i2c_dev { void __iomem *regs; struct i2c_adapter adapter; struct i2c_board_info *gpu_ccgx_ucsi; + struct i2c_client *ccgx_client; }; static void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd) @@ -268,8 +269,6 @@ static const struct property_entry ccgx_props[] = { static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) { - struct i2c_client *ccgx_client; - i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev, sizeof(*i2cd->gpu_ccgx_ucsi), GFP_KERNEL); @@ -281,8 +280,8 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) i2cd->gpu_ccgx_ucsi->addr = 0x8; i2cd->gpu_ccgx_ucsi->irq = irq; i2cd->gpu_ccgx_ucsi->properties = ccgx_props; - ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); - if (!ccgx_client) + i2cd->ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); + if (!i2cd->ccgx_client) return -ENODEV; return 0; @@ -377,6 +376,13 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); gpu_enable_i2c_bus(i2cd); + /* +* Runtime resume ccgx client so that it can see for any +* connector change event. Old ccg firmware has known +* issue of not triggering interrupt when a device is +* connected to runtime resume the controller. +*/ + pm_request_resume(&i2cd->ccgx_client->dev); return 0; } -- 2.17.1
[PATCH v5 3/5] usb: typec: ucsi: ccg: enable runtime pm support
From: Ajay Gupta The change enables runtime pm support to UCSI CCG driver. Added ucsi_resume() function to enable notification after system reusme. Exported both ucsi_resume() and ucsi_send_command() symbols in ucsi.c for modular build. Signed-off-by: Ajay Gupta Acked-by: Heikki Krogerus --- Changes from v4->v5 : None drivers/usb/typec/ucsi/ucsi.c | 10 drivers/usb/typec/ucsi/ucsi.h | 1 + drivers/usb/typec/ucsi/ucsi_ccg.c | 38 +++ 3 files changed, 49 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 7850b851cecd..ba288b964dc8 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -206,7 +206,17 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, return ret; } +EXPORT_SYMBOL_GPL(ucsi_send_command); +int ucsi_resume(struct ucsi *ucsi) +{ + struct ucsi_control ctrl; + + /* Restore UCSI notification enable mask after system resume */ + UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL); + return ucsi_send_command(ucsi, &ctrl, NULL, 0); +} +EXPORT_SYMBOL_GPL(ucsi_resume); /* -- */ void ucsi_altmode_update_active(struct ucsi_connector *con) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 1e2981aef629..de87d0b8319d 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -430,6 +430,7 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, void *retval, size_t size); void ucsi_altmode_update_active(struct ucsi_connector *con); +int ucsi_resume(struct ucsi *ucsi); #if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE) struct typec_altmode * diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 9d46aa9e4e35..b15bc6c29c46 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include "ucsi.h" @@ -210,6 +212,7 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; rlen = min_t(u16, rem_len, max_read_len); @@ -218,12 +221,14 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); return status; } rab += rlen; rem_len -= rlen; } + pm_runtime_put_sync(uc->dev); return 0; } @@ -249,13 +254,16 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { dev_err(uc->dev, "i2c_transfer failed %d\n", status); + pm_runtime_put_sync(uc->dev); kfree(buf); return status; } + pm_runtime_put_sync(uc->dev); kfree(buf); return 0; } @@ -1134,6 +1142,10 @@ static int ucsi_ccg_probe(struct i2c_client *client, if (status) dev_err(uc->dev, "cannot create sysfs group: %d\n", status); + pm_runtime_set_active(uc->dev); + pm_runtime_enable(uc->dev); + pm_runtime_idle(uc->dev); + return 0; } @@ -1143,6 +1155,7 @@ static int ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->work); ucsi_unregister_ppm(uc->ucsi); + pm_runtime_disable(uc->dev); free_irq(uc->irq, uc); sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group); @@ -1155,9 +1168,34 @@ static const struct i2c_device_id ucsi_ccg_device_id[] = { }; MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); +static int ucsi_ccg_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ucsi_ccg *uc = i2c_get_clientdata(client); + + return ucsi_resume(uc->ucsi); +} + +static int ucsi_ccg_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int ucsi_ccg_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops ucsi_ccg_pm = { + .resume = ucsi_ccg_resume, + .runtime_suspend = ucsi_ccg_runtime_suspend, +
[PATCH v5 2/5] i2c: nvidia-gpu: add runtime pm support
From: Ajay Gupta Enable runtime pm support with autosuspend delay of three second. This is to make sure I2C client device Cypress CCGx has completed all transaction. Signed-off-by: Ajay Gupta --- Changes from v4->v5: - Removed extra comments for gpu_i2c_suspend() based on Wolfram's comment. drivers/i2c/busses/i2c-nvidia-gpu.c | 22 +- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 7678a460bf9a..364244ffb5bf 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -176,6 +176,7 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, * The controller supports maximum 4 byte read due to known * limitation of sending STOP after every read. */ + pm_runtime_get_sync(i2cd->dev); for (i = 0; i < num; i++) { if (msgs[i].flags & I2C_M_RD) { /* program client address before starting read */ @@ -217,6 +218,8 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap, if (status2 < 0) dev_err(i2cd->dev, "i2c stop failed %d\n", status2); } + pm_runtime_mark_last_busy(i2cd->dev); + pm_runtime_put_autosuspend(i2cd->dev); return status; } @@ -336,6 +339,11 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto del_adapter; } + pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_allow(&pdev->dev); + return 0; del_adapter: @@ -349,10 +357,21 @@ static void gpu_i2c_remove(struct pci_dev *pdev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); + pm_runtime_get_noresume(i2cd->dev); i2c_del_adapter(&i2cd->adapter); pci_free_irq_vectors(pdev); } +/* + * We need gpu_i2c_suspend() even if it is stub, for runtime pm to work + * correctly. Without it, lspci shows runtime pm status as "D0" for the card. + * Documentation/power/pci.txt also insists for driver to provide this. + */ +static __maybe_unused int gpu_i2c_suspend(struct device *dev) +{ + return 0; +} + static __maybe_unused int gpu_i2c_resume(struct device *dev) { struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); @@ -361,7 +380,8 @@ static __maybe_unused int gpu_i2c_resume(struct device *dev) return 0; } -static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, NULL, gpu_i2c_resume, NULL); +static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, gpu_i2c_suspend, gpu_i2c_resume, + NULL); static struct pci_driver gpu_i2c_driver = { .name = "nvidia-gpu", -- 2.17.1
[PATCH v5 5/5] usb: typec: ucsi: ccg: add runtime pm workaround
From: Ajay Gupta Cypress USB Type-C CCGx controller firmware version 3.1.10 (which is being used in many NVIDIA GPU cards) has known issue of not triggering interrupt when a USB device is hot plugged to runtime resume the controller. If any GPU card gets latest kernel with runtime pm support but does not get latest fixed firmware then also it should continue to work and therefore a workaround is required to check for any connector change event. The workaround is that i2c bus driver will call pm_request_resume() to runtime resume ucsi_ccg driver. CCG driver will call the ISR for any connector change event for NVIDIA GPU card and only if it has old CCG firmware with the known issue. Signed-off-by: Ajay Gupta Acked-by: Heikki Krogerus --- Changes from v4->v5: None drivers/usb/typec/ucsi/ucsi_ccg.c | 80 +-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index b15bc6c29c46..a5b81c011148 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -109,12 +109,21 @@ struct version_format { __le16 build; u8 patch; u8 ver; +#define CCG_VERSION_PATCH(x) ((x) << 16) +#define CCG_VERSION(x) ((x) << 24) #define CCG_VERSION_MIN_SHIFT (0) #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) #define CCG_VERSION_MAJ_SHIFT (4) #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) } __packed; +/* + * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue + * of missing interrupt when a device is connected for runtime resume + */ +#define CCG_FW_BUILD_NVIDIA(('n' << 8) | 'v') +#define CCG_OLD_FW_VERSION (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10)) + struct version_info { struct version_format base; struct version_format app; @@ -172,6 +181,7 @@ struct ucsi_ccg { struct ccg_dev_info info; /* version info for boot, primary and secondary */ struct version_info version[FW2 + 1]; + u32 fw_version; /* CCG HPI communication flags */ unsigned long flags; #define RESET_PENDING 0 @@ -185,6 +195,8 @@ struct ucsi_ccg { /* fw build with vendor information */ u16 fw_build; + bool run_isr; /* flag to call ISR routine during resume */ + struct work_struct pm_work; }; static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) @@ -212,6 +224,18 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; + if (uc->fw_build == CCG_FW_BUILD_NVIDIA && + uc->fw_version <= CCG_OLD_FW_VERSION) { + mutex_lock(&uc->lock); + /* +* Do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; @@ -254,6 +278,18 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; + if (uc->fw_build == CCG_FW_BUILD_NVIDIA && + uc->fw_version <= CCG_OLD_FW_VERSION) { + mutex_lock(&uc->lock); + /* +* Do not schedule pm_work to run ISR in +* ucsi_ccg_runtime_resume() after pm_runtime_get_sync() +* since we are already in ISR path. +*/ + uc->run_isr = false; + mutex_unlock(&uc->lock); + } + pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { @@ -383,6 +419,13 @@ static irqreturn_t ccg_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static void ccg_pm_workaround_work(struct work_struct *pm_work) +{ + struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work); + + ucsi_notify(uc->ucsi); +} + static int get_fw_info(struct ucsi_ccg *uc) { int err; @@ -392,6 +435,9 @@ static int get_fw_info(struct ucsi_ccg *uc) if (err < 0) return err; + uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) | + CCG_VERSION_PATCH(uc->version[FW2].app.patch); + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), sizeof(uc->info)); if (err < 0) @@ -740,11 +786,12 @@ static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char
[RFC] usb: typec: ucsi: add support for separate DP altmode devices
CCGx controller used on NVIDIA GPU card has two separate display altmode for two DP pin assignments. UCSI specification doesn't prohibits using separate display altmode. Current UCSI Type-C framework expects only one display altmode for all DP pin assignment. This patch squashes two separate display altmode into single altmode to support controllers with separate display altmode. We first read all the alternate modes of connector and then run through it to know if there are separate display altmodes. If so, it prepares a new port altmode set after squashing two or more separate altmodes into one. Signed-off-by: Ajay Gupta --- drivers/usb/typec/ucsi/ucsi.c | 191 +- drivers/usb/typec/ucsi/ucsi.h | 12 +++ 2 files changed, 199 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index ba288b964dc8..491b5c925a90 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -39,8 +39,27 @@ */ #define UCSI_SWAP_TIMEOUT_MS 5000 +static void ucsi_update_get_current_cam_cmd(struct ucsi_connector *con, + struct ucsi_data *data) +{ + u8 cam, new_cam; + + if (data->cci.data_length == 0x1) { + cam = data->message_in[0]; + new_cam = con->port_alt[cam].linked_idx; + data->message_in[0] = new_cam; + con->new_port_alt[new_cam].active_idx = cam; + } +} + static inline int ucsi_sync(struct ucsi *ucsi) { + struct ucsi_connector *con = ucsi->connector; + struct ucsi_data *data = ucsi->ppm->data; + + if (data->ctrl.alt.cmd == UCSI_GET_CURRENT_CAM && con->has_multiple_dp) + ucsi_update_get_current_cam_cmd(con, data); + if (ucsi->ppm && ucsi->ppm->sync) return ucsi->ppm->sync(ucsi->ppm); return 0; @@ -101,14 +120,57 @@ static int ucsi_ack(struct ucsi *ucsi, u8 ack) return ret; } +static void ucsi_update_set_new_cam_cmd(struct ucsi_connector *con, + struct ucsi_control *ctrl) +{ + struct new_ucsi_altmode *new_port, *port; + struct typec_altmode *partner = NULL; + u64 cmd; + u8 new_cam, cam; + bool enter_new_mode; + int i, j; + + cmd = ctrl->raw_cmd; + new_cam = (cmd >> 24) & 0xff; + new_port = &con->new_port_alt[new_cam]; + cam = new_port->linked_idx; + enter_new_mode = (cmd >> 23) & 1; + + if (cam == UCSI_MULTI_LINKED_INDEX) { + if (enter_new_mode) { + port = con->port_alt; + for (i = 0; con->partner_altmode[i]; i++) { + partner = con->partner_altmode[i]; + if (partner->svid == new_port->svid) + break; + } + for (j = 0; port[j].svid; j++) + if (partner && port[j].svid == partner->svid && + port[j].mid & partner->vdo) + break; + cam = j; + new_port->active_idx = cam; + } else { + cam = new_port->active_idx; + } + } + cmd &= ~(0xff << 24); + cmd |= (cam << 24); + ctrl->raw_cmd = cmd; +} + static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, void *data, size_t size) { struct ucsi_control _ctrl; + struct ucsi_connector *con = ucsi->connector; u8 data_length; u16 error; int ret; + if (ctrl->alt.cmd == UCSI_SET_NEW_CAM && con->has_multiple_dp) + ucsi_update_set_new_cam_cmd(con, ctrl); + ret = ucsi_command(ucsi, ctrl); if (ret) goto err; @@ -364,10 +426,24 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) for (i = 0; i < max_altmodes;) { memset(alt, 0, sizeof(alt)); - UCSI_CMD_GET_ALTERNATE_MODES(ctrl, recipient, con->num, i, 1); - len = ucsi_run_command(con->ucsi, &ctrl, alt, sizeof(alt)); - if (len <= 0) - return len; + + if (recipient == UCSI_RECIPIENT_CON) { + if (con->has_multiple_dp) { + alt[0].svid = con->new_port_alt[i].svid; + alt[0].mid = con->new_port_alt[i].mid; + } else { + alt[0].svid = con->port_alt[i].svid; + alt[0].mid = con->port_alt[i].mid; +
Re: [RFC] usb: typec: ucsi: add support for separate DP altmode devices
Hi Heikki, > On Jun 28, 2019, at 3:47 AM, Heikki Krogerus > wrote: > >> On Thu, Jun 27, 2019 at 10:45:47PM -0700, Ajay Gupta wrote: >> CCGx controller used on NVIDIA GPU card has two separate display >> altmode for two DP pin assignments. UCSI specification doesn't >> prohibits using separate display altmode. >> >> Current UCSI Type-C framework expects only one display altmode for >> all DP pin assignment. This patch squashes two separate display >> altmode into single altmode to support controllers with separate >> display altmode. We first read all the alternate modes of connector >> and then run through it to know if there are separate display >> altmodes. If so, it prepares a new port altmode set after squashing >> two or more separate altmodes into one. > > I don't have any better ideas how to solve this at them moment. > Therefore, I don't have a problem with going forward with this. Thanks, please review the change and provide any comment. I will anyways repost this as a patch. Thanks > nvpublic > But > please note, that we are already at -rc6, so nothing is going to > happen for a while. I'll away for the next four weeks. > >> Signed-off-by: Ajay Gupta >> --- >> drivers/usb/typec/ucsi/ucsi.c | 191 +- >> drivers/usb/typec/ucsi/ucsi.h | 12 +++ >> 2 files changed, 199 insertions(+), 4 deletions(-) >> >> diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c >> index ba288b964dc8..491b5c925a90 100644 >> --- a/drivers/usb/typec/ucsi/ucsi.c >> +++ b/drivers/usb/typec/ucsi/ucsi.c >> @@ -39,8 +39,27 @@ >> */ >> #define UCSI_SWAP_TIMEOUT_MS5000 >> >> +static void ucsi_update_get_current_cam_cmd(struct ucsi_connector *con, >> +struct ucsi_data *data) >> +{ >> +u8 cam, new_cam; >> + >> +if (data->cci.data_length == 0x1) { >> +cam = data->message_in[0]; >> +new_cam = con->port_alt[cam].linked_idx; >> +data->message_in[0] = new_cam; >> +con->new_port_alt[new_cam].active_idx = cam; >> +} >> +} >> + >> static inline int ucsi_sync(struct ucsi *ucsi) >> { >> +struct ucsi_connector *con = ucsi->connector; >> +struct ucsi_data *data = ucsi->ppm->data; >> + >> +if (data->ctrl.alt.cmd == UCSI_GET_CURRENT_CAM && con->has_multiple_dp) >> +ucsi_update_get_current_cam_cmd(con, data); >> + >>if (ucsi->ppm && ucsi->ppm->sync) >>return ucsi->ppm->sync(ucsi->ppm); >>return 0; >> @@ -101,14 +120,57 @@ static int ucsi_ack(struct ucsi *ucsi, u8 ack) >>return ret; >> } >> >> +static void ucsi_update_set_new_cam_cmd(struct ucsi_connector *con, >> +struct ucsi_control *ctrl) >> +{ >> +struct new_ucsi_altmode *new_port, *port; >> +struct typec_altmode *partner = NULL; >> +u64 cmd; >> +u8 new_cam, cam; >> +bool enter_new_mode; >> +int i, j; >> + >> +cmd = ctrl->raw_cmd; >> +new_cam = (cmd >> 24) & 0xff; >> +new_port = &con->new_port_alt[new_cam]; >> +cam = new_port->linked_idx; >> +enter_new_mode = (cmd >> 23) & 1; >> + >> +if (cam == UCSI_MULTI_LINKED_INDEX) { >> +if (enter_new_mode) { >> +port = con->port_alt; >> +for (i = 0; con->partner_altmode[i]; i++) { >> +partner = con->partner_altmode[i]; >> +if (partner->svid == new_port->svid) >> +break; >> +} >> +for (j = 0; port[j].svid; j++) >> +if (partner && port[j].svid == partner->svid && >> +port[j].mid & partner->vdo) >> +break; >> +cam = j; >> +new_port->active_idx = cam; >> +} else { >> +cam = new_port->active_idx; >> +} >> +} >> +cmd &= ~(0xff << 24); >> +cmd |= (cam << 24); >> +ctrl->raw_cmd = cmd; >> +} >> + >> static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, >>void *data, size_t size) >> { >>struct ucsi_control _ctrl; >> +struct ucsi_connector *con = ucsi->connector; >>u8 data_length; >>u16 error; >>int ret; >>
[PATCH] usb: typec: ucsi: add support for separate DP altmode devices
From: Ajay Gupta CCGx controller used on NVIDIA GPU card has two separate display altmode for two DP pin assignments. UCSI specification doesn't prohibits using separate display altmode. Current UCSI Type-C framework expects only one display altmode for all DP pin assignment. This patch squashes two separate display altmode into single altmode to support controllers with separate display altmode. We first read all the alternate modes of connector and then run through it to know if there are separate display altmodes. If so, it prepares a new port altmode set after squashing two or more separate altmodes into one. Signed-off-by: Ajay Gupta --- Original discussion on this issue is at [1] RFC patch version is at [2] Changes from RFC : - Update logic to prioritixe pin E -> D -> C - Fix a bug in ucsi_sync() 1. https://marc.info/?l=linux-usb&m=154905866830998&w=2 2. https://marc.info/?l=linux-usb&m=156170104116037&w=2 drivers/usb/typec/ucsi/ucsi.c | 210 -- drivers/usb/typec/ucsi/ucsi.h | 12 ++ 2 files changed, 215 insertions(+), 7 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index ba288b964dc8..e673e95e9f6c 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -39,11 +39,35 @@ */ #define UCSI_SWAP_TIMEOUT_MS 5000 +static void ucsi_update_get_current_cam_cmd(struct ucsi_connector *con, + struct ucsi_data *data) +{ + u8 cam, new_cam; + + if (data->cci.data_length == 0x1) { + cam = data->message_in[0]; + new_cam = con->port_alt[cam].linked_idx; + data->message_in[0] = new_cam; + con->new_port_alt[new_cam].active_idx = cam; + } +} + static inline int ucsi_sync(struct ucsi *ucsi) { - if (ucsi->ppm && ucsi->ppm->sync) - return ucsi->ppm->sync(ucsi->ppm); - return 0; + struct ucsi_connector *con = ucsi->connector; + struct ucsi_data *data = ucsi->ppm->data; + int ret = 0; + + if (ucsi->ppm && ucsi->ppm->sync) { + ret = ucsi->ppm->sync(ucsi->ppm); + if (ret) + return ret; + } + + if (data->ctrl.alt.cmd == UCSI_GET_CURRENT_CAM && con->has_multiple_dp) + ucsi_update_get_current_cam_cmd(con, data); + + return ret; } static int ucsi_command(struct ucsi *ucsi, struct ucsi_control *ctrl) @@ -101,14 +125,65 @@ static int ucsi_ack(struct ucsi *ucsi, u8 ack) return ret; } +static void ucsi_update_set_new_cam_cmd(struct ucsi_connector *con, + struct ucsi_control *ctrl) +{ + struct new_ucsi_altmode *new_port, *port; + struct typec_altmode *alt = NULL; + u64 cmd; + u8 new_cam, cam, pin; + bool enter_new_mode; + int i, j, k = 0xff; + + cmd = ctrl->raw_cmd; + new_cam = (cmd >> 24) & 0xff; + new_port = &con->new_port_alt[new_cam]; + cam = new_port->linked_idx; + enter_new_mode = (cmd >> 23) & 1; + + if (cam == UCSI_MULTI_LINKED_INDEX) { + if (enter_new_mode) { + port = con->port_alt; + for (i = 0; con->partner_altmode[i]; i++) { + alt = con->partner_altmode[i]; + if (alt->svid == new_port->svid) + break; + } + for (j = 0; port[j].svid; j++) { + pin = DP_CONF_GET_PIN_ASSIGN(port[j].mid); + if (alt && port[j].svid == alt->svid && + (pin & DP_CONF_GET_PIN_ASSIGN(alt->vdo))) { + /* prioritize pin E->D->C */ + if (k == 0xff || (k != 0xff && pin > + DP_CONF_GET_PIN_ASSIGN(port[k].mid)) + ) { + k = j; + } + } + } + cam = k; + new_port->active_idx = cam; + } else { + cam = new_port->active_idx; + } + } + cmd &= ~(0xff << 24); + cmd |= (cam << 24); + ctrl->raw_cmd = cmd; +} + static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, void *data, size_t size) { struct ucsi_control _ctrl; + struct ucsi_connector *con = ucsi->connector; u8
[PATCH v2] usb: typec: ucsi: add support for separate DP altmode devices
From: Ajay Gupta CCGx controller used on NVIDIA GPU card has two separate display altmode for two DP pin assignments. UCSI specification doesn't prohibits using separate display altmode. Current UCSI Type-C framework expects only one display altmode for all DP pin assignment. This patch squashes two separate display altmode into single altmode to support controllers with separate display altmode. We first read all the alternate modes of connector and then run through it to know if there are separate display altmodes. If so, it prepares a new port altmode set after squashing two or more separate altmodes into one. Signed-off-by: Ajay Gupta --- Original discussion on this issue is at [1] Change from v1->v2 - Fix ucsi->ppm NULL check in ucsi_sync based on comment from an automated email from someone (I lost the email). 1. https://marc.info/?l=linux-usb&m=154905866830998&w=2 drivers/usb/typec/ucsi/ucsi.c | 212 -- drivers/usb/typec/ucsi/ucsi.h | 12 ++ 2 files changed, 217 insertions(+), 7 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index ba288b964dc8..68ea66fcaa0e 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -39,11 +39,37 @@ */ #define UCSI_SWAP_TIMEOUT_MS 5000 +static void ucsi_update_get_current_cam_cmd(struct ucsi_connector *con, + struct ucsi_data *data) +{ + u8 cam, new_cam; + + if (data->cci.data_length == 0x1) { + cam = data->message_in[0]; + new_cam = con->port_alt[cam].linked_idx; + data->message_in[0] = new_cam; + con->new_port_alt[new_cam].active_idx = cam; + } +} + static inline int ucsi_sync(struct ucsi *ucsi) { - if (ucsi->ppm && ucsi->ppm->sync) - return ucsi->ppm->sync(ucsi->ppm); - return 0; + struct ucsi_connector *con = ucsi->connector; + struct ucsi_data *data; + int ret = 0; + + if (ucsi->ppm && ucsi->ppm->sync) { + ret = ucsi->ppm->sync(ucsi->ppm); + if (ret) + return ret; + + data = ucsi->ppm->data; + if (data->ctrl.alt.cmd == UCSI_GET_CURRENT_CAM && + con->has_multiple_dp) + ucsi_update_get_current_cam_cmd(con, data); + } + + return ret; } static int ucsi_command(struct ucsi *ucsi, struct ucsi_control *ctrl) @@ -101,14 +127,65 @@ static int ucsi_ack(struct ucsi *ucsi, u8 ack) return ret; } +static void ucsi_update_set_new_cam_cmd(struct ucsi_connector *con, + struct ucsi_control *ctrl) +{ + struct new_ucsi_altmode *new_port, *port; + struct typec_altmode *alt = NULL; + u64 cmd; + u8 new_cam, cam, pin; + bool enter_new_mode; + int i, j, k = 0xff; + + cmd = ctrl->raw_cmd; + new_cam = (cmd >> 24) & 0xff; + new_port = &con->new_port_alt[new_cam]; + cam = new_port->linked_idx; + enter_new_mode = (cmd >> 23) & 1; + + if (cam == UCSI_MULTI_LINKED_INDEX) { + if (enter_new_mode) { + port = con->port_alt; + for (i = 0; con->partner_altmode[i]; i++) { + alt = con->partner_altmode[i]; + if (alt->svid == new_port->svid) + break; + } + for (j = 0; port[j].svid; j++) { + pin = DP_CONF_GET_PIN_ASSIGN(port[j].mid); + if (alt && port[j].svid == alt->svid && + (pin & DP_CONF_GET_PIN_ASSIGN(alt->vdo))) { + /* prioritize pin E->D->C */ + if (k == 0xff || (k != 0xff && pin > + DP_CONF_GET_PIN_ASSIGN(port[k].mid)) + ) { + k = j; + } + } + } + cam = k; + new_port->active_idx = cam; + } else { + cam = new_port->active_idx; + } + } + cmd &= ~(0xff << 24); + cmd |= (cam << 24); + ctrl->raw_cmd = cmd; +} + static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, void *data, size_t size) { struct ucsi_control _ctrl; + struct ucsi_connector *con = ucsi->connector; u8 da
[PATCH] usb: xhci: increase CRS timeout value
Some controllers take almost 55ms to complete controller restore state (CRS). There is no timeout limit mentioned in xhci specification so fix the issue by increasing the timeout limit to 55ms Signed-off-by: Ajay Gupta Signed-off-by: Nagaraj Annaiah --- drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8c8da2d..44b1af5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1044,7 +1044,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) command |= CMD_CRS; writel(command, &xhci->op_regs->command); if (xhci_handshake(&xhci->op_regs->status, - STS_RESTORE, 0, 10 * 1000)) { + STS_RESTORE, 0, 55 * 1000)) { xhci_warn(xhci, "WARN: xHC restore state timeout\n"); spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2] usb: xhci: increase CRS timeout value
Some controllers take almost 55ms to complete controller restore state (CRS). There is no timeout limit mentioned in xhci specification so fixing the issue by increasing the timeout limit to 55ms Signed-off-by: Ajay Gupta Signed-off-by: Nagaraj Annaiah --- drivers/usb/host/xhci.c | 7 ++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8c8da2d..fa7bd71 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1043,8 +1043,13 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) command = readl(&xhci->op_regs->command); command |= CMD_CRS; writel(command, &xhci->op_regs->command); + /* Some controllers take upto 55+ ms to complete +* the controller restore so setting the timeout to +* 100ms. Xhci specification doesn't mention any +* timeout value. +*/ if (xhci_handshake(&xhci->op_regs->status, - STS_RESTORE, 0, 10 * 1000)) { + STS_RESTORE, 0, 100 * 1000)) { xhci_warn(xhci, "WARN: xHC restore state timeout\n"); spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v3] usb: xhci: increase CRS timeout value
Some controllers take almost 55ms to complete controller restore state (CRS). There is no timeout limit mentioned in xhci specification so fixing the issue by increasing the timeout limit to 100ms Signed-off-by: Ajay Gupta Signed-off-by: Nagaraj Annaiah --- Changes from v2->v3 Updated commit message for timeout value to 100ms Changes from v1->v2 Updated timeout value from 55 to 100ms and added a comment. drivers/usb/host/xhci.c | 7 ++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8c8da2d..fa7bd71 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1043,8 +1043,13 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) command = readl(&xhci->op_regs->command); command |= CMD_CRS; writel(command, &xhci->op_regs->command); + /* Some controllers take upto 55+ ms to complete +* the controller restore so setting the timeout to +* 100ms. Xhci specification doesn't mention any +* timeout value. +*/ if (xhci_handshake(&xhci->op_regs->status, - STS_RESTORE, 0, 10 * 1000)) { + STS_RESTORE, 0, 100 * 1000)) { xhci_warn(xhci, "WARN: xHC restore state timeout\n"); spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2] USB: serial: ftdi_sio: Add MTP NVM support
Hi Loic On 6/21/18, Loic Poulain wrote: > Most of FTDI's devices have an EEPROM which records FTDI devices > configuration setting (e.g. the VID, PID, I/O config...) and user > data. FT230R chip integrates a 128-byte eeprom, FT230X a 2048-byte > eeprom... > > This patch adds support for FTDI EEPROM read/write via USB control > transfers and register a new nvm device to the nvmem core. > > This permits to expose the eeprom as a sysfs file, allowing userspace > to read/modify FTDI configuration and its user data without having to > rely on a specific userspace USB driver. > > Moreover, any upcoming new tentative to add CBUS GPIO support could > integrate CBUS EEPROM configuration reading in order to determine > which of the CBUS pins are available as GPIO. > > Signed-off-by: Loic Poulain > --- > v2: Use ifdef instead of IS_ENABLED > error message in case of nvmem registering failure > Fix space/tab in Kconfig > > drivers/usb/serial/Kconfig| 13 - > drivers/usb/serial/ftdi_sio.c | 111 > ++ > drivers/usb/serial/ftdi_sio.h | 28 +++ > 3 files changed, 151 insertions(+), 1 deletion(-) > > diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig > index 533f127..f05af5f 100644 > --- a/drivers/usb/serial/Kconfig > +++ b/drivers/usb/serial/Kconfig > @@ -153,7 +153,7 @@ config USB_SERIAL_CYPRESS_M8 > > Supported microcontrollers in the CY4601 family are: > CY7C63741 CY7C63742 CY7C63743 CY7C64013 > - > + There is no change here so please remove it. > To compile this driver as a module, choose M here: the > module will be called cypress_m8. > > @@ -181,6 +181,17 @@ config USB_SERIAL_FTDI_SIO > To compile this driver as a module, choose M here: the > module will be called ftdi_sio. > > +config USB_SERIAL_FTDI_SIO_NVMEM > + bool "FTDI MTP non-volatile memory support" > + depends on USB_SERIAL_FTDI_SIO > + select NVMEM > + default y > + help > + Say yes here to add support for the MTP non-volatile memory > + present on FTDI. Most of FTDI's devices have an EEPROM which > + records FTDI device's configuration setting as well as user > + data. > + > config USB_SERIAL_VISOR > tristate "USB Handspring Visor / Palm m50x / Sony Clie Driver" > help > diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c > index 7ea221d..9e242e8 100644 > --- a/drivers/usb/serial/ftdi_sio.c > +++ b/drivers/usb/serial/ftdi_sio.c > @@ -40,6 +40,7 @@ > #include > #include > #include > +#include > #include "ftdi_sio.h" > #include "ftdi_sio_ids.h" > > @@ -73,6 +74,8 @@ struct ftdi_private { > unsigned int latency; /* latency setting in use */ > unsigned short max_packet_size; > struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() > and change_speed() */ > + > + struct nvmem_device *eeprom; > }; > > /* struct ftdi_sio_quirk is used by devices requiring special attention. > */ > @@ -1529,6 +1532,104 @@ static int get_lsr_info(struct usb_serial_port > *port, > return 0; > } > > +#ifdef CONFIG_USB_SERIAL_FTDI_SIO_NVMEM > + > +static int write_eeprom(void *priv, unsigned int off, void *_val, size_t > bytes) > +{ > + struct usb_serial_port *port = priv; > + struct usb_serial *serial = port->serial; > + struct usb_device *udev = serial->dev; > + unsigned char *buf = _val; > + > + while (bytes) { /* bytes value is always a multiple of 2 */ We should add check that 'bytes' is always multiple of 2 otherwise in case its not then there will be memory overrun due to buf[1] access below. while (bytes / 2) { } > + uint16_t val; > + int rv; > + > + val = buf[0] + (buf[1] << 8); > + > + rv = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), > + FTDI_SIO_WRITE_EEPROM_REQUEST, > + FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE, > + val, off / 2, NULL, 0, WDR_TIMEOUT); > + if (rv < 0) > + return rv; > + > + off += 2; > + buf += 2; > + bytes -= 2; > + } > + > + return 0; > +} > + > +static int read_eeprom(void *priv, unsigned int off, void *val, size_t > bytes) > +{ > + struct usb_serial_port *port = priv; > + struct usb_serial *serial = port->serial; > + struct usb_device *udev = serial->dev; > + unsigned char *buf = val; > + > + while (bytes) { /* bytes value is always a multiple of 2 */ same here > + int rv; > + > + rv = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), > + FTDI_SIO_READ_EEPROM_REQUEST, > + FTDI_SIO_READ_EEPROM_REQUEST_TYPE, > + 0, off / 2, buf, 2, WDR_TIMEOUT); > + if (rv < 0) > +
Re: [PATCH v6] USB: serial: ftdi_sio: Add MTP NVM support
Hi Loic. Thanks for fixing comments. Looks good to me now. On 6/26/18, Loic Poulain wrote: > Most of FTDI's devices have an EEPROM which records FTDI devices > configuration setting (e.g. the VID, PID, I/O config...) and user > data. For example, FT232R and FTX chips have 128-byte and 2048-byte > internal EEPROM respectively. > > This patch adds support for FTDI EEPROM read/write via USB control > transfers and register a new nvm device to the nvmem core. > > This permits to expose the EEPROM as a sysfs file, allowing userspace > to read/modify FTDI configuration and its user data without having to > rely on a specific userspace USB driver. > > Moreover, any upcoming new tentative to add CBUS GPIO support could > integrate CBUS EEPROM configuration reading in order to determine > which of the CBUS pins are available as GPIO. > > Reviewed-by: Andy Shevchenko > Signed-off-by: Loic Poulain Reviewed-by: Ajay Gupta > --- > v2: Use ifdef instead of IS_ENABLED > error message in case of nvmem registering failure > Fix space/tab in Kconfig > v3: Make nvmem a child of the usb dev instead of the serial port > Add macros defining eeprom sizes > Check read/write size is a nultiple of the eeprom word-size > Remove useless change in Kconfig > v4: Reword commit message > Remove default-yes from Kconfig > Change includes ordering > Use default linux size defines > Use get_unaligned_le16 helper > Prepend EEPROM functions with ftdi_ > Error message in ftdi_eeprom_register() > v5: Fix missing linux/sizes header > v6: Ordering new headers insertion > Remove unecessary additional buf pointer from read/write_eeprom > > drivers/usb/serial/Kconfig| 10 > drivers/usb/serial/ftdi_sio.c | 118 > ++ > drivers/usb/serial/ftdi_sio.h | 28 ++ > 3 files changed, 156 insertions(+) > > diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig > index 533f127..5747562 100644 > --- a/drivers/usb/serial/Kconfig > +++ b/drivers/usb/serial/Kconfig > @@ -181,6 +181,16 @@ config USB_SERIAL_FTDI_SIO > To compile this driver as a module, choose M here: the > module will be called ftdi_sio. > > +config USB_SERIAL_FTDI_SIO_NVMEM > + bool "FTDI MTP non-volatile memory support" > + depends on USB_SERIAL_FTDI_SIO > + select NVMEM > + help > + Say yes here to add support for the MTP non-volatile memory > + present on FTDI. Most of FTDI's devices have an EEPROM which > + records FTDI device's configuration setting as well as user > + data. > + > config USB_SERIAL_VISOR > tristate "USB Handspring Visor / Palm m50x / Sony Clie Driver" > help > diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c > index 7ea221d..34daa8c 100644 > --- a/drivers/usb/serial/ftdi_sio.c > +++ b/drivers/usb/serial/ftdi_sio.c > @@ -34,12 +34,17 @@ > #include > #include > #include > +#include > +#include > #include > #include > #include > #include > #include > #include > + > +#include > + > #include "ftdi_sio.h" > #include "ftdi_sio_ids.h" > > @@ -73,6 +78,8 @@ struct ftdi_private { > unsigned int latency; /* latency setting in use */ > unsigned short max_packet_size; > struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() > and change_speed() */ > + > + struct nvmem_device *eeprom; > }; > > /* struct ftdi_sio_quirk is used by devices requiring special attention. > */ > @@ -1529,6 +1536,110 @@ static int get_lsr_info(struct usb_serial_port > *port, > return 0; > } > > +#ifdef CONFIG_USB_SERIAL_FTDI_SIO_NVMEM > + > +static int ftdi_write_eeprom(void *priv, unsigned int off, void *val, > + size_t bytes) > +{ > + struct usb_serial_port *port = priv; > + struct usb_device *udev = port->serial->dev; > + > + if (bytes % 2) /* 16-bit eeprom */ > + return -EINVAL; > + > + while (bytes) { > + int rv; > + > + rv = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), > + FTDI_SIO_WRITE_EEPROM_REQUEST, > + FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE, > + get_unaligned_le16(val), off / 2, NULL, > + 0, WDR_TIMEOUT); > + if (rv < 0) > + return rv; > + > + off += 2; > + val += 2; > + b
[PATCH 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C controller which can be accessed over I2C. This driver add I2C bus driver to communicate with Type-C controller. I2C client driver will be part of USB Type-C UCSI driver. Signed-off-by: Ajay Gupta --- Documentation/i2c/busses/i2c-gpu | 18 ++ MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-gpu.c | 493 +++ 5 files changed, 528 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-gpu create mode 100644 drivers/i2c/busses/i2c-gpu.c diff --git a/Documentation/i2c/busses/i2c-gpu b/Documentation/i2c/busses/i2c-gpu new file mode 100644 index 000..873ba34 --- /dev/null +++ b/Documentation/i2c/busses/i2c-gpu @@ -0,0 +1,18 @@ +Kernel driver i2c-gpu + +Datasheet: not publicly available. + +Authors: + Ajay Gupta + +Description +--- + +i2c-gpu is a driver for I2C controller included in NVIDIA Turing and later +GPUs and it is used to communicate with Type-C controller on GPUs. + +If your 'lspci -v' listing shows something like the following, + +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) + +then this driver should support the I2C controller of your GPU. diff --git a/MAINTAINERS b/MAINTAINERS index efb08d7..ee137de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6781,6 +6781,13 @@ L: linux-a...@vger.kernel.org S: Maintained F: drivers/i2c/i2c-core-acpi.c +I2C CONTROLLER DRIVER FOR NVIDIA GPU +M: Ajay Gupta +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/i2c/busses/i2c-gpu +F: drivers/i2c/busses/i2c-gpu.c + I2C MUXES M: Peter Rosin L: linux-...@vger.kernel.org diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 451d4ae..ff8b2d4 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 This driver can also be built as a module. If so, the module will be called i2c-nforce2-s4985. +config I2C_GPU + tristate "NVIDIA GPU I2C controller" + depends on PCI + help + If you say yes to this option, support will be included for the + NVIDIA GPU I2C controller which is used to communicate with the GPU's + Type-C controller. This driver can also be built as a module called + i2c-gpu.ko. + config I2C_SIS5595 tristate "SiS 5595" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af..15d2894 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_FSI) += i2c-fsi.o +obj-$(CONFIG_I2C_GPU) += i2c-gpu.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-gpu.c b/drivers/i2c/busses/i2c-gpu.c new file mode 100644 index 000..0fd2944 --- /dev/null +++ b/drivers/i2c/busses/i2c-gpu.c @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvidia GPU I2C controller Driver + * + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* STATUS definitions */ +#define STATUS_SUCCESS 0 +#define STATUS_UNSUCCESSFUL0x8000UL +#define STATUS_TIMEOUT 0x8001UL +#define STATUS_IO_DEVICE_ERROR 0x8002UL +#define STATUS_IO_TIMEOUT 0x8004UL +#define STATUS_IO_PREEMPTED0x8008UL + +/* Cypress Type-C controllers (CCGx) device */ +#define CCGX_I2C_DEV_ADDRESS 0x08 + +/* I2C definitions */ +#define I2C_MST_CNTL 0x00 +#define I2C_MST_CNTL_GEN_START (1 << 0) +#define I2C_MST_CNTL_GEN_STOP (1 << 1) +#define I2C_MST_CNTL_CMD_NONE (0 << 2) +#define I2C_MST_CNTL_CMD_READ (1 << 2) +#define I2C_MST_CNTL_CMD_WRITE (2 << 2) +#define I2C_MST_CNTL_CMD_RESET (3 << 2) +#define I2C_MST_CNTL_GEN_RAB (1 << 4) +#define I2C_MST_CNTL_BURST_SIZE_SHIFT (6) +#define I2C_MST_CNTL_GEN_NACK (1 << 28) +#define I2C_MST_CNTL_STATUS(3 << 29) +#define I2C_MST_CNTL_STATUS_OKAY (0 << 29) +#define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29) +#define I2C_MST_CNTL_STATUS_TIMEOUT(2 << 29) +#define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29) +#define I2C_MST_CNTL_CYCLE_TRIGGER (1 << 31) + +#define I2C_M
[PATCH 2/2] usb: typec: ucsi: add support for Cypress CCGx
Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller over I2C interface. This UCSI I2C driver uses I2C bus driver interface for communicating with Type-C controller. Signed-off-by: Ajay Gupta --- drivers/usb/typec/ucsi/Kconfig| 10 + drivers/usb/typec/ucsi/Makefile | 2 + drivers/usb/typec/ucsi/ucsi_i2c_ccg.c | 591 ++ 3 files changed, 603 insertions(+) create mode 100644 drivers/usb/typec/ucsi/ucsi_i2c_ccg.c diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index e36d6c7..5136aeb 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -23,6 +23,16 @@ config TYPEC_UCSI if TYPEC_UCSI +config UCSI_I2C_CCG +tristate "UCSI I2C Interface Driver for Cypress CCGx" + depends on I2C_GPU +help + This driver enables UCSI support on NVIDIA GPUs that expose a + Cypress CCGx Type-C controller over I2C interface. + + To compile the driver as a module, choose M here: the module will be + called ucsi_i2c_ccg.ko. + config UCSI_ACPI tristate "UCSI ACPI Interface Driver" depends on ACPI diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 7afbea5..4439482 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -8,3 +8,5 @@ typec_ucsi-y:= ucsi.o typec_ucsi-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_UCSI_ACPI)+= ucsi_acpi.o + +obj-$(CONFIG_UCSI_I2C_CCG) += ucsi_i2c_ccg.o diff --git a/drivers/usb/typec/ucsi/ucsi_i2c_ccg.c b/drivers/usb/typec/ucsi/ucsi_i2c_ccg.c new file mode 100644 index 000..587e3f8 --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_i2c_ccg.c @@ -0,0 +1,591 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI I2C driver for Cypress CCGx Type-C controller + * + * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c + */ +#include +#include +#include +#include +#include +#include +#include "ucsi.h" + +struct ucsi_i2c_ccg { + struct device *dev; + struct ucsi *ucsi; + struct ucsi_ppm ppm; + struct i2c_client *client; + int irq; + bool wake_enabled; + unsigned char ver; +}; + +#define CCGX_I2C_RAB_DEVICE_MODE 0xU +#define CCGX_I2C_RAB_BOOT_MODE_REASON 0x0001U +#define CCGX_I2C_RAB_READ_SILICON_ID 0x0002U +#define CCGX_I2C_RAB_INTR_REG 0x0006U +#define CCGX_I2C_RAB_RESET 0x0008U +#define CCGX_I2C_RAB_READ_ALL_VERSION 0x0010U +#define CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER \ + (CCGX_I2C_RAB_READ_ALL_VERSION + 0x00) +#define CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER_BASE \ + (CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER + 0) +#define CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER_FW \ + (CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER + 4) +#define CCGX_I2C_RAB_READ_ALL_VERSION_APP \ + (CCGX_I2C_RAB_READ_ALL_VERSION + 0x08) +#define CCGX_I2C_RAB_READ_ALL_VERSION_APP_BASE \ + (CCGX_I2C_RAB_READ_ALL_VERSION_APP + 0) +#define CCGX_I2C_RAB_READ_ALL_VERSION_APP_FW \ + (CCGX_I2C_RAB_READ_ALL_VERSION_APP + 4) +#define CCGX_I2C_RAB_FW2_VERSION 0x0020U +#define CCGX_I2C_RAB_PDPORT_ENABLE 0x002CU +#define CCGX_I2C_RAB_UCSI_STATUS 0x0038U +#define CCGX_I2C_RAB_UCSI_CONTROL 0x0039U +#define CCGX_I2C_RAB_UCSI_CONTROL_STOP 0x2U +#define CCGX_I2C_RAB_UCSI_CONTROL_START0x1U +#define CCGX_I2C_RAB_HPI_VERSION 0x003CU +#define CCGX_I2C_RAB_RESPONSE_REG 0x007EU +#define CCGX_I2C_RAB_DM_CONTROL_1 0x1000U +#define CCGX_I2C_RAB_WRITE_DATA_MEMORY_1 0x1800U +#define CCGX_I2C_RAB_DM_CONTROL_2 0x2000U +#define CCGX_I2C_RAB_WRITE_DATA_MEMORY_2 0x2800U +#define CCGX_I2C_RAB_UCSI_DATA_BLOCK 0xf000U + +#define CCGX_I2C_RAB_RESPONSE_REG_RESET_COMPLETE 0x80 + +static int ccg_read(struct ucsi_i2c_ccg *ui, u16 rab, u8 *data, u32 len) +{ + struct device *dev = ui->dev; + struct i2c_client *client = ui->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0x0, + .len= 0x2, + .buf= buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .buf= data, + }, + }; +
[PATCH v2 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C controller which can be accessed over I2C. This driver add I2C bus driver to communicate with Type-C controller. I2C client driver will be part of USB Type-C UCSI driver. Signed-off-by: Ajay Gupta --- Changes from v1 -> v2: None Documentation/i2c/busses/i2c-gpu | 18 ++ MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-gpu.c | 493 +++ 5 files changed, 528 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-gpu create mode 100644 drivers/i2c/busses/i2c-gpu.c diff --git a/Documentation/i2c/busses/i2c-gpu b/Documentation/i2c/busses/i2c-gpu new file mode 100644 index 000..873ba34 --- /dev/null +++ b/Documentation/i2c/busses/i2c-gpu @@ -0,0 +1,18 @@ +Kernel driver i2c-gpu + +Datasheet: not publicly available. + +Authors: + Ajay Gupta + +Description +--- + +i2c-gpu is a driver for I2C controller included in NVIDIA Turing and later +GPUs and it is used to communicate with Type-C controller on GPUs. + +If your 'lspci -v' listing shows something like the following, + +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) + +then this driver should support the I2C controller of your GPU. diff --git a/MAINTAINERS b/MAINTAINERS index b2fcd1c..e99f8a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6796,6 +6796,13 @@ L: linux-a...@vger.kernel.org S: Maintained F: drivers/i2c/i2c-core-acpi.c +I2C CONTROLLER DRIVER FOR NVIDIA GPU +M: Ajay Gupta +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/i2c/busses/i2c-gpu +F: drivers/i2c/busses/i2c-gpu.c + I2C MUXES M: Peter Rosin L: linux-...@vger.kernel.org diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 451d4ae..ff8b2d4 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 This driver can also be built as a module. If so, the module will be called i2c-nforce2-s4985. +config I2C_GPU + tristate "NVIDIA GPU I2C controller" + depends on PCI + help + If you say yes to this option, support will be included for the + NVIDIA GPU I2C controller which is used to communicate with the GPU's + Type-C controller. This driver can also be built as a module called + i2c-gpu.ko. + config I2C_SIS5595 tristate "SiS 5595" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af..15d2894 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_FSI) += i2c-fsi.o +obj-$(CONFIG_I2C_GPU) += i2c-gpu.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-gpu.c b/drivers/i2c/busses/i2c-gpu.c new file mode 100644 index 000..0fd2944 --- /dev/null +++ b/drivers/i2c/busses/i2c-gpu.c @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvidia GPU I2C controller Driver + * + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* STATUS definitions */ +#define STATUS_SUCCESS 0 +#define STATUS_UNSUCCESSFUL0x8000UL +#define STATUS_TIMEOUT 0x8001UL +#define STATUS_IO_DEVICE_ERROR 0x8002UL +#define STATUS_IO_TIMEOUT 0x8004UL +#define STATUS_IO_PREEMPTED0x8008UL + +/* Cypress Type-C controllers (CCGx) device */ +#define CCGX_I2C_DEV_ADDRESS 0x08 + +/* I2C definitions */ +#define I2C_MST_CNTL 0x00 +#define I2C_MST_CNTL_GEN_START (1 << 0) +#define I2C_MST_CNTL_GEN_STOP (1 << 1) +#define I2C_MST_CNTL_CMD_NONE (0 << 2) +#define I2C_MST_CNTL_CMD_READ (1 << 2) +#define I2C_MST_CNTL_CMD_WRITE (2 << 2) +#define I2C_MST_CNTL_CMD_RESET (3 << 2) +#define I2C_MST_CNTL_GEN_RAB (1 << 4) +#define I2C_MST_CNTL_BURST_SIZE_SHIFT (6) +#define I2C_MST_CNTL_GEN_NACK (1 << 28) +#define I2C_MST_CNTL_STATUS(3 << 29) +#define I2C_MST_CNTL_STATUS_OKAY (0 << 29) +#define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29) +#define I2C_MST_CNTL_STATUS_TIMEOUT(2 << 29) +#define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29) +#define I2C_MST_CNTL_CYCLE_TRIGGER
[PATCH v2 2/2] usb: typec: ucsi: add support for Cypress CCGx
Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller over I2C interface. This UCSI I2C driver uses I2C bus driver interface for communicating with Type-C controller. Signed-off-by: Ajay Gupta --- Changes from v1 -> v2: Fixed identation in drivers/usb/typec/ucsi/Kconfig drivers/usb/typec/ucsi/Kconfig| 10 + drivers/usb/typec/ucsi/Makefile | 2 + drivers/usb/typec/ucsi/ucsi_i2c_ccg.c | 591 ++ 3 files changed, 603 insertions(+) create mode 100644 drivers/usb/typec/ucsi/ucsi_i2c_ccg.c diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index e36d6c7..0ce9d48 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -23,6 +23,16 @@ config TYPEC_UCSI if TYPEC_UCSI +config UCSI_I2C_CCG + tristate "UCSI I2C Interface Driver for Cypress CCGx" + depends on I2C_GPU + help + This driver enables UCSI support on NVIDIA GPUs that expose a + Cypress CCGx Type-C controller over I2C interface. + + To compile the driver as a module, choose M here: the module will be + called ucsi_i2c_ccg.ko. + config UCSI_ACPI tristate "UCSI ACPI Interface Driver" depends on ACPI diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 7afbea5..4439482 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -8,3 +8,5 @@ typec_ucsi-y:= ucsi.o typec_ucsi-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_UCSI_ACPI)+= ucsi_acpi.o + +obj-$(CONFIG_UCSI_I2C_CCG) += ucsi_i2c_ccg.o diff --git a/drivers/usb/typec/ucsi/ucsi_i2c_ccg.c b/drivers/usb/typec/ucsi/ucsi_i2c_ccg.c new file mode 100644 index 000..587e3f8 --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_i2c_ccg.c @@ -0,0 +1,591 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI I2C driver for Cypress CCGx Type-C controller + * + * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c + */ +#include +#include +#include +#include +#include +#include +#include "ucsi.h" + +struct ucsi_i2c_ccg { + struct device *dev; + struct ucsi *ucsi; + struct ucsi_ppm ppm; + struct i2c_client *client; + int irq; + bool wake_enabled; + unsigned char ver; +}; + +#define CCGX_I2C_RAB_DEVICE_MODE 0xU +#define CCGX_I2C_RAB_BOOT_MODE_REASON 0x0001U +#define CCGX_I2C_RAB_READ_SILICON_ID 0x0002U +#define CCGX_I2C_RAB_INTR_REG 0x0006U +#define CCGX_I2C_RAB_RESET 0x0008U +#define CCGX_I2C_RAB_READ_ALL_VERSION 0x0010U +#define CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER \ + (CCGX_I2C_RAB_READ_ALL_VERSION + 0x00) +#define CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER_BASE \ + (CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER + 0) +#define CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER_FW \ + (CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER + 4) +#define CCGX_I2C_RAB_READ_ALL_VERSION_APP \ + (CCGX_I2C_RAB_READ_ALL_VERSION + 0x08) +#define CCGX_I2C_RAB_READ_ALL_VERSION_APP_BASE \ + (CCGX_I2C_RAB_READ_ALL_VERSION_APP + 0) +#define CCGX_I2C_RAB_READ_ALL_VERSION_APP_FW \ + (CCGX_I2C_RAB_READ_ALL_VERSION_APP + 4) +#define CCGX_I2C_RAB_FW2_VERSION 0x0020U +#define CCGX_I2C_RAB_PDPORT_ENABLE 0x002CU +#define CCGX_I2C_RAB_UCSI_STATUS 0x0038U +#define CCGX_I2C_RAB_UCSI_CONTROL 0x0039U +#define CCGX_I2C_RAB_UCSI_CONTROL_STOP 0x2U +#define CCGX_I2C_RAB_UCSI_CONTROL_START0x1U +#define CCGX_I2C_RAB_HPI_VERSION 0x003CU +#define CCGX_I2C_RAB_RESPONSE_REG 0x007EU +#define CCGX_I2C_RAB_DM_CONTROL_1 0x1000U +#define CCGX_I2C_RAB_WRITE_DATA_MEMORY_1 0x1800U +#define CCGX_I2C_RAB_DM_CONTROL_2 0x2000U +#define CCGX_I2C_RAB_WRITE_DATA_MEMORY_2 0x2800U +#define CCGX_I2C_RAB_UCSI_DATA_BLOCK 0xf000U + +#define CCGX_I2C_RAB_RESPONSE_REG_RESET_COMPLETE 0x80 + +static int ccg_read(struct ucsi_i2c_ccg *ui, u16 rab, u8 *data, u32 len) +{ + struct device *dev = ui->dev; + struct i2c_client *client = ui->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0x0, + .len= 0x2, + .buf= buf, + }, + { + .addr = client->addr, + .flags = I2C
RE: [PATCH v2 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Hi Andy, > > Latest NVIDIA GPU card has USB Type-C interface. There is a > > Type-C controller which can be accessed over I2C. > > > > This driver add I2C bus driver to communicate with Type-C controller. > > I2C client driver will be part of USB Type-C UCSI driver. > > > drivers/i2c/busses/i2c-gpu.c | 493 > +++ > > Can we got more better name, which includes vendor and/or model of the I2C > host? Sure will change to i2c-nvidia-gpu.c > > +/* STATUS definitions */ > > +#define STATUS_SUCCESS 0 > > +#define STATUS_UNSUCCESSFUL0x8000UL > > +#define STATUS_TIMEOUT 0x8001UL > > +#define STATUS_IO_DEVICE_ERROR 0x8002UL > > +#define STATUS_IO_TIMEOUT 0x8004UL > > +#define STATUS_IO_PREEMPTED0x8008UL > > Looks slightly different from my point of view, something like > > /* Bit 31 shows error condition while LSB encodes the error code */ > STATUS_TIMEOUT BIT(0) > ... > STATUS_ERROR BIT(31) Will fix. > > + dev_dbg(dev, "%s: %p (I2C_MST_HYBRID_PADCTL) <- %08x", __func__, > > + (gdev->regs + I2C_MST_HYBRID_PADCTL), val); > > Parens are redundant, __func__ is redundant. Will fix. > > + dev_dbg(dev, "%s: %p (I2C_MST_I2C0_TIMING) <- %08x", __func__, > > + gdev->regs + I2C_MST_I2C0_TIMING, val); > > Ditto. Check your debug messages, and perheps even drop some. Will fix. > > +static u32 i2c_check_status(struct gpu_i2c_dev *gdev) > > +{ > > > + while (time_is_after_jiffies(target)) { > > + } > > For functions like this better to get in a form > do { > } while(). Ok, will fix. > There is no guarantee that it runs even once in your case. > > > + dev_err(dev, "%si2c timeout", __func__); > > No space? Ok, will fix. > > > + val = readl(gdev->regs + I2C_MST_DATA); > > + switch (len) { > > + case 1: > > + data[0] = (val >> 0) & 0xff; > > + break; > > + case 2: > > + data[0] = (val >> 8) & 0xff; > > + data[1] = (val >> 0) & 0xff; > > + break; > > + case 3: > > + data[0] = (val >> 16) & 0xff; > > + data[1] = (val >> 8) & 0xff; > > + data[2] = (val >> 0) & 0xff; > > + break; > > + case 4: > > + data[0] = (val >> 24) & 0xff; > > + data[1] = (val >> 16) & 0xff; > > + data[2] = (val >> 8) & 0xff; > > + data[3] = (val >> 0) & 0xff; > > + break; > > Redundant & 0xff. > We have get_unaligned*(), put_unaligned*() and many variations of > cpu_to_Xe*() and Xe*_to_cpu(). Ok, will fix. > > > + u32 val = 0; > > Redundant assignment. Ok, will fix. > > > + val = addr << I2C_MST_ADDR_DAB; > > > + val = 0; > > Ditto. What's wrong with assign value below directly? > > > + val |= I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_CMD_NONE | > > + I2C_MST_CNTL_GEN_NACK; > > > + u32 val = 0; > > Check your code for these kind of style mistakes. > > > +/* gdev i2c adapter */ > > Pointless. > > > +static int gpu_i2c_master_xfer(struct i2c_adapter *adap, > > + struct i2c_msg *msgs, int num) > > +{ > > + struct gpu_i2c_dev *gdev = i2c_get_adapdata(adap); > > + struct device *dev = &gdev->pci_dev->dev; > > + int retry1b = 10; > > + u32 status; > > + int i, j; > > > + goto exit; > > +exit_stop: > > + status = i2c_manual_stop(gdev); > > + if (status != STATUS_SUCCESS) > > + dev_err(dev, "i2c_manual_stop failed %x", status); > > +exit: > > + mutex_unlock(&gdev->mutex); > > + return i; > > +} > > Ouch! Besides many small style issues and redundancy (like __LINE__), > this function needs to be refactored to few smaller and readable ones. Ok, will fix. > > > +#define PCI_CLASS_SERIAL_UNKNOWN 0x0c80 > > > +/* pci driver */ > > Pointless. Ok, will fix. > > > +static const struct pci_device_id gpu_i2c_ids[] = { > > + { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, > > + PCI_CLASS_SERIAL_UNKNOWN << 8, 0xff00}, > > Are you sure?! Yes, we want to identify using vendor ID and class code. Currently there is no class code defined for UCSI device over PCI so using UNKNOWN. > > > + { }, > > Terminator line better w/o comma. Ok, will fix. > > > +}; > > +MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); > > > +static int gpu_i2c_probe(struct pci_dev *dev, const struct pci_device_id > *id) > > +{ > > + struct gpu_i2c_dev *gdev; > > + int status; > > + > > > + dev_info(&dev->dev, > > + "dev %p id %08x %08x sub %08x %08x class %08x %08x\n", > > + dev, id->vendor, id->device, id->subvendor, id->subdevice, > > + id->class, id->class_mask); > > Useless. We have PCI core printed this information out sever
RE: [v2,1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Hi Thierry, > > Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C > > controller which can be accessed over I2C. > > > > This driver add I2C bus driver to communicate with Type-C controller. > > I2C client driver will be part of USB Type-C UCSI driver. > > > > Signed-off-by: Ajay Gupta > > --- > > Changes from v1 -> v2: None > > > > Documentation/i2c/busses/i2c-gpu | 18 ++ > > MAINTAINERS | 7 + > > drivers/i2c/busses/Kconfig | 9 + > > drivers/i2c/busses/Makefile | 1 + > > drivers/i2c/busses/i2c-gpu.c | 493 > +++ > > 5 files changed, 528 insertions(+) > > create mode 100644 Documentation/i2c/busses/i2c-gpu create mode > > 100644 drivers/i2c/busses/i2c-gpu.c > > Hi Ajay, > > I think this looks pretty good. A couple of minor, mostly nit-picky, comments > below. > > > > > diff --git a/Documentation/i2c/busses/i2c-gpu > > b/Documentation/i2c/busses/i2c-gpu > > new file mode 100644 > > index 000..873ba34 > > --- /dev/null > > +++ b/Documentation/i2c/busses/i2c-gpu > > I think this is too generic. Maybe use something like i2c-nvidia-gpu here and > everywhere else, to make it explicit that this is for NVIDIA GPUs rather than > GPUs in general. ok > > @@ -0,0 +1,18 @@ > > +Kernel driver i2c-gpu > > + > > +Datasheet: not publicly available. > > + > > +Authors: > > + Ajay Gupta > > + > > +Description > > +--- > > + > > +i2c-gpu is a driver for I2C controller included in NVIDIA Turing and > > +later GPUs and it is used to communicate with Type-C controller on GPUs. > > + > > +If your 'lspci -v' listing shows something like the following, > > + > > +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 > > +(rev a1) > > + > > +then this driver should support the I2C controller of your GPU. > > diff --git a/MAINTAINERS b/MAINTAINERS index b2fcd1c..e99f8a2 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -6796,6 +6796,13 @@ L: linux-a...@vger.kernel.org > > S: Maintained > > F: drivers/i2c/i2c-core-acpi.c > > > > +I2C CONTROLLER DRIVER FOR NVIDIA GPU > > +M: Ajay Gupta > > +L: linux-...@vger.kernel.org > > +S: Maintained > > +F: Documentation/i2c/busses/i2c-gpu > > +F: drivers/i2c/busses/i2c-gpu.c > > + > > I2C MUXES > > M: Peter Rosin > > L: linux-...@vger.kernel.org > > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig > > index 451d4ae..ff8b2d4 100644 > > --- a/drivers/i2c/busses/Kconfig > > +++ b/drivers/i2c/busses/Kconfig > > @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 > > This driver can also be built as a module. If so, the module > > will be called i2c-nforce2-s4985. > > > > +config I2C_GPU > > + tristate "NVIDIA GPU I2C controller" > > + depends on PCI > > + help > > + If you say yes to this option, support will be included for the > > + NVIDIA GPU I2C controller which is used to communicate with the > GPU's > > + Type-C controller. This driver can also be built as a module called > > + i2c-gpu.ko. > > + > > config I2C_SIS5595 > > tristate "SiS 5595" > > depends on PCI > > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile > > index 18b26af..15d2894 100644 > > --- a/drivers/i2c/busses/Makefile > > +++ b/drivers/i2c/busses/Makefile > > @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o > > obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o > > obj-$(CONFIG_SCx200_ACB) += scx200_acb.o > > obj-$(CONFIG_I2C_FSI) += i2c-fsi.o > > +obj-$(CONFIG_I2C_GPU) += i2c-gpu.o > > > > ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git > > a/drivers/i2c/busses/i2c-gpu.c b/drivers/i2c/busses/i2c-gpu.c new file > > mode 100644 index 000..0fd2944 > > --- /dev/null > > +++ b/drivers/i2c/busses/i2c-gpu.c > > @@ -0,0 +1,493 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Nvidia GPU I2C controller Driver > > + * > > + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. > > + * Author: Ajay Gupta > > + * > > + */ > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +/* STATUS definitions
RE: [PATCH v2 2/2] usb: typec: ucsi: add support for Cypress CCGx
Hi Heikki, > On Fri, Aug 24, 2018 at 02:33:36PM -0700, Ajay Gupta wrote: > > Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller > > over I2C interface. > > > > This UCSI I2C driver uses I2C bus driver interface for communicating > > with Type-C controller. > > Cool. The patch looks fairly good to me, but I put a few comments > below. Thanks for review. > > > Signed-off-by: Ajay Gupta > > --- > > Changes from v1 -> v2: > > Fixed identation in drivers/usb/typec/ucsi/Kconfig > > > > drivers/usb/typec/ucsi/Kconfig| 10 + > > drivers/usb/typec/ucsi/Makefile | 2 + > > drivers/usb/typec/ucsi/ucsi_i2c_ccg.c | 591 > ++ > > CCGx controllers support also SPI and UART AFAIK. Though, I'm not sure > how commonly they are used (I would expect I2C to be the most common > with these controllers), the driver should ultimately work with both > of those busses as well. > > To avoid confusion, and potential driver duplicates in the future, > just name the driver ucsi_ccg.c for now, and also s/i2c_ccg/ccg/ > everything in the driver (except the i2c_driver structure of course). Sure. > > 3 files changed, 603 insertions(+) > > create mode 100644 drivers/usb/typec/ucsi/ucsi_i2c_ccg.c > > > > diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig > > index e36d6c7..0ce9d48 100644 > > --- a/drivers/usb/typec/ucsi/Kconfig > > +++ b/drivers/usb/typec/ucsi/Kconfig > > @@ -23,6 +23,16 @@ config TYPEC_UCSI > > > > if TYPEC_UCSI > > > > +config UCSI_I2C_CCG > > + tristate "UCSI I2C Interface Driver for Cypress CCGx" > > + depends on I2C_GPU > > Why does it need to depend only on your I2C controller? > > I think that should be just "depends on I2C". ok > > + help > > + This driver enables UCSI support on NVIDIA GPUs that expose a > > + Cypress CCGx Type-C controller over I2C interface. > > + > > + To compile the driver as a module, choose M here: the module will > be > > + called ucsi_i2c_ccg.ko. > > + > > config UCSI_ACPI > > tristate "UCSI ACPI Interface Driver" > > depends on ACPI > > diff --git a/drivers/usb/typec/ucsi/Makefile > b/drivers/usb/typec/ucsi/Makefile > > index 7afbea5..4439482 100644 > > --- a/drivers/usb/typec/ucsi/Makefile > > +++ b/drivers/usb/typec/ucsi/Makefile > > @@ -8,3 +8,5 @@ typec_ucsi-y:= ucsi.o > > typec_ucsi-$(CONFIG_TRACING) += trace.o > > > > obj-$(CONFIG_UCSI_ACPI)+= ucsi_acpi.o > > + > > +obj-$(CONFIG_UCSI_I2C_CCG) += ucsi_i2c_ccg.o > > diff --git a/drivers/usb/typec/ucsi/ucsi_i2c_ccg.c > b/drivers/usb/typec/ucsi/ucsi_i2c_ccg.c > > new file mode 100644 > > index 000..587e3f8 > > --- /dev/null > > +++ b/drivers/usb/typec/ucsi/ucsi_i2c_ccg.c > > @@ -0,0 +1,591 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * UCSI I2C driver for Cypress CCGx Type-C controller > > + * > > + * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. > > + * Author: Ajay Gupta > > + * > > + * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c > > + */ > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include "ucsi.h" > > + > > +struct ucsi_i2c_ccg { > > + struct device *dev; > > + struct ucsi *ucsi; > > + struct ucsi_ppm ppm; > > + struct i2c_client *client; > > + int irq; > > + bool wake_enabled; > > + unsigned char ver; > > +}; > > + > > +#define CCGX_I2C_RAB_DEVICE_MODE 0xU > > +#define CCGX_I2C_RAB_BOOT_MODE_REASON > 0x0001U > > +#define CCGX_I2C_RAB_READ_SILICON_ID 0x0002U > > +#define CCGX_I2C_RAB_INTR_REG 0x0006U > > +#define CCGX_I2C_RAB_RESET 0x0008U > > +#define CCGX_I2C_RAB_READ_ALL_VERSION 0x0010U > > +#define CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER \ > > + (CCGX_I2C_RAB_READ_ALL_VERSION + 0x00) > > +#define CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER_BASE \ > > + (CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER > + 0) > > +#define CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER_FW \ > > + (CCGX_I2C_RAB_READ_ALL_VERSION_BOOTLOADER > + 4) > > +#define CCGX_I2C_RAB_READ_ALL_VERSION_APP \ > > +
RE: [PATCH v3 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Hi Andy, > > Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C > > controller which can be accessed over I2C. > > > > This driver adds I2C bus driver to communicate with Type-C controller. > > I2C client driver will be part of USB Type-C UCSI driver. > > > > Some small comments below, after addressing them > > Reviewed-by: Andy Shevchenko > > > Signed-off-by: Ajay Gupta > > --- > > Changes from v1 -> v2 > > None > > Changes from v2 -> v3 > > Fixed review comments from Andy and Thierry > > Rename i2c-gpu.c -> i2c-nvidia-gpu.c > > > > Documentation/i2c/busses/i2c-nvidia-gpu | 18 ++ > > MAINTAINERS | 7 + > > drivers/i2c/busses/Kconfig | 9 + > > drivers/i2c/busses/Makefile | 1 + > > drivers/i2c/busses/i2c-nvidia-gpu.c | 389 > > > 5 files changed, 424 insertions(+) > > create mode 100644 Documentation/i2c/busses/i2c-nvidia-gpu > > create mode 100644 drivers/i2c/busses/i2c-nvidia-gpu.c > > > > diff --git a/Documentation/i2c/busses/i2c-nvidia-gpu > > b/Documentation/i2c/busses/i2c-nvidia-gpu > > new file mode 100644 > > index 000..31884d2 > > --- /dev/null > > +++ b/Documentation/i2c/busses/i2c-nvidia-gpu > > @@ -0,0 +1,18 @@ > > +Kernel driver i2c-nvidia-gpu > > + > > +Datasheet: not publicly available. > > + > > +Authors: > > + Ajay Gupta > > + > > +Description > > +--- > > + > > +i2c-nvidia-gpu is a driver for I2C controller included in NVIDIA > > +Turing and later GPUs and it is used to communicate with Type-C controller > on GPUs. > > + > > +If your 'lspci -v' listing shows something like the following, > > + > > +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 > > +(rev a1) > > + > > +then this driver should support the I2C controller of your GPU. > > diff --git a/MAINTAINERS b/MAINTAINERS index 9ad052a..2d1c5a1 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -6797,6 +6797,13 @@ L: linux-a...@vger.kernel.org > > S: Maintained > > F: drivers/i2c/i2c-core-acpi.c > > > > +I2C CONTROLLER DRIVER FOR NVIDIA GPU > > +M: Ajay Gupta > > +L: linux-...@vger.kernel.org > > +S: Maintained > > +F: Documentation/i2c/busses/i2c-nvidia-gpu > > +F: drivers/i2c/busses/i2c-nvidia-gpu.c > > + > > I2C MUXES > > M: Peter Rosin > > L: linux-...@vger.kernel.org > > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig > > index 451d4ae..eed827b 100644 > > --- a/drivers/i2c/busses/Kconfig > > +++ b/drivers/i2c/busses/Kconfig > > @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 > > This driver can also be built as a module. If so, the module > > will be called i2c-nforce2-s4985. > > > > +config I2C_NVIDIA_GPU > > + tristate "NVIDIA GPU I2C controller" > > + depends on PCI > > + help > > + If you say yes to this option, support will be included for the > > + NVIDIA GPU I2C controller which is used to communicate with the > GPU's > > + Type-C controller. This driver can also be built as a module > > called > > + i2c-nvidia-gpu. > > + > > config I2C_SIS5595 > > tristate "SiS 5595" > > depends on PCI > > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile > > index 18b26af..d499813 100644 > > --- a/drivers/i2c/busses/Makefile > > +++ b/drivers/i2c/busses/Makefile > > @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o > > obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o > > obj-$(CONFIG_SCx200_ACB) += scx200_acb.o > > obj-$(CONFIG_I2C_FSI) += i2c-fsi.o > > +obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o > > > > ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git > > a/drivers/i2c/busses/i2c-nvidia-gpu.c > > b/drivers/i2c/busses/i2c-nvidia-gpu.c > > new file mode 100644 > > index 000..fa01187 > > --- /dev/null > > +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c > > @@ -0,0 +1,389 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Nvidia GPU I2C controller Driver > > + * > > + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. > > + * Author: Ajay Gupta > > > + * > &g
RE: [PATCH v3 2/2] usb: typec: ucsi: add support for Cypress CCGx
"ucsi_ccg_ack_interrupt() err %d\n", > > + err); > > + > > + return 0; > > +} > > + > > +static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control > > +*ctrl) { > > > + struct ucsi_ccg *uc = container_of(ppm, > > + struct ucsi_ccg, ppm); > > Why not one line? sure > > > + int err = 0; > > + > > + ppm->data->ctrl.raw_cmd = ctrl->raw_cmd; > > + err = ucsi_ccg_send_data(uc); > > + > > + return err; > > +} > > + > > +static irqreturn_t ccg_irq_handler(int irq, void *data) { > > + struct ucsi_ccg *uc = data; > > + > > + ucsi_notify(uc->ucsi); > > + > > + return IRQ_HANDLED; > > +} > > + > > > +static int ucsi_ccg_probe(struct i2c_client *client, > > + const struct i2c_device_id *id) > > One line? > > > +{ > > + struct device *dev = &client->dev; > > + struct ucsi_ccg *uc; > > + int status; > > + > > + uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); > > + if (!uc) > > + return -ENOMEM; > > + > > + uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), > GFP_KERNEL); > > + if (!uc->ppm.data) > > + return -ENOMEM; > > + > > + uc->ppm.cmd = ucsi_ccg_cmd; > > + uc->ppm.sync = ucsi_ccg_sync; > > + uc->dev = dev; > > + uc->client = client; > > + > > + /* reset ccg device and initialize ucsi */ > > + status = ucsi_ccg_init(uc); > > + if (status < 0) { > > + dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status); > > + return status; > > + } > > + > > + uc->irq = client->irq; > > + > > > + status = devm_request_threaded_irq(dev, uc->irq, NULL, > ccg_irq_handler, > > + IRQF_ONESHOT | > > + IRQF_TRIGGER_HIGH, > > ONESHORT can be dropped here (it's set based on condition when irq handler > NULL and threadirq is not). Below error is seen if I drop ONESHOT "genirq: Threaded irq requested with handler=NULL and !ONESHOT for irq 129" > > > + dev_name(dev), uc); > > + if (status < 0) { > > + dev_err(uc->dev, "request_threaded_irq failed - %d\n", > > status); > > + return status; > > + } > > + > > + uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); > > + if (IS_ERR(uc->ucsi)) { > > + dev_err(uc->dev, "ucsi_register_ppm failed\n"); > > + return PTR_ERR(uc->ucsi); > > + } > > + > > + i2c_set_clientdata(client, uc); > > + return 0; > > +} > > + > > +static int ucsi_ccg_remove(struct i2c_client *client) { > > + struct ucsi_ccg *uc = i2c_get_clientdata(client); > > + > > + ucsi_unregister_ppm(uc->ucsi); > > + > > + return 0; > > +} > > + > > +static const struct i2c_device_id ucsi_ccg_device_id[] = { > > + {"ccgx-ucsi", 0}, > > > + {}, > > Terminator line better without comma. Ok Thanks Ajay -- nvpublic -- > > > +}; > > +MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); > > + > > +static struct i2c_driver ucsi_ccg_driver = { > > + .driver = { > > + .name = "ucsi_ccg", > > + }, > > + .probe = ucsi_ccg_probe, > > + .remove = ucsi_ccg_remove, > > + .id_table = ucsi_ccg_device_id, }; > > + > > +module_i2c_driver(ucsi_ccg_driver); > > + > > +MODULE_AUTHOR("Ajay Gupta "); > > +MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller"); > > +MODULE_LICENSE("GPL v2"); > > -- > > 2.7.4 > > > > > -- > With Best Regards, > Andy Shevchenko
[PATCH v4 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C controller which can be accessed over I2C. This driver adds I2C bus driver to communicate with Type-C controller. I2C client driver will be part of USB Type-C UCSI driver. Signed-off-by: Ajay Gupta --- Changes from v1 -> v2 None Changes from v2 -> v3 Fixed review comments from Andy and Thierry Rename i2c-gpu.c -> i2c-nvidia-gpu.c Changes from v3 -> v4 Fixed review comments from Andy Documentation/i2c/busses/i2c-nvidia-gpu | 18 ++ MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-nvidia-gpu.c | 398 5 files changed, 433 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-nvidia-gpu create mode 100644 drivers/i2c/busses/i2c-nvidia-gpu.c diff --git a/Documentation/i2c/busses/i2c-nvidia-gpu b/Documentation/i2c/busses/i2c-nvidia-gpu new file mode 100644 index 000..31884d2 --- /dev/null +++ b/Documentation/i2c/busses/i2c-nvidia-gpu @@ -0,0 +1,18 @@ +Kernel driver i2c-nvidia-gpu + +Datasheet: not publicly available. + +Authors: + Ajay Gupta + +Description +--- + +i2c-nvidia-gpu is a driver for I2C controller included in NVIDIA Turing +and later GPUs and it is used to communicate with Type-C controller on GPUs. + +If your 'lspci -v' listing shows something like the following, + +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) + +then this driver should support the I2C controller of your GPU. diff --git a/MAINTAINERS b/MAINTAINERS index 9ad052a..2d1c5a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6797,6 +6797,13 @@ L: linux-a...@vger.kernel.org S: Maintained F: drivers/i2c/i2c-core-acpi.c +I2C CONTROLLER DRIVER FOR NVIDIA GPU +M: Ajay Gupta +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/i2c/busses/i2c-nvidia-gpu +F: drivers/i2c/busses/i2c-nvidia-gpu.c + I2C MUXES M: Peter Rosin L: linux-...@vger.kernel.org diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 451d4ae..eed827b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 This driver can also be built as a module. If so, the module will be called i2c-nforce2-s4985. +config I2C_NVIDIA_GPU + tristate "NVIDIA GPU I2C controller" + depends on PCI + help + If you say yes to this option, support will be included for the + NVIDIA GPU I2C controller which is used to communicate with the GPU's + Type-C controller. This driver can also be built as a module called + i2c-nvidia-gpu. + config I2C_SIS5595 tristate "SiS 5595" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af..d499813 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_FSI) += i2c-fsi.o +obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c new file mode 100644 index 000..51b8712 --- /dev/null +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvidia GPU I2C controller Driver + * + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* I2C definitions */ +#define I2C_MST_CNTL 0x00 +#define I2C_MST_CNTL_GEN_START BIT(0) +#define I2C_MST_CNTL_GEN_STOP BIT(1) +#define I2C_MST_CNTL_CMD_NONE (0 << 2) +#define I2C_MST_CNTL_CMD_READ (1 << 2) +#define I2C_MST_CNTL_CMD_WRITE (2 << 2) +#define I2C_MST_CNTL_GEN_RAB BIT(4) +#define I2C_MST_CNTL_BURST_SIZE_SHIFT 6 +#define I2C_MST_CNTL_GEN_NACK BIT(28) +#define I2C_MST_CNTL_STATUSGENMASK(30, 29) +#define I2C_MST_CNTL_STATUS_OKAY (0 << 29) +#define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29) +#define I2C_MST_CNTL_STATUS_TIMEOUT(2 << 29) +#define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29) +#define I2C_MST_CNTL_CYCLE_TRIGGER BIT(31) + +#define I2C_MST_ADDR 0x04 +#define I2C_MST_ADDR_DAB 0 + +#define I2C_MST_I2C0_TIMING0x08 +#define I2C_MST_
[PATCH v4 2/2] usb: typec: ucsi: add support for Cypress CCGx
Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller over I2C interface. This UCSI I2C driver uses I2C bus driver interface for communicating with Type-C controller. Signed-off-by: Ajay Gupta --- Changes from v1 -> v2 Fixed identation in drivers/usb/typec/ucsi/Kconfig Changes from v2 -> v3 Fixed most of comments from Heikki Rename ucsi_i2c_ccg.c -> ucsi_ccg.c Changes from v3 -> v4 Fixed comments from Andy drivers/usb/typec/ucsi/Kconfig| 10 + drivers/usb/typec/ucsi/Makefile | 2 + drivers/usb/typec/ucsi/ucsi_ccg.c | 393 ++ 3 files changed, 405 insertions(+) create mode 100644 drivers/usb/typec/ucsi/ucsi_ccg.c diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index e36d6c7..7811888 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -23,6 +23,16 @@ config TYPEC_UCSI if TYPEC_UCSI +config UCSI_CCG + tristate "UCSI Interface Driver for Cypress CCGx" + depends on I2C + help + This driver enables UCSI support on platforms that expose a + Cypress CCGx Type-C controller over I2C interface. + + To compile the driver as a module, choose M here: the module will be + called ucsi_ccg. + config UCSI_ACPI tristate "UCSI ACPI Interface Driver" depends on ACPI diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 7afbea5..2f4900b 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -8,3 +8,5 @@ typec_ucsi-y:= ucsi.o typec_ucsi-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_UCSI_ACPI)+= ucsi_acpi.o + +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 000..68efefa --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI driver for Cypress CCGx Type-C controller + * + * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c + */ +#include +#include +#include +#include +#include +#include +#include +#include "ucsi.h" + +struct ucsi_ccg { + struct device *dev; + struct ucsi *ucsi; + struct ucsi_ppm ppm; + struct i2c_client *client; + int irq; +}; + +#define CCGX_I2C_RAB_DEVICE_MODE 0x00 +#define CCGX_I2C_RAB_READ_SILICON_ID 0x2 +#define CCGX_I2C_RAB_INTR_REG 0x06 +#define CCGX_I2C_RAB_FW1_VERSION 0x28 +#define CCGX_I2C_RAB_FW2_VERSION 0x20 +#define CCGX_I2C_RAB_UCSI_CONTROL 0x39 +#define CCGX_I2C_RAB_UCSI_CONTROL_STARTBIT(0) +#define CCGX_I2C_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_I2C_RAB_RESPONSE_REG 0x7E +#define CCGX_I2C_RAB_UCSI_DATA_BLOCK 0xf000 + +static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct device *dev = uc->dev; + struct i2c_client *client = uc->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0x0, + .len= 0x2, + .buf= buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .buf= data, + }, + }; + u32 rlen, rem_len = len; + int err = -EIO; + + while (rem_len > 0) { + msgs[1].buf = &data[len - rem_len]; + rlen = min_t(u16, rem_len, 4); + msgs[1].len = rlen; + put_unaligned_le16(rab, buf); + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (err == ARRAY_SIZE(msgs)) { + err = 0; + } else if (err >= 0) { + dev_err(dev, "i2c_transfer failed, err %d\n", err); + return -EIO; + } + rab += rlen; + rem_len -= rlen; + } + + return err; +} + +static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct device *dev = uc->dev; + struct i2c_client *client = uc->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0x0, + .len= 0x2, + .buf= buf, + }, + { + .addr = clien
RE: [PATCH v4 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Hi Andy, > > Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C > > controller which can be accessed over I2C. > > > > This driver adds I2C bus driver to communicate with Type-C controller. > > I2C client driver will be part of USB Type-C UCSI driver. > > Thanks for an update, my comments below. Thanks for reviewing. > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + blank line. Ok. I could not find kernel documentation which requires this blank line. Can you please point me to it? > > +#include > > > +struct gpu_i2c_dev { > > + struct device *dev; > > + void __iomem *regs; > > + struct i2c_adapter adapter; > > + struct i2c_client *client; > > + struct mutex mutex; /* to sync read/write */ > > + bool do_start; > > +}; > > > +static int i2c_check_status(struct gpu_i2c_dev *i2cd) { > > + unsigned long target = jiffies + msecs_to_jiffies(1000); > > + u32 val; > > + > > + do { > > + val = readl(i2cd->regs + I2C_MST_CNTL); > > > + if ((val & I2C_MST_CNTL_CYCLE_TRIGGER) != > > + I2C_MST_CNTL_CYCLE_TRIGGER) > > Part after != redundant since it's one bit. > But I'm fine with current as well. Ok, will fix. > > > + break; > > + if ((val & I2C_MST_CNTL_STATUS) != > > + I2C_MST_CNTL_STATUS_BUS_BUSY) > > + break; > > + usleep_range(1000, 2000); > > + } while (time_is_after_jiffies(target)); > > > + > > Redundant. Ok, I will remove it but I didn't understand why its redundant? I thought adding extra line would be more readable. > > + if (time_is_before_jiffies(target)) > > + return -EIO; > > + > > + val = readl(i2cd->regs + I2C_MST_CNTL); > > + switch (val & I2C_MST_CNTL_STATUS) { > > + case I2C_MST_CNTL_STATUS_OKAY: > > + return 0; > > + case I2C_MST_CNTL_STATUS_NO_ACK: > > + return -EIO; > > + case I2C_MST_CNTL_STATUS_TIMEOUT: > > + return -ETIME; > > + case I2C_MST_CNTL_STATUS_BUS_BUSY: > > + return -EBUSY; > > + default: > > > + break; > > return 0; ? Ok, will fix. > > > + } > > > + return 0; > > See above. Ok > > > +} > > > +static int i2c_write(struct gpu_i2c_dev *i2cd, u8 data) { > > + u32 val; > > + > > + writel(data, i2cd->regs + I2C_MST_DATA); > > + > > + val = I2C_MST_CNTL_CMD_WRITE | (1 << > I2C_MST_CNTL_BURST_SIZE_SHIFT) | > > + I2C_MST_CNTL_GEN_NACK; > > > + val &= ~(I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_GEN_STOP > > + | I2C_MST_CNTL_GEN_RAB); > > "|" should be on previous line to follow common style in this module. The "|" here is to clear the bit together. [val &= ~(X | Y | Z)] Style in this module is still followed. Please see first line which does "|" to val. > > + writel(val, i2cd->regs + I2C_MST_CNTL); > > + > > + return i2c_check_status(i2cd); } > > + > > +static int gpu_i2c_master_xfer(struct i2c_adapter *adap, > > + struct i2c_msg *msgs, int num) { > > + struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap); > > + struct device *dev = i2cd->dev; > > + int status; > > + int i, j; > > > +stop: > > + status = i2c_stop(i2cd); > > + if (status < 0) > > + dev_err(dev, "i2c_stop error %x", status); > > +unlock: > > + mutex_unlock(&i2cd->mutex); > > > + return i; > > Shouldn't it return status in case of error? Thanks, will fix. > > +} > > > +/* > > + * This driver is for the Nvidia GPU cards with USB Type-C interface. > > + * We want to identify the cards using vendor ID and class code. > > + * Currently there is no class code defined for UCSI device over PCI > > + * so using UNKNOWN. > > + */ > > So, I didn't see how it *guarantees* no collision with other devices of the > same class... I checked and there is no other NVIDIA cards with UNKNOWN class code. Moreover, even if the driver gets loaded for a wrong card then eventually i2c_read() (initiated from UCSI driver) will timeout or UCSI commands will timeout. I think this is safe enough for now. We will update the class code when UCSI gets a PCI class code. We don't want to add dependency of adding product id for any new card which requires this driver. > > +#define PCI_CLASS_SERIAL_UNKNOWN 0x0c80 > > +static const struct pci_device_id gpu_i2c_ids[] = { > > > + { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, > > + PCI_CLASS_SERIAL_UNKNOWN << 8, 0xff00}, > > ...for now, I would suggest to be more stricted, i.e. > > { PCI_VDEVICE(NVIDIA, 0x1ad9) }, > > Whenever the class appears it can be added later on. See above. > > > + { } > > +}; > > +MODULE_DEVICE_TABLE(pci,
RE: [PATCH v4 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Hi Andy > > > Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C > > > controller which can be accessed over I2C. > > > + val &= ~(I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_GEN_STOP > > > + | I2C_MST_CNTL_GEN_RAB); > > > > "|" should be on previous line to follow common style in this module. > The "|" here is to clear the bit together. [val &= ~(X | Y | Z)] Style in > this > module is still followed. Please see first line which does "|" to val. Ok, I got your point of putting "|" on line above and will fix it. Thanks Ajay -- nvpublic -- > > > > + writel(val, i2cd->regs + I2C_MST_CNTL); > > > + > > > + return i2c_check_status(i2cd); } > > > + > > > +static int gpu_i2c_master_xfer(struct i2c_adapter *adap, > > > + struct i2c_msg *msgs, int num) { > > > + struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap); > > > + struct device *dev = i2cd->dev; > > > + int status; > > > + int i, j; > >
[PATCH v5 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C controller which can be accessed over I2C. This driver adds I2C bus driver to communicate with Type-C controller. I2C client driver will be part of USB Type-C UCSI driver. Signed-off-by: Ajay Gupta --- Changes from v1 -> v2 None Changes from v2 -> v3 Fixed review comments from Andy and Thierry Rename i2c-gpu.c -> i2c-nvidia-gpu.c Changes from v3 -> v4 Fixed review comments from Andy Changes from v4 -> v5 Fixed review comments from Andy Documentation/i2c/busses/i2c-nvidia-gpu | 18 ++ MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-nvidia-gpu.c | 405 5 files changed, 440 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-nvidia-gpu create mode 100644 drivers/i2c/busses/i2c-nvidia-gpu.c diff --git a/Documentation/i2c/busses/i2c-nvidia-gpu b/Documentation/i2c/busses/i2c-nvidia-gpu new file mode 100644 index 000..31884d2 --- /dev/null +++ b/Documentation/i2c/busses/i2c-nvidia-gpu @@ -0,0 +1,18 @@ +Kernel driver i2c-nvidia-gpu + +Datasheet: not publicly available. + +Authors: + Ajay Gupta + +Description +--- + +i2c-nvidia-gpu is a driver for I2C controller included in NVIDIA Turing +and later GPUs and it is used to communicate with Type-C controller on GPUs. + +If your 'lspci -v' listing shows something like the following, + +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) + +then this driver should support the I2C controller of your GPU. diff --git a/MAINTAINERS b/MAINTAINERS index 9ad052a..2d1c5a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6797,6 +6797,13 @@ L: linux-a...@vger.kernel.org S: Maintained F: drivers/i2c/i2c-core-acpi.c +I2C CONTROLLER DRIVER FOR NVIDIA GPU +M: Ajay Gupta +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/i2c/busses/i2c-nvidia-gpu +F: drivers/i2c/busses/i2c-nvidia-gpu.c + I2C MUXES M: Peter Rosin L: linux-...@vger.kernel.org diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 451d4ae..eed827b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 This driver can also be built as a module. If so, the module will be called i2c-nforce2-s4985. +config I2C_NVIDIA_GPU + tristate "NVIDIA GPU I2C controller" + depends on PCI + help + If you say yes to this option, support will be included for the + NVIDIA GPU I2C controller which is used to communicate with the GPU's + Type-C controller. This driver can also be built as a module called + i2c-nvidia-gpu. + config I2C_SIS5595 tristate "SiS 5595" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af..d499813 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_FSI) += i2c-fsi.o +obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c new file mode 100644 index 000..2ce6706 --- /dev/null +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvidia GPU I2C controller Driver + * + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* I2C definitions */ +#define I2C_MST_CNTL 0x00 +#define I2C_MST_CNTL_GEN_START BIT(0) +#define I2C_MST_CNTL_GEN_STOP BIT(1) +#define I2C_MST_CNTL_CMD_NONE (0 << 2) +#define I2C_MST_CNTL_CMD_READ (1 << 2) +#define I2C_MST_CNTL_CMD_WRITE (2 << 2) +#define I2C_MST_CNTL_GEN_RAB BIT(4) +#define I2C_MST_CNTL_BURST_SIZE_SHIFT 6 +#define I2C_MST_CNTL_GEN_NACK BIT(28) +#define I2C_MST_CNTL_STATUSGENMASK(30, 29) +#define I2C_MST_CNTL_STATUS_OKAY (0 << 29) +#define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29) +#define I2C_MST_CNTL_STATUS_TIMEOUT(2 << 29) +#define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29) +#define I2C_MST_CNTL_CYCLE_TRIGGER BIT(31) + +#define I2C_MST_ADDR 0x04 +#define I2C_MST_ADDR_DAB 0 + +#define I2C_MS
[PATCH v5 2/2] usb: typec: ucsi: add support for Cypress CCGx
Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller over I2C interface. This UCSI I2C driver uses I2C bus driver interface for communicating with Type-C controller. Signed-off-by: Ajay Gupta --- Changes from v1 -> v2 Fixed identation in drivers/usb/typec/ucsi/Kconfig Changes from v2 -> v3 Fixed most of comments from Heikki Rename ucsi_i2c_ccg.c -> ucsi_ccg.c Changes from v3 -> v4 Fixed comments from Andy Changes from v4 -> v5 Fixed comments from Andy drivers/usb/typec/ucsi/Kconfig| 10 + drivers/usb/typec/ucsi/Makefile | 2 + drivers/usb/typec/ucsi/ucsi_ccg.c | 390 ++ 3 files changed, 402 insertions(+) create mode 100644 drivers/usb/typec/ucsi/ucsi_ccg.c diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index e36d6c7..7811888 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -23,6 +23,16 @@ config TYPEC_UCSI if TYPEC_UCSI +config UCSI_CCG + tristate "UCSI Interface Driver for Cypress CCGx" + depends on I2C + help + This driver enables UCSI support on platforms that expose a + Cypress CCGx Type-C controller over I2C interface. + + To compile the driver as a module, choose M here: the module will be + called ucsi_ccg. + config UCSI_ACPI tristate "UCSI ACPI Interface Driver" depends on ACPI diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 7afbea5..2f4900b 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -8,3 +8,5 @@ typec_ucsi-y:= ucsi.o typec_ucsi-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_UCSI_ACPI)+= ucsi_acpi.o + +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 000..1e7c3b2 --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI driver for Cypress CCGx Type-C controller + * + * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c + */ +#include +#include +#include +#include +#include +#include + +#include +#include "ucsi.h" + +struct ucsi_ccg { + struct device *dev; + struct ucsi *ucsi; + struct ucsi_ppm ppm; + struct i2c_client *client; + int irq; +}; + +#define CCGX_I2C_RAB_DEVICE_MODE 0x00 +#define CCGX_I2C_RAB_READ_SILICON_ID 0x2 +#define CCGX_I2C_RAB_INTR_REG 0x06 +#define CCGX_I2C_RAB_FW1_VERSION 0x28 +#define CCGX_I2C_RAB_FW2_VERSION 0x20 +#define CCGX_I2C_RAB_UCSI_CONTROL 0x39 +#define CCGX_I2C_RAB_UCSI_CONTROL_STARTBIT(0) +#define CCGX_I2C_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_I2C_RAB_RESPONSE_REG 0x7E +#define CCGX_I2C_RAB_UCSI_DATA_BLOCK 0xf000 + +static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct device *dev = uc->dev; + struct i2c_client *client = uc->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0x0, + .len= 0x2, + .buf= buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .buf= data, + }, + }; + u32 rlen, rem_len = len; + int err; + + while (rem_len > 0) { + msgs[1].buf = &data[len - rem_len]; + rlen = min_t(u16, rem_len, 4); + msgs[1].len = rlen; + put_unaligned_le16(rab, buf); + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (err < 0) { + dev_err(dev, "i2c_transfer failed, err %d\n", err); + return err; + } + rab += rlen; + rem_len -= rlen; + } + + return 0; +} + +static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct device *dev = uc->dev; + struct i2c_client *client = uc->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0x0, + .len= 0x2, + .buf= buf, + }, + { + .addr = client->addr, +
RE: [PATCH v4 2/2] usb: typec: ucsi: add support for Cypress CCGx
Hi Andy, > > Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller over I2C > > interface. > > > > This UCSI I2C driver uses I2C bus driver interface for communicating > > with Type-C controller. > > > Changes from v3 -> v4 > > Fixed comments from Andy > > Unfortunatelly not all comments was addressed, see below. > > > + if (err == ARRAY_SIZE(msgs)) { > > + err = 0; > > + } else if (err >= 0) { > > + dev_err(dev, "i2c_transfer failed, err %d\n", err); > > + return -EIO; > > + } > > Shouldn't be simple > if (err < 0) { > ... > return err; > } > > ? Ok, will fix > > > + if (err == ARRAY_SIZE(msgs)) { > > + err = 0; > > + } else if (err >= 0) { > > + dev_err(dev, "i2c_transfer failed, err %d\n", err); > > + return -EIO; > > + } > > Ditto. ok > > > + struct device *dev = uc->dev; > > + u8 data[64]; > > + int err; > > > + unsigned int count = 10; > > Move this line upper a bit. ok > > > + unsigned char buf[3] = { > > + CCGX_I2C_RAB_INTR_REG & 0xff, CCGX_I2C_RAB_INTR_REG >> > > + 8, 0x0}; > > This should follow the style you applied earlier in the same file. > > Also, & 0xff is redundant (better just to use >> 0). Ok, even ">> 0" will be redundant so will just drop "& 0xff". > > + struct ucsi_ccg *uc = container_of(ppm, > > + struct ucsi_ccg, ppm); > > One line? ok > > > +static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control > > +*ctrl) { > > + struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); > > > + int err = 0; > > Redundant assignment. ok > > > + > > + ppm->data->ctrl.raw_cmd = ctrl->raw_cmd; > > + err = ucsi_ccg_send_data(uc); > > + > > + return err; > > +} > > > +static int ucsi_ccg_probe(struct i2c_client *client, > > + const struct i2c_device_id *id) > > One line? It crosses column width of 80. It takes total 85. > > > +static const struct i2c_device_id ucsi_ccg_device_id[] = { > > + {"ccgx-ucsi", 0}, > > > + {}, > > Terminator better w/o comma. Ok. > > > +}; > > +MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); > Thanks Ajay -- nvpublic -- > -- > With Best Regards, > Andy Shevchenko
RE: [PATCH v5 2/2] usb: typec: ucsi: add support for Cypress CCGx
Hi Greg, > On Fri, Aug 31, 2018 at 01:22:21PM -0700, Ajay Gupta wrote: > > + dev_info(dev, "Silicon id %2ph", data + > CCGX_I2C_RAB_READ_SILICON_ID); > > + dev_info(dev, "FW1 version %8ph\n", data + > CCGX_I2C_RAB_FW1_VERSION); > > + dev_info(dev, "FW2 version %8ph\n", data + > > +CCGX_I2C_RAB_FW2_VERSION); > > Drivers should be totally quiet when initialized, unless something goes wrong. > Please don't spam the kernel log with this type of stuff. > > You can use dev_dbg() if it helps with debugging the code, that might be a > better idea here. Ok, will use dev_dbg(). > Also, a pointer? Are you sure about that? That looks really odd... Yes, FW version is 8 byte hex code at address data + CCGX_I2C_RAB_FWx_VERSION Thanks Ajay -- nvpublic --
RE: [PATCH v5 2/2] usb: typec: ucsi: add support for Cypress CCGx
Hi Greg, > > +static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) > > +{ > > + struct device *dev = uc->dev; > > + struct i2c_client *client = uc->client; > > + unsigned char buf[2]; > > + struct i2c_msg msgs[] = { > > + { > > + .addr = client->addr, > > + .flags = 0x0, > > + .len= 0x2, > > + .buf= buf, > > + }, > > + { > > + .addr = client->addr, > > + .flags = I2C_M_RD, > > + .buf= data, > > + }, > > + }; > > Are you sure you are allowed to do i2c messages off of the stack like this? > Will that work on all platforms? Most of the drivers in kernel today seem to use stack memory for i2c_transfer() but I will fix it in next version. > > + u32 rlen, rem_len = len; > > + int err; > > + > > + while (rem_len > 0) { > > + msgs[1].buf = &data[len - rem_len]; > > + rlen = min_t(u16, rem_len, 4); > > + msgs[1].len = rlen; > > + put_unaligned_le16(rab, buf); > > + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); > > + if (err < 0) { > > + dev_err(dev, "i2c_transfer failed, err %d\n", err); > > You are printing out an error here, no need to print out another error every > time you call this function and it fails, right? Only do it in one place, > otherwise > it is extra noisy when things fail. I will fix and add error print in only one location. I think better to keep it here only and remove error print line from all the caller of this function. > > + return err; > > + } > > + rab += rlen; > > + rem_len -= rlen; > > + } > > + > > + return 0; > > +} > > + > > +static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) > > +{ > > + struct device *dev = uc->dev; > > + struct i2c_client *client = uc->client; > > + unsigned char buf[2]; > > + struct i2c_msg msgs[] = { > > + { > > + .addr = client->addr, > > + .flags = 0x0, > > + .len= 0x2, > > + .buf= buf, > > + }, > > + { > > + .addr = client->addr, > > + .flags = 0x0, > > + .buf= data, > > + .len= len, > > + }, > > + { > > + .addr = client->addr, > > + .flags = I2C_M_STOP, > > + }, > > + }; > > + int err; > > Same question here, i2c message off of the stack? > > > + > > + put_unaligned_le16(rab, buf); > > + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); > > + if (err < 0) { > > + dev_err(dev, "i2c_transfer failed, err %d\n", err); > > + return err; > > And again, only print an error in one place please. Ok. > This can be simplified to just: > return i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); right? Only issue is that i2c_transfer() return number of messages on success but ucsi APIs sync() and cmd() at drivers/usb/typec/ucsi/ucsi.h expects return value of '0' on success. > > + } > > + > > + return 0; > > +} > > + > > +static int ucsi_ccg_init(struct ucsi_ccg *uc) { > > + struct device *dev = uc->dev; > > + unsigned int count = 10; > > + u8 data[64]; > > + int err; > > + > > + /* > > +* Selectively issue device reset > > +* - if RESPONSE register is RESET_COMPLETE, do not issue device > reset > > +* (will cause usb device disconnect / reconnect) > > +* - if RESPONSE register is not RESET_COMPLETE, issue device reset > > +* (causes PPC to resync device connect state by re-issuing > > +* set mux command) > > +*/ > > + data[0] = 0x00; > > + data[1] = 0x00; > > Again, it's ok to do messages off of the stack? Will fix. Thanks Ajay -- nvpublic -- > > thanks, > > greg k-h
[PATCH v6 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C controller which can be accessed over I2C. This driver adds I2C bus driver to communicate with Type-C controller. I2C client driver will be part of USB Type-C UCSI driver. Signed-off-by: Ajay Gupta --- Changes from v1 -> v2 None Changes from v2 -> v3 Fixed review comments from Andy and Thierry Rename i2c-gpu.c -> i2c-nvidia-gpu.c Changes from v3 -> v4 Fixed review comments from Andy Changes from v4 -> v5 Fixed review comments from Andy Changes from v5 -> v6 None Documentation/i2c/busses/i2c-nvidia-gpu | 18 ++ MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-nvidia-gpu.c | 405 5 files changed, 440 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-nvidia-gpu create mode 100644 drivers/i2c/busses/i2c-nvidia-gpu.c diff --git a/Documentation/i2c/busses/i2c-nvidia-gpu b/Documentation/i2c/busses/i2c-nvidia-gpu new file mode 100644 index 000..31884d2 --- /dev/null +++ b/Documentation/i2c/busses/i2c-nvidia-gpu @@ -0,0 +1,18 @@ +Kernel driver i2c-nvidia-gpu + +Datasheet: not publicly available. + +Authors: + Ajay Gupta + +Description +--- + +i2c-nvidia-gpu is a driver for I2C controller included in NVIDIA Turing +and later GPUs and it is used to communicate with Type-C controller on GPUs. + +If your 'lspci -v' listing shows something like the following, + +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) + +then this driver should support the I2C controller of your GPU. diff --git a/MAINTAINERS b/MAINTAINERS index 9ad052a..2d1c5a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6797,6 +6797,13 @@ L: linux-a...@vger.kernel.org S: Maintained F: drivers/i2c/i2c-core-acpi.c +I2C CONTROLLER DRIVER FOR NVIDIA GPU +M: Ajay Gupta +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/i2c/busses/i2c-nvidia-gpu +F: drivers/i2c/busses/i2c-nvidia-gpu.c + I2C MUXES M: Peter Rosin L: linux-...@vger.kernel.org diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 451d4ae..eed827b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 This driver can also be built as a module. If so, the module will be called i2c-nforce2-s4985. +config I2C_NVIDIA_GPU + tristate "NVIDIA GPU I2C controller" + depends on PCI + help + If you say yes to this option, support will be included for the + NVIDIA GPU I2C controller which is used to communicate with the GPU's + Type-C controller. This driver can also be built as a module called + i2c-nvidia-gpu. + config I2C_SIS5595 tristate "SiS 5595" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af..d499813 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_FSI) += i2c-fsi.o +obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c new file mode 100644 index 000..2ce6706 --- /dev/null +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvidia GPU I2C controller Driver + * + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* I2C definitions */ +#define I2C_MST_CNTL 0x00 +#define I2C_MST_CNTL_GEN_START BIT(0) +#define I2C_MST_CNTL_GEN_STOP BIT(1) +#define I2C_MST_CNTL_CMD_NONE (0 << 2) +#define I2C_MST_CNTL_CMD_READ (1 << 2) +#define I2C_MST_CNTL_CMD_WRITE (2 << 2) +#define I2C_MST_CNTL_GEN_RAB BIT(4) +#define I2C_MST_CNTL_BURST_SIZE_SHIFT 6 +#define I2C_MST_CNTL_GEN_NACK BIT(28) +#define I2C_MST_CNTL_STATUSGENMASK(30, 29) +#define I2C_MST_CNTL_STATUS_OKAY (0 << 29) +#define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29) +#define I2C_MST_CNTL_STATUS_TIMEOUT(2 << 29) +#define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29) +#define I2C_MST_CNTL_CYCLE_TRIGGER BIT(31) + +#define I2C_MST_ADDR 0x04 +#define I2C_MST_ADDR_DAB
[PATCH v6 2/2] usb: typec: ucsi: add support for Cypress CCGx
Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller over I2C interface. This UCSI I2C driver uses I2C bus driver interface for communicating with Type-C controller. Signed-off-by: Ajay Gupta --- Changes from v1 -> v2 Fixed identation in drivers/usb/typec/ucsi/Kconfig Changes from v2 -> v3 Fixed most of comments from Heikki Rename ucsi_i2c_ccg.c -> ucsi_ccg.c Changes from v3 -> v4 Fixed comments from Andy Changes from v4 -> v5 Fixed comments from Andy Changes from v5 -> v6 Fixed review comments from Greg drivers/usb/typec/ucsi/Kconfig| 10 + drivers/usb/typec/ucsi/Makefile | 2 + drivers/usb/typec/ucsi/ucsi_ccg.c | 382 ++ 3 files changed, 394 insertions(+) create mode 100644 drivers/usb/typec/ucsi/ucsi_ccg.c diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index e36d6c7..7811888 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -23,6 +23,16 @@ config TYPEC_UCSI if TYPEC_UCSI +config UCSI_CCG + tristate "UCSI Interface Driver for Cypress CCGx" + depends on I2C + help + This driver enables UCSI support on platforms that expose a + Cypress CCGx Type-C controller over I2C interface. + + To compile the driver as a module, choose M here: the module will be + called ucsi_ccg. + config UCSI_ACPI tristate "UCSI ACPI Interface Driver" depends on ACPI diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 7afbea5..2f4900b 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -8,3 +8,5 @@ typec_ucsi-y:= ucsi.o typec_ucsi-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_UCSI_ACPI)+= ucsi_acpi.o + +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 000..65292bf --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI driver for Cypress CCGx Type-C controller + * + * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c + */ +#include +#include +#include +#include +#include +#include + +#include +#include "ucsi.h" + +struct ucsi_ccg { + struct device *dev; + struct ucsi *ucsi; + struct ucsi_ppm ppm; + struct i2c_client *client; + int irq; +}; + +#define CCGX_I2C_RAB_DEVICE_MODE 0x00 +#define CCGX_I2C_RAB_READ_SILICON_ID 0x2 +#define CCGX_I2C_RAB_INTR_REG 0x06 +#define CCGX_I2C_RAB_FW1_VERSION 0x28 +#define CCGX_I2C_RAB_FW2_VERSION 0x20 +#define CCGX_I2C_RAB_UCSI_CONTROL 0x39 +#define CCGX_I2C_RAB_UCSI_CONTROL_STARTBIT(0) +#define CCGX_I2C_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_I2C_RAB_RESPONSE_REG 0x7E +#define CCGX_I2C_RAB_UCSI_DATA_BLOCK 0xf000 + +static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct i2c_client *client = uc->client; + unsigned char *buf; + struct i2c_msg *msgs; + u32 rlen, rem_len = len; + int status; + + buf = kzalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msgs = kcalloc(2, sizeof(struct i2c_msg), GFP_KERNEL); + if (!msgs) { + kfree(buf); + return -ENOMEM; + } + + msgs[0].addr = client->addr; + msgs[0].len = 2; + msgs[0].buf = buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + + while (rem_len > 0) { + msgs[1].buf = &data[len - rem_len]; + rlen = min_t(u16, rem_len, 4); + msgs[1].len = rlen; + put_unaligned_le16(rab, buf); + status = i2c_transfer(client->adapter, msgs, 2); + if (status < 0) { + dev_err(uc->dev, "i2c_transfer failed %d", status); + kfree(buf); + kfree(msgs); + return status; + } + rab += rlen; + rem_len -= rlen; + } + + kfree(buf); + kfree(msgs); + return 0; +} + +static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct i2c_client *client = uc->client; + unsigned char *buf; + struct i2c_msg *msgs; + int status; + + buf = kzalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msgs = kcalloc(3, sizeof(struct i2c_msg), GFP_KE
RE: [PATCH v6 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Hi Peter, > > Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C > > controller which can be accessed over I2C. > > > > This driver adds I2C bus driver to communicate with Type-C controller. > > I2C client driver will be part of USB Type-C UCSI driver. > > > > Signed-off-by: Ajay Gupta > > +static void enable_i2c_bus(struct gpu_i2c_dev *i2cd) > > Please prefix all your functions with gpu_, or nvidia_gpu_ or something like > that (because gpu sounds like a something graphics, not that nvidia_gpu helps > with that problem though...) Ok, will prefix them. > > +{ > > + u32 val; > > + > > + /* enable I2C */ > > + val = readl(i2cd->regs + I2C_MST_HYBRID_PADCTL); > > + val |= I2C_MST_HYBRID_PADCTL_MODE_I2C | > > + I2C_MST_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | > > + I2C_MST_HYBRID_PADCTL_I2C_SDA_INPUT_RCV; > > + writel(val, i2cd->regs + I2C_MST_HYBRID_PADCTL); > > + > > + /* enable 100KHZ mode */ > > + val = I2C_MST_I2C0_TIMING_SCL_PERIOD_100KHZ; > > + val |= (I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT_MAX > > + << I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT); > > + val |= I2C_MST_I2C0_TIMING_TIMEOUT_CHECK; > > + writel(val, i2cd->regs + I2C_MST_I2C0_TIMING); } > > + > > +static int i2c_check_status(struct gpu_i2c_dev *i2cd) { > > + unsigned long target = jiffies + msecs_to_jiffies(1000); > > + u32 val; > > + > > + do { > > + val = readl(i2cd->regs + I2C_MST_CNTL); > > + if (!(val & I2C_MST_CNTL_CYCLE_TRIGGER)) > > + break; > > + if ((val & I2C_MST_CNTL_STATUS) != > > + I2C_MST_CNTL_STATUS_BUS_BUSY) > > + break; > > + usleep_range(1000, 2000); > > + } while (time_is_after_jiffies(target)); > > + if (time_is_before_jiffies(target)) > > + return -EIO; > > + > > + val = readl(i2cd->regs + I2C_MST_CNTL); > > + switch (val & I2C_MST_CNTL_STATUS) { > > + case I2C_MST_CNTL_STATUS_OKAY: > > + return 0; > > + case I2C_MST_CNTL_STATUS_NO_ACK: > > + return -EIO; > > + case I2C_MST_CNTL_STATUS_TIMEOUT: > > + return -ETIME; > > + case I2C_MST_CNTL_STATUS_BUS_BUSY: > > + return -EBUSY; > > + default: > > + return 0; > > + } > > +} > > + > > +static int i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16 len) { > > + int status; > > + u32 val; > > + > > + val = I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_GEN_STOP | > > + I2C_MST_CNTL_CMD_READ | (len << > I2C_MST_CNTL_BURST_SIZE_SHIFT) | > > + I2C_MST_CNTL_CYCLE_TRIGGER | > I2C_MST_CNTL_GEN_NACK; > > + val &= ~I2C_MST_CNTL_GEN_RAB; > > + writel(val, i2cd->regs + I2C_MST_CNTL); > > + > > + status = i2c_check_status(i2cd); > > + if (status < 0) > > + return status; > > + > > + val = readl(i2cd->regs + I2C_MST_DATA); > > + switch (len) { > > + case 1: > > + data[0] = val; > > + break; > > + case 2: > > + put_unaligned_be16(val, data); > > + break; > > + case 3: > > + put_unaligned_be16(val >> 8, data); > > + data[2] = val; > > + break; > > + case 4: > > + put_unaligned_be32(val, data); > > + break; > > + default: > > This seems to behave rather strange for len > 4, and I do not see anything > preventing that from happening. Actually the check is in ccg_read() of the client driver at https://marc.info/?l=linux-usb&m=153618608301206&w=2 > Am I missing something, or do you need to add a quirk for max_read_len? I will add a check in this function as well. > > + break; > > + } > > + return status; > > +} > > + > > +static int i2c_start(struct gpu_i2c_dev *i2cd, u16 addr) { > > + u32 val; > > + > > + val = addr << I2C_MST_ADDR_DAB; > > + writel(val, i2cd->regs + I2C_MST_ADDR); > > + > > + val = I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_CMD_NONE | > > + I2C_MST_CNTL_GEN_NACK; > > + val &= ~(I2C_MST_CNTL_GEN_STOP | I2C_MST_CNTL_GEN_RAB); > > + writel(val, i2cd->regs + I2C_MST_CNTL); > > + > > + return i2c_check_status(i2cd); > > +} > > + > > +static int i2c_stop(struct gpu_i2c_dev *i2cd) { > > + u32 val; > > + > > + va
[PATCH v7 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C controller which can be accessed over I2C. This driver adds I2C bus driver to communicate with Type-C controller. I2C client driver will be part of USB Type-C UCSI driver. Signed-off-by: Ajay Gupta Reviewed-by: Andy Shevchenko Reviewed-by: Heikki Krogerus --- Changes from v1 -> v2 None Changes from v2 -> v3 Fixed review comments from Andy and Thierry Rename i2c-gpu.c -> i2c-nvidia-gpu.c Changes from v3 -> v4 Fixed review comments from Andy Changes from v4 -> v5 Fixed review comments from Andy Changes from v5 -> v6 None Changes from v6 -> v7 Fixed review comments from Peter Documentation/i2c/busses/i2c-nvidia-gpu | 18 ++ MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-nvidia-gpu.c | 394 5 files changed, 429 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-nvidia-gpu create mode 100644 drivers/i2c/busses/i2c-nvidia-gpu.c diff --git a/Documentation/i2c/busses/i2c-nvidia-gpu b/Documentation/i2c/busses/i2c-nvidia-gpu new file mode 100644 index 000..31884d2 --- /dev/null +++ b/Documentation/i2c/busses/i2c-nvidia-gpu @@ -0,0 +1,18 @@ +Kernel driver i2c-nvidia-gpu + +Datasheet: not publicly available. + +Authors: + Ajay Gupta + +Description +--- + +i2c-nvidia-gpu is a driver for I2C controller included in NVIDIA Turing +and later GPUs and it is used to communicate with Type-C controller on GPUs. + +If your 'lspci -v' listing shows something like the following, + +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) + +then this driver should support the I2C controller of your GPU. diff --git a/MAINTAINERS b/MAINTAINERS index 9ad052a..2d1c5a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6797,6 +6797,13 @@ L: linux-a...@vger.kernel.org S: Maintained F: drivers/i2c/i2c-core-acpi.c +I2C CONTROLLER DRIVER FOR NVIDIA GPU +M: Ajay Gupta +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/i2c/busses/i2c-nvidia-gpu +F: drivers/i2c/busses/i2c-nvidia-gpu.c + I2C MUXES M: Peter Rosin L: linux-...@vger.kernel.org diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 451d4ae..eed827b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 This driver can also be built as a module. If so, the module will be called i2c-nforce2-s4985. +config I2C_NVIDIA_GPU + tristate "NVIDIA GPU I2C controller" + depends on PCI + help + If you say yes to this option, support will be included for the + NVIDIA GPU I2C controller which is used to communicate with the GPU's + Type-C controller. This driver can also be built as a module called + i2c-nvidia-gpu. + config I2C_SIS5595 tristate "SiS 5595" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af..d499813 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_FSI) += i2c-fsi.o +obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c new file mode 100644 index 000..5282f44 --- /dev/null +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvidia GPU I2C controller Driver + * + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* I2C definitions */ +#define I2C_MST_CNTL 0x00 +#define I2C_MST_CNTL_GEN_START BIT(0) +#define I2C_MST_CNTL_GEN_STOP BIT(1) +#define I2C_MST_CNTL_CMD_NONE (0 << 2) +#define I2C_MST_CNTL_CMD_READ (1 << 2) +#define I2C_MST_CNTL_CMD_WRITE (2 << 2) +#define I2C_MST_CNTL_GEN_RAB BIT(4) +#define I2C_MST_CNTL_BURST_SIZE_SHIFT 6 +#define I2C_MST_CNTL_GEN_NACK BIT(28) +#define I2C_MST_CNTL_STATUSGENMASK(30, 29) +#define I2C_MST_CNTL_STATUS_OKAY (0 << 29) +#define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29) +#define I2C_MST_CNTL_STATUS_TIMEOUT(2 << 29) +#define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29) +#define I2C
[PATCH v7 2/2] usb: typec: ucsi: add support for Cypress CCGx
Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller over I2C interface. This UCSI I2C driver uses I2C bus driver interface for communicating with Type-C controller. Signed-off-by: Ajay Gupta Reviewed-by: Andy Shevchenko Acked-by: Heikki Krogerus --- Changes from v1 -> v2 Fixed identation in drivers/usb/typec/ucsi/Kconfig Changes from v2 -> v3 Fixed most of comments from Heikki Rename ucsi_i2c_ccg.c -> ucsi_ccg.c Changes from v3 -> v4 Fixed comments from Andy Changes from v4 -> v5 Fixed comments from Andy Changes from v5 -> v6 Fixed review comments from Greg Changes from v6 -> v7 None drivers/usb/typec/ucsi/Kconfig| 10 + drivers/usb/typec/ucsi/Makefile | 2 + drivers/usb/typec/ucsi/ucsi_ccg.c | 382 ++ 3 files changed, 394 insertions(+) create mode 100644 drivers/usb/typec/ucsi/ucsi_ccg.c diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index e36d6c7..7811888 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -23,6 +23,16 @@ config TYPEC_UCSI if TYPEC_UCSI +config UCSI_CCG + tristate "UCSI Interface Driver for Cypress CCGx" + depends on I2C + help + This driver enables UCSI support on platforms that expose a + Cypress CCGx Type-C controller over I2C interface. + + To compile the driver as a module, choose M here: the module will be + called ucsi_ccg. + config UCSI_ACPI tristate "UCSI ACPI Interface Driver" depends on ACPI diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 7afbea5..2f4900b 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -8,3 +8,5 @@ typec_ucsi-y:= ucsi.o typec_ucsi-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_UCSI_ACPI)+= ucsi_acpi.o + +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 000..65292bf --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI driver for Cypress CCGx Type-C controller + * + * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c + */ +#include +#include +#include +#include +#include +#include + +#include +#include "ucsi.h" + +struct ucsi_ccg { + struct device *dev; + struct ucsi *ucsi; + struct ucsi_ppm ppm; + struct i2c_client *client; + int irq; +}; + +#define CCGX_I2C_RAB_DEVICE_MODE 0x00 +#define CCGX_I2C_RAB_READ_SILICON_ID 0x2 +#define CCGX_I2C_RAB_INTR_REG 0x06 +#define CCGX_I2C_RAB_FW1_VERSION 0x28 +#define CCGX_I2C_RAB_FW2_VERSION 0x20 +#define CCGX_I2C_RAB_UCSI_CONTROL 0x39 +#define CCGX_I2C_RAB_UCSI_CONTROL_STARTBIT(0) +#define CCGX_I2C_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_I2C_RAB_RESPONSE_REG 0x7E +#define CCGX_I2C_RAB_UCSI_DATA_BLOCK 0xf000 + +static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct i2c_client *client = uc->client; + unsigned char *buf; + struct i2c_msg *msgs; + u32 rlen, rem_len = len; + int status; + + buf = kzalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msgs = kcalloc(2, sizeof(struct i2c_msg), GFP_KERNEL); + if (!msgs) { + kfree(buf); + return -ENOMEM; + } + + msgs[0].addr = client->addr; + msgs[0].len = 2; + msgs[0].buf = buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + + while (rem_len > 0) { + msgs[1].buf = &data[len - rem_len]; + rlen = min_t(u16, rem_len, 4); + msgs[1].len = rlen; + put_unaligned_le16(rab, buf); + status = i2c_transfer(client->adapter, msgs, 2); + if (status < 0) { + dev_err(uc->dev, "i2c_transfer failed %d", status); + kfree(buf); + kfree(msgs); + return status; + } + rab += rlen; + rem_len -= rlen; + } + + kfree(buf); + kfree(msgs); + return 0; +} + +static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct i2c_client *client = uc->client; + unsigned char *buf; + struct i2c_msg *msgs; + int status; + + buf = kzalloc(2, GFP_KERNEL); + if (!buf) + re
RE: [PATCH v6 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
>dev, "pcim_enable_device failed %d\n", > >> status); > >>> + return status; > >>> + } > >>> + > >>> + pci_set_master(pdev); > >>> + > >>> + i2cd->regs = pcim_iomap(pdev, 0, 0); > >>> + if (!i2cd->regs) { > >>> + dev_err(&pdev->dev, "pcim_iomap failed\n"); > >>> + return -ENOMEM; > >>> + } > >>> + > >>> + status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); > >>> + if (status < 0) { > >>> + dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", > >> status); > >>> + return status; > >>> + } > >>> + > >>> + i2cd->do_start = true; > >>> + mutex_init(&i2cd->mutex); > >>> + enable_i2c_bus(i2cd); > >>> + > >>> + i2c_set_adapdata(&i2cd->adapter, i2cd); > >>> + i2cd->adapter.owner = THIS_MODULE; > >>> + strlcpy(i2cd->adapter.name, "NVIDIA GPU I2C adapter", > >>> + sizeof(i2cd->adapter.name)); > >>> + i2cd->adapter.algo = &gpu_i2c_algorithm; > >>> + i2cd->adapter.dev.parent = &pdev->dev; > >>> + status = i2c_add_adapter(&i2cd->adapter); > >>> + if (status < 0) { > >>> + dev_err(&pdev->dev, "i2c_add_adapter failed %d\n", status); > >>> + goto free_irq_vectors; > >>> + } > >>> + > >>> + status = gpu_populate_client(i2cd, pdev->irq); > >>> + if (status < 0) { > >>> + dev_err(&pdev->dev, "gpu_populate_client failed %d\n", > >> status); > >>> + goto del_adapter; > >>> + } > >>> + > >>> + return 0; > >>> + > >>> +del_adapter: > >>> + i2c_del_adapter(&i2cd->adapter); > >>> +free_irq_vectors: > >>> + pci_free_irq_vectors(pdev); > >>> + return status; > >>> +} > >>> + > >>> +static void gpu_i2c_remove(struct pci_dev *pdev) { > >>> + struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); > >>> + > >>> + i2c_del_adapter(&i2cd->adapter); > >>> + pci_free_irq_vectors(pdev); > >>> +} > >>> + > >>> +static int gpu_i2c_resume(struct device *dev) { > >>> + struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); > >>> + > >>> + enable_i2c_bus(i2cd); > >>> + return 0; > >>> +} > >>> + > >>> +static int gpu_i2c_idle(struct device *dev) { > >>> + struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); > >>> + > >>> + if (!mutex_trylock(&i2cd->mutex)) { > >>> + dev_info(dev, "-EBUSY\n"); > >>> + return -EBUSY; > >>> + } > >>> + mutex_unlock(&i2cd->mutex); > >>> + > >>> + return 0; > >>> +} > >>> + > >>> +UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, NULL, gpu_i2c_resume, > >>> +gpu_i2c_idle); > >>> + > >>> +static struct pci_driver gpu_i2c_driver = { > >>> + .name = "nvidia-gpu", > >>> + .id_table = gpu_i2c_ids, > >>> + .probe = gpu_i2c_probe, > >>> + .remove = gpu_i2c_remove, > >>> + .driver = { > >>> + .pm = &gpu_i2c_driver_pm, > >>> + }, > >>> +}; > >>> + > >>> +module_pci_driver(gpu_i2c_driver); > >>> + > >>> +MODULE_AUTHOR("Ajay Gupta "); > >>> +MODULE_DESCRIPTION("Nvidia GPU I2C controller Driver"); > >>> +MODULE_LICENSE("GPL v2"); > >>> > >
RE: [PATCH v7 2/2] usb: typec: ucsi: add support for Cypress CCGx
csi_ppm *ppm, struct ucsi_control > > +*ctrl) { > > + struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); > > + > > + ppm->data->ctrl.raw_cmd = ctrl->raw_cmd; > > + return ucsi_ccg_send_data(uc); > > +} > > + > > +static irqreturn_t ccg_irq_handler(int irq, void *data) { > > + struct ucsi_ccg *uc = data; > > + > > + ucsi_notify(uc->ucsi); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static int ucsi_ccg_probe(struct i2c_client *client, > > + const struct i2c_device_id *id) > > +{ > > + struct device *dev = &client->dev; > > + struct ucsi_ccg *uc; > > + int status; > > + > > + uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); > > + if (!uc) > > + return -ENOMEM; > > + > > + uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), > GFP_KERNEL); > > + if (!uc->ppm.data) > > + return -ENOMEM; > > + > > + uc->ppm.cmd = ucsi_ccg_cmd; > > + uc->ppm.sync = ucsi_ccg_sync; > > + uc->dev = dev; > > + uc->client = client; > > + > > + /* reset ccg device and initialize ucsi */ > > + status = ucsi_ccg_init(uc); > > + if (status < 0) { > > + dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status); > > + return status; > > + } > > + > > + uc->irq = client->irq; > > + > > + status = devm_request_threaded_irq(dev, uc->irq, NULL, > ccg_irq_handler, > > + IRQF_ONESHOT | > IRQF_TRIGGER_HIGH, > > + dev_name(dev), uc); > > + if (status < 0) { > > + dev_err(uc->dev, "request_threaded_irq failed - %d\n", > status); > > + return status; > > + } > > + > > + uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); > > + if (IS_ERR(uc->ucsi)) { > > + dev_err(uc->dev, "ucsi_register_ppm failed\n"); > > + return PTR_ERR(uc->ucsi); > > + } > > + > > + i2c_set_clientdata(client, uc); > > + return 0; > > +} > > + > > +static int ucsi_ccg_remove(struct i2c_client *client) { > > + struct ucsi_ccg *uc = i2c_get_clientdata(client); > > + > > + ucsi_unregister_ppm(uc->ucsi); > > + > > + return 0; > > +} > > + > > +static const struct i2c_device_id ucsi_ccg_device_id[] = { > > + {"ccgx-ucsi", 0}, > > + {} > > +}; > > +MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); > > + > > +static struct i2c_driver ucsi_ccg_driver = { > > + .driver = { > > + .name = "ucsi_ccg", > > + }, > > + .probe = ucsi_ccg_probe, > > + .remove = ucsi_ccg_remove, > > + .id_table = ucsi_ccg_device_id, > > +}; > > + > > +module_i2c_driver(ucsi_ccg_driver); > > + > > +MODULE_AUTHOR("Ajay Gupta "); > > +MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller"); > > +MODULE_LICENSE("GPL v2"); > >
[PATCH v8 2/2] usb: typec: ucsi: add support for Cypress CCGx
Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller over I2C interface. This UCSI I2C driver uses I2C bus driver interface for communicating with Type-C controller. Signed-off-by: Ajay Gupta --- Changes from v1 -> v2 Fixed identation in drivers/usb/typec/ucsi/Kconfig Changes from v2 -> v3 Fixed most of comments from Heikki Rename ucsi_i2c_ccg.c -> ucsi_ccg.c Changes from v3 -> v4 Fixed comments from Andy Changes from v4 -> v5 Fixed comments from Andy Changes from v5 -> v6 Fixed review comments from Greg Changes from v6 -> v7 None Changes from v7 -> v8 Fixed review comments from Peter - Removed empty STOP message - Using stack memory for i2c_transfer() drivers/usb/typec/ucsi/Kconfig| 10 ++ drivers/usb/typec/ucsi/Makefile | 2 + drivers/usb/typec/ucsi/ucsi_ccg.c | 335 ++ 3 files changed, 347 insertions(+) create mode 100644 drivers/usb/typec/ucsi/ucsi_ccg.c diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index e36d6c7..7811888 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -23,6 +23,16 @@ config TYPEC_UCSI if TYPEC_UCSI +config UCSI_CCG + tristate "UCSI Interface Driver for Cypress CCGx" + depends on I2C + help + This driver enables UCSI support on platforms that expose a + Cypress CCGx Type-C controller over I2C interface. + + To compile the driver as a module, choose M here: the module will be + called ucsi_ccg. + config UCSI_ACPI tristate "UCSI ACPI Interface Driver" depends on ACPI diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 7afbea5..2f4900b 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -8,3 +8,5 @@ typec_ucsi-y:= ucsi.o typec_ucsi-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_UCSI_ACPI)+= ucsi_acpi.o + +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 000..387b6fd --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI driver for Cypress CCGx Type-C controller + * + * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c + */ +#include +#include +#include +#include +#include +#include + +#include +#include "ucsi.h" + +struct ucsi_ccg { + struct device *dev; + struct ucsi *ucsi; + struct ucsi_ppm ppm; + struct i2c_client *client; + int irq; +}; + +#define CCGX_I2C_RAB_DEVICE_MODE 0x00 +#define CCGX_I2C_RAB_READ_SILICON_ID 0x2 +#define CCGX_I2C_RAB_INTR_REG 0x06 +#define CCGX_I2C_RAB_FW1_VERSION 0x28 +#define CCGX_I2C_RAB_FW2_VERSION 0x20 +#define CCGX_I2C_RAB_UCSI_CONTROL 0x39 +#define CCGX_I2C_RAB_UCSI_CONTROL_STARTBIT(0) +#define CCGX_I2C_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_I2C_RAB_RESPONSE_REG 0x7E +#define CCGX_I2C_RAB_UCSI_DATA_BLOCK 0xf000 + +static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct i2c_client *client = uc->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0x0, + .len= 0x2, + .buf= buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .buf= data, + }, + }; + u32 rlen, rem_len = len; + int status; + + while (rem_len > 0) { + msgs[1].buf = &data[len - rem_len]; + rlen = min_t(u16, rem_len, 4); + msgs[1].len = rlen; + put_unaligned_le16(rab, buf); + status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (status < 0) { + dev_err(uc->dev, "i2c_transfer failed %d\n", status); + return status; + } + rab += rlen; + rem_len -= rlen; + } + + return 0; +} + +static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct i2c_client *client = uc->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, +
[PATCH v8 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C controller which can be accessed over I2C. This driver adds I2C bus driver to communicate with Type-C controller. I2C client driver will be part of USB Type-C UCSI driver. Signed-off-by: Ajay Gupta Reviewed-by: Andy Shevchenko Reviewed-by: Heikki Krogerus --- Changes from v1 -> v2 None Changes from v2 -> v3 Fixed review comments from Andy and Thierry Rename i2c-gpu.c -> i2c-nvidia-gpu.c Changes from v3 -> v4 Fixed review comments from Andy Changes from v4 -> v5 Fixed review comments from Andy Changes from v5 -> v6 None Changes from v6 -> v7 -> v8 Fixed review comments from Peter - Add implicit STOP for last write message - Add i2c_adapter_quirks with max_read_len and I2C_AQ_COMB flags Documentation/i2c/busses/i2c-nvidia-gpu | 18 ++ MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-nvidia-gpu.c | 400 5 files changed, 435 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-nvidia-gpu create mode 100644 drivers/i2c/busses/i2c-nvidia-gpu.c diff --git a/Documentation/i2c/busses/i2c-nvidia-gpu b/Documentation/i2c/busses/i2c-nvidia-gpu new file mode 100644 index 000..31884d2 --- /dev/null +++ b/Documentation/i2c/busses/i2c-nvidia-gpu @@ -0,0 +1,18 @@ +Kernel driver i2c-nvidia-gpu + +Datasheet: not publicly available. + +Authors: + Ajay Gupta + +Description +--- + +i2c-nvidia-gpu is a driver for I2C controller included in NVIDIA Turing +and later GPUs and it is used to communicate with Type-C controller on GPUs. + +If your 'lspci -v' listing shows something like the following, + +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) + +then this driver should support the I2C controller of your GPU. diff --git a/MAINTAINERS b/MAINTAINERS index 9ad052a..2d1c5a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6797,6 +6797,13 @@ L: linux-a...@vger.kernel.org S: Maintained F: drivers/i2c/i2c-core-acpi.c +I2C CONTROLLER DRIVER FOR NVIDIA GPU +M: Ajay Gupta +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/i2c/busses/i2c-nvidia-gpu +F: drivers/i2c/busses/i2c-nvidia-gpu.c + I2C MUXES M: Peter Rosin L: linux-...@vger.kernel.org diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 451d4ae..eed827b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 This driver can also be built as a module. If so, the module will be called i2c-nforce2-s4985. +config I2C_NVIDIA_GPU + tristate "NVIDIA GPU I2C controller" + depends on PCI + help + If you say yes to this option, support will be included for the + NVIDIA GPU I2C controller which is used to communicate with the GPU's + Type-C controller. This driver can also be built as a module called + i2c-nvidia-gpu. + config I2C_SIS5595 tristate "SiS 5595" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af..d499813 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_FSI) += i2c-fsi.o +obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c new file mode 100644 index 000..4816a31 --- /dev/null +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvidia GPU I2C controller Driver + * + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* I2C definitions */ +#define I2C_MST_CNTL 0x00 +#define I2C_MST_CNTL_GEN_START BIT(0) +#define I2C_MST_CNTL_GEN_STOP BIT(1) +#define I2C_MST_CNTL_CMD_NONE (0 << 2) +#define I2C_MST_CNTL_CMD_READ (1 << 2) +#define I2C_MST_CNTL_CMD_WRITE (2 << 2) +#define I2C_MST_CNTL_GEN_RAB BIT(4) +#define I2C_MST_CNTL_BURST_SIZE_SHIFT 6 +#define I2C_MST_CNTL_GEN_NACK BIT(28) +#define I2C_MST_CNTL_STATUSGENMASK(30, 29) +#define I2C_MST_CNTL_STATUS_OKAY (0 << 29) +#define I2C_MST_CNTL_STATUS_NO_ACK
[PATCH v9 1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C controller which can be accessed over I2C. This driver adds I2C bus driver to communicate with Type-C controller. I2C client driver will be part of USB Type-C UCSI driver. Signed-off-by: Ajay Gupta Reviewed-by: Andy Shevchenko Reviewed-by: Heikki Krogerus --- Changes from v1 -> v2 None Changes from v2 -> v3 Fixed review comments from Andy and Thierry Rename i2c-gpu.c -> i2c-nvidia-gpu.c Changes from v3 -> v4 Fixed review comments from Andy Changes from v4 -> v5 Fixed review comments from Andy Changes from v5 -> v6 None Changes from v6 -> v7 -> v8 Fixed review comments from Peter - Add implicit STOP for last write message - Add i2c_adapter_quirks with max_read_len and I2C_AQ_COMB flags Changes from v8 -> v9 Fixed review comments from Peter - Drop do_start flag - Use i2c_8bit_addr_from_msg() Documentation/i2c/busses/i2c-nvidia-gpu | 18 ++ MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-nvidia-gpu.c | 396 5 files changed, 431 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-nvidia-gpu create mode 100644 drivers/i2c/busses/i2c-nvidia-gpu.c diff --git a/Documentation/i2c/busses/i2c-nvidia-gpu b/Documentation/i2c/busses/i2c-nvidia-gpu new file mode 100644 index 000..31884d2 --- /dev/null +++ b/Documentation/i2c/busses/i2c-nvidia-gpu @@ -0,0 +1,18 @@ +Kernel driver i2c-nvidia-gpu + +Datasheet: not publicly available. + +Authors: + Ajay Gupta + +Description +--- + +i2c-nvidia-gpu is a driver for I2C controller included in NVIDIA Turing +and later GPUs and it is used to communicate with Type-C controller on GPUs. + +If your 'lspci -v' listing shows something like the following, + +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) + +then this driver should support the I2C controller of your GPU. diff --git a/MAINTAINERS b/MAINTAINERS index 9ad052a..2d1c5a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6797,6 +6797,13 @@ L: linux-a...@vger.kernel.org S: Maintained F: drivers/i2c/i2c-core-acpi.c +I2C CONTROLLER DRIVER FOR NVIDIA GPU +M: Ajay Gupta +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/i2c/busses/i2c-nvidia-gpu +F: drivers/i2c/busses/i2c-nvidia-gpu.c + I2C MUXES M: Peter Rosin L: linux-...@vger.kernel.org diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 451d4ae..eed827b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985 This driver can also be built as a module. If so, the module will be called i2c-nforce2-s4985. +config I2C_NVIDIA_GPU + tristate "NVIDIA GPU I2C controller" + depends on PCI + help + If you say yes to this option, support will be included for the + NVIDIA GPU I2C controller which is used to communicate with the GPU's + Type-C controller. This driver can also be built as a module called + i2c-nvidia-gpu. + config I2C_SIS5595 tristate "SiS 5595" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af..d499813 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE)+= i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_FSI) += i2c-fsi.o +obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c new file mode 100644 index 000..4a63a4e4 --- /dev/null +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvidia GPU I2C controller Driver + * + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* I2C definitions */ +#define I2C_MST_CNTL 0x00 +#define I2C_MST_CNTL_GEN_START BIT(0) +#define I2C_MST_CNTL_GEN_STOP BIT(1) +#define I2C_MST_CNTL_CMD_NONE (0 << 2) +#define I2C_MST_CNTL_CMD_READ (1 << 2) +#define I2C_MST_CNTL_CMD_WRITE (2 << 2) +#define I2C_MST_CNTL_GEN_RAB BIT(4) +#define I2C_MST_CNTL_BURST_SIZE_SHIFT 6 +#define I2C_MST_CNTL_GEN_NACK BIT(28) +#define I2C_MST_CNTL_STATUS
[PATCH v9 2/2] usb: typec: ucsi: add support for Cypress CCGx
Latest NVIDIA GPU cards have a Cypress CCGx Type-C controller over I2C interface. This UCSI I2C driver uses I2C bus driver interface for communicating with Type-C controller. Signed-off-by: Ajay Gupta Reviewed-by: Andy Shevchenko Acked-by: Heikki Krogerus --- Changes from v1 -> v2 Fixed identation in drivers/usb/typec/ucsi/Kconfig Changes from v2 -> v3 Fixed most of comments from Heikki Rename ucsi_i2c_ccg.c -> ucsi_ccg.c Changes from v3 -> v4 Fixed comments from Andy Changes from v4 -> v5 Fixed comments from Andy Changes from v5 -> v6 Fixed review comments from Greg Changes from v6 -> v7 None Changes from v7 -> v8 Fixed review comments from Peter - Removed empty STOP message - Using stack memory for i2c_transfer() Changes from v8 -> v9 None drivers/usb/typec/ucsi/Kconfig| 10 ++ drivers/usb/typec/ucsi/Makefile | 2 + drivers/usb/typec/ucsi/ucsi_ccg.c | 335 ++ 3 files changed, 347 insertions(+) create mode 100644 drivers/usb/typec/ucsi/ucsi_ccg.c diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index e36d6c7..7811888 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -23,6 +23,16 @@ config TYPEC_UCSI if TYPEC_UCSI +config UCSI_CCG + tristate "UCSI Interface Driver for Cypress CCGx" + depends on I2C + help + This driver enables UCSI support on platforms that expose a + Cypress CCGx Type-C controller over I2C interface. + + To compile the driver as a module, choose M here: the module will be + called ucsi_ccg. + config UCSI_ACPI tristate "UCSI ACPI Interface Driver" depends on ACPI diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 7afbea5..2f4900b 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -8,3 +8,5 @@ typec_ucsi-y:= ucsi.o typec_ucsi-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_UCSI_ACPI)+= ucsi_acpi.o + +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 000..387b6fd --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI driver for Cypress CCGx Type-C controller + * + * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + * + * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c + */ +#include +#include +#include +#include +#include +#include + +#include +#include "ucsi.h" + +struct ucsi_ccg { + struct device *dev; + struct ucsi *ucsi; + struct ucsi_ppm ppm; + struct i2c_client *client; + int irq; +}; + +#define CCGX_I2C_RAB_DEVICE_MODE 0x00 +#define CCGX_I2C_RAB_READ_SILICON_ID 0x2 +#define CCGX_I2C_RAB_INTR_REG 0x06 +#define CCGX_I2C_RAB_FW1_VERSION 0x28 +#define CCGX_I2C_RAB_FW2_VERSION 0x20 +#define CCGX_I2C_RAB_UCSI_CONTROL 0x39 +#define CCGX_I2C_RAB_UCSI_CONTROL_STARTBIT(0) +#define CCGX_I2C_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_I2C_RAB_RESPONSE_REG 0x7E +#define CCGX_I2C_RAB_UCSI_DATA_BLOCK 0xf000 + +static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct i2c_client *client = uc->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0x0, + .len= 0x2, + .buf= buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .buf= data, + }, + }; + u32 rlen, rem_len = len; + int status; + + while (rem_len > 0) { + msgs[1].buf = &data[len - rem_len]; + rlen = min_t(u16, rem_len, 4); + msgs[1].len = rlen; + put_unaligned_le16(rab, buf); + status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (status < 0) { + dev_err(uc->dev, "i2c_transfer failed %d\n", status); + return status; + } + rab += rlen; + rem_len -= rlen; + } + + return 0; +} + +static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +{ + struct i2c_client *client = uc->client; + unsigned char buf[2]; + struct i2c_msg msgs[] = { +