* Re: [PATCH] dt: bindings: as3645a: Fix the example node
From: Pavel Machek @ 2017-12-27 21:19 UTC (permalink / raw)
To: Rob Herring
Cc: Dan Murphy, Mark Rutland, Richard Purdie, Jacek Anaszewski,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Linux LED Subsystem
In-Reply-To: <CAL_JsqKG2TWLTREqNecy_3pOqLFJWa4SM4G4yzyKsZqW7opDvw@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1622 bytes --]
On Thu 2017-12-07 09:42:29, Rob Herring wrote:
> On Wed, Dec 6, 2017 at 4:32 PM, Dan Murphy <dmurphy@ti.com> wrote:
> > Rob
> >
> > On 12/06/2017 03:52 PM, Rob Herring wrote:
> >> On Tue, Dec 05, 2017 at 07:36:37AM -0600, Dan Murphy wrote:
> >>> Fix the address-cells and size-cells example node
> >>> to reflect to the correct representation.
> >>>
> >>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> >>> ---
> >>> Documentation/devicetree/bindings/leds/ams,as3645a.txt | 4 ++--
> >>> 1 file changed, 2 insertions(+), 2 deletions(-)
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/leds/ams,as3645a.txt b/Documentation/devicetree/bindings/leds/ams,as3645a.txt
> >>> index fdc40e354a64..8a49ccfec29b 100644
> >>> --- a/Documentation/devicetree/bindings/leds/ams,as3645a.txt
> >>> +++ b/Documentation/devicetree/bindings/leds/ams,as3645a.txt
> >>> @@ -59,8 +59,8 @@ Example
> >>> =======
> >>>
> >>> as3645a@30 {
> >>> - #address-cells: 1
> >>> - #size-cells: 0
> >>> + #address-cells = <1>;
> >>> + #size-cells = <0>;
> >>> reg = <0x30>;
> >>> compatible = "ams,as3645a";
> >>
> >> Convention is also we put compatible first.
> >>
> >
> > OK I will move that too for v2.
> >
> > Can that be in the same patch or do you want a separate patch?
>
> Same patch is fine.
For fixed patch:
Signed-off-by: Pavel Machek <pavel@ucw.cz>
Thanks,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply
* Re: [PATCH] Device tree binding for Avago APDS990X light sensor
From: Sakari Ailus @ 2017-12-27 21:16 UTC (permalink / raw)
To: Pavel Machek
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
pali.rohar-Re5JQEeQqe8AvxtiuMwx3w, sre-DgEjT+Ai2ygdnm+yROfE0A,
linux-arm-kernel, linux-omap-u79uwXL29TY76Z2rM5mHXA,
tony-4v6yS6AI5VpBDgjK7y7TUQ, khilman-DgEjT+Ai2ygdnm+yROfE0A,
aaro.koskinen-X3B1VOXEql0,
ivo.g.dimitrov.75-Re5JQEeQqe8AvxtiuMwx3w,
patrikbachan-Re5JQEeQqe8AvxtiuMwx3w, serge-A9i7LUbDfNHQT0dZR+AlfA,
abcloriens-Re5JQEeQqe8AvxtiuMwx3w, clayton-fehKsxFhGzZIf6P1QZMOBw,
martijn-28JJ9oSIdodmR6Xm/wNWPw, Filip Matijević
In-Reply-To: <20171227200147.GB16799@amd>
On Wed, Dec 27, 2017 at 09:01:47PM +0100, Pavel Machek wrote:
> Hi!
>
> > > +Required properties:
> > > +- compatible: "avago,apds990x"
> > > +- reg: address on the I2C bus
> > > +- interrupts: external interrupt line number
> > > +- Vdd-supply: power supply for VDD
> > > +- Vled-supply: power supply for LEDA
> >
> > AFAIK the custom is to use lower case letters for regulator supplies.
> >
> > > +- ga: Glass attenuation
> > > +- cf1: Clear channel factor 1
> > > +- irf1: IR channel factor 1
> > > +- cf2: Clear channel factor 2
> > > +- irf2: IR channel factor 2
> > > +- df: Device factor
> > > +- pdrive: IR current, one of APDS_IRLED_CURR_XXXmA values
> > > +- ppcount: Proximity pulse count
> >
> > Are these device specific? If so, please add the vendor prefix to them.
>
> Well, whole binding is "vendor specific". Does it make sense to add
> prefix in such case?
Yes, it does. If you later find one or more of these are generic, you could
remove the vendor prefix. I doubt that'll happen though, these seem very
device specific parameters.
--
Sakari Ailus
sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] Device tree binding for Avago APDS990X light sensor
From: Sakari Ailus @ 2017-12-27 21:15 UTC (permalink / raw)
To: Filip Matijević
Cc: Pavel Machek, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
pali.rohar-Re5JQEeQqe8AvxtiuMwx3w, sre-DgEjT+Ai2ygdnm+yROfE0A,
linux-arm-kernel, linux-omap-u79uwXL29TY76Z2rM5mHXA,
tony-4v6yS6AI5VpBDgjK7y7TUQ, khilman-DgEjT+Ai2ygdnm+yROfE0A,
aaro.koskinen-X3B1VOXEql0,
ivo.g.dimitrov.75-Re5JQEeQqe8AvxtiuMwx3w,
patrikbachan-Re5JQEeQqe8AvxtiuMwx3w, serge-A9i7LUbDfNHQT0dZR+AlfA,
abcloriens-Re5JQEeQqe8AvxtiuMwx3w, clayton-fehKsxFhGzZIf6P1QZMOBw,
martijn-28JJ9oSIdodmR6Xm/wNWPw
In-Reply-To: <7a5d43a9-27f5-bdbd-780f-6c6bc47fb987-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Wed, Dec 27, 2017 at 07:50:42PM +0100, Filip Matijević wrote:
> Hi Sakari,
>
> and thank you for your input - I've added a few comments below.
>
> On 12/27/2017 07:00 PM, Sakari Ailus wrote:
> > Hi Pavel,
> >
> > Thanks for the patch. Please see my comments below.
> >
> > On Wed, Dec 27, 2017 at 10:18:28AM +0100, Pavel Machek wrote:
> >> From: Filip Matijević <filip.matijevic.pz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> >>
> >> This prepares binding for light sensor used in Nokia N9.
> >>
> >> Signed-off-by: Filip Matijević <filip.matijevic.pz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> >> Signed-off-by: Pavel machek <pavel-+ZI9xUNit7I@public.gmane.org>
> >>
> >> ---
> >>
> >> Patches to convert APDS990X driver to device tree and to switch to iio
> >> are available.
> >>
> >> diff --git a/Documentation/devicetree/bindings/misc/avago-apds990x.txt b/Documentation/devicetree/bindings/misc/avago-apds990x.txt
> >> new file mode 100644
> >> index 0000000..e038146
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/misc/avago-apds990x.txt
> >> @@ -0,0 +1,39 @@
> >> +Avago APDS990X driver
> >> +
> >> +Required properties:
> >> +- compatible: "avago,apds990x"
> >> +- reg: address on the I2C bus
> >> +- interrupts: external interrupt line number
> >> +- Vdd-supply: power supply for VDD
> >> +- Vled-supply: power supply for LEDA
> >
> > AFAIK the custom is to use lower case letters for regulator supplies.
>
> I've just used the same notation as in current driver. Datasheet calls
> those VDD (with DD being in subscript) and LEDA. I see no problem in
> changing those to vdd-supply and vled-supply if no one else objects.
>
> >
> >> +- ga: Glass attenuation
> >> +- cf1: Clear channel factor 1
> >> +- irf1: IR channel factor 1
> >> +- cf2: Clear channel factor 2
> >> +- irf2: IR channel factor 2
> >> +- df: Device factor
> >> +- pdrive: IR current, one of APDS_IRLED_CURR_XXXmA values
> >> +- ppcount: Proximity pulse count
> >
> > Are these device specific? If so, please add the vendor prefix to them.
>
> AFAIK yes - same as before if no one else objects, these will be changed.
>
> >
> > I might not use short abbreviations such as "df" either. I wonder what
> > others think.
>
> These are also come from current driver - some of these comes from the
> datasheet itself, but not all. For example "ga" and "df" are in there
> (so I I would leave those alone), but "irf1" is called "B", "cf2" is
> called "C" and "irf2" is called "D" (I guess the missing "cf1" should be
> "A", but there is no such thing in the datasheet).
> IMHO using some other names might just add to the confusion.
Ack, datasheet names are fine.
You could also use a single property with all device specific coefficients
in a pre-defined order.
I don't have a strong opinion either way.
--
Regards,
Sakari Ailus
sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 0/3] mtd: spi-nor: fix DMA-unsafe buffer issue between MTD and SPI
From: Trent Piepho @ 2017-12-27 20:15 UTC (permalink / raw)
To: broonie@kernel.org
Cc: nicolas.ferre@microchip.com, linux-kernel@vger.kernel.org,
linux-mtd@lists.infradead.org, radu.pirea@microchip.com,
linux@armlinux.org.uk, devicetree@vger.kernel.org,
linux-spi@vger.kernel.org, robh@kernel.org,
cyrille.pitchen@wedev4u.fr, dwmw2@infradead.org,
computersforpeace@gmail.com, vigneshr@ti.com,
boris.brezillon@free-electrons.com, richard@nod.at, marek.vasut
In-Reply-To: <20171227103609.GQ1827@finisterre>
On Wed, 2017-12-27 at 10:36 +0000, Mark Brown wrote:
> On Tue, Dec 26, 2017 at 06:45:28PM +0000, Trent Piepho wrote:
>
> > Or, since this only fixes instances of DMA-unsafe buffers used in
> > access to SPI NOR flash chips, and since there are other SPI master
> > interface users, those chip specific fixes in some/all spi master
> > drivers are still needed to fix transfers not originated via spi-nor?
>
> SPI client drivers are *supposed* to use DMA safe memory already. How
> often that happens in cases where it matters is a separate question, we
> definitely have users with smaller transfers that don't do the right
> thing but they're normally done using PIO anyway.
I wonder what the end goal is here?
A random collection of spi master drivers will accept DMA-unsafe
buffers in some way. In some cases a framework like spi-nor provides
the fixup to spi-nor master drivers (none so far) and in other cases
(atmel-quadspi), the spi-nor master driver has its own fixes.
Generic spi masters like spi-atmel, spi-ti-qspi, and spi-davinci will
have their fixes for certain cases.
Perhaps spi flash drivers like m25p80 will have fixes too?
Some spi clients, like spidev, will have internal bounce buffers,
rather than userspace addresses or commands in stack variables, so that
they follow the rules about DMA safe buffers.
What exactly is caught as DMA unsafe and what is not will of course
vary greatly from driver to driver. Some drivers will catch highmem
memory while other drivers will only detect vmalloc memory. Some will
only catch an unsafe buffer if a specific SoC known to the driver to
have an aliasing cache is enabled. Some will check buffers that arrive
via the spi_flash_read interface but not via generic spi transfers,
while others will check all spi transfer buffers.
Obviously, I don't think this path will lead to a desirable end. Maybe
the basic assumption, that clients should provide DMA safe buffers,
should be revisited. Experience has shown that it's too much to ask
for and spi clients will never get it right. It would be better to try
to fix this at some common point between the clients and masters so it
can be done once and for all.
^ permalink raw reply
* Re: [PATCH] Device tree binding for Avago APDS990X light sensor
From: Pavel Machek @ 2017-12-27 20:01 UTC (permalink / raw)
To: Sakari Ailus
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
pali.rohar-Re5JQEeQqe8AvxtiuMwx3w, sre-DgEjT+Ai2ygdnm+yROfE0A,
linux-arm-kernel, linux-omap-u79uwXL29TY76Z2rM5mHXA,
tony-4v6yS6AI5VpBDgjK7y7TUQ, khilman-DgEjT+Ai2ygdnm+yROfE0A,
aaro.koskinen-X3B1VOXEql0,
ivo.g.dimitrov.75-Re5JQEeQqe8AvxtiuMwx3w,
patrikbachan-Re5JQEeQqe8AvxtiuMwx3w, serge-A9i7LUbDfNHQT0dZR+AlfA,
abcloriens-Re5JQEeQqe8AvxtiuMwx3w, clayton-fehKsxFhGzZIf6P1QZMOBw,
martijn-28JJ9oSIdodmR6Xm/wNWPw, Filip Matijević
In-Reply-To: <20171227180000.6ejpbqmr736nqx5i-sGAanXTfQ4777SC2UrCW1FMQynFLKtET@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 1063 bytes --]
Hi!
> > +Required properties:
> > +- compatible: "avago,apds990x"
> > +- reg: address on the I2C bus
> > +- interrupts: external interrupt line number
> > +- Vdd-supply: power supply for VDD
> > +- Vled-supply: power supply for LEDA
>
> AFAIK the custom is to use lower case letters for regulator supplies.
>
> > +- ga: Glass attenuation
> > +- cf1: Clear channel factor 1
> > +- irf1: IR channel factor 1
> > +- cf2: Clear channel factor 2
> > +- irf2: IR channel factor 2
> > +- df: Device factor
> > +- pdrive: IR current, one of APDS_IRLED_CURR_XXXmA values
> > +- ppcount: Proximity pulse count
>
> Are these device specific? If so, please add the vendor prefix to them.
Well, whole binding is "vendor specific". Does it make sense to add
prefix in such case?
> I might not use short abbreviations such as "df" either. I wonder what
> others think.
I see.
Thanks,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] ARM: dts: imx6: RDU2: correct RTC compatible
From: Andrey Smirnov @ 2017-12-27 19:33 UTC (permalink / raw)
To: Fabio Estevam
Cc: Shawn Guo, Sascha Hauer, Fabio Estevam, Rob Herring, Mark Rutland,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
linux-kernel, Chris Healy, Lucas Stach
In-Reply-To: <CAOMZO5BO=kX6ZDLV75c44VPNOggLzf630SHcyzfAU_+4ME2vDQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Wed, Dec 27, 2017 at 3:11 AM, Fabio Estevam <festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Hi Andrey,
>
> On Wed, Dec 27, 2017 at 1:56 AM, Andrey Smirnov
> <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> The RTC is manufactured by Maxim. This is a cosmetic fix, as Linux
>> doesn't match the vendor string for i2c devices.
>>
>> Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
>> Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
>> Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
>> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
>> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> Cc: cphealy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
>> Signed-off-by: Lucas Stach <l.stach-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
>> Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>
> This patch seems to be from Lucas:
> https://patchwork.kernel.org/patch/10099397/
>
> ,so his name should appear in the From field.
>
> Anyway, this patch has been sent earlier and we suggested to keep the
> existing binding, which is the documented form:
> https://patchwork.kernel.org/patch/10099397/
Yes, the patch is from Lucas and I was just being a messenger. And
understood and thanks for the info.
Sorry for the noise and disregard this patch.
Thanks,
Andrey Smirnov
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] ARM: dts: imx6: RDU2: disable internal watchdog
From: Andrey Smirnov @ 2017-12-27 19:30 UTC (permalink / raw)
To: Fabio Estevam
Cc: Shawn Guo, Mark Rutland,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
linux-kernel, Rob Herring, Chris Healy, Sascha Hauer,
Fabio Estevam,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
Lucas Stach
In-Reply-To: <CAOMZO5AGrJ0f64mLQyjpbiZWusT+J6Q39Rc6+aN-hxX8Xe=R8A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Wed, Dec 27, 2017 at 3:09 AM, Fabio Estevam <festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Hi Andrey,
>
> On Wed, Dec 27, 2017 at 1:56 AM, Andrey Smirnov
> <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> The system has an external watchdog in the environment processor
>> so the internal watchdog is of no use.
>>
>> Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
>> Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
>> Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
>> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
>> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> Cc: cphealy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
>> Signed-off-by: Lucas Stach <l.stach-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
>> Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>
> Patch looks good.
>
> Just not clear if the authorship comes from you or Lucas.
>
> If Lucas is the original author then his name should appear in the From field.
Lucas is the original author and I am just a messenger. Sorry for the
confusion, will send a v2 with "From" fixed.
Sorry for the noise.
Thanks,
Andrey Smirnov
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] Device tree binding for Avago APDS990X light sensor
From: Filip Matijević @ 2017-12-27 18:50 UTC (permalink / raw)
To: Sakari Ailus, Pavel Machek
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
pali.rohar-Re5JQEeQqe8AvxtiuMwx3w, sre-DgEjT+Ai2ygdnm+yROfE0A,
linux-arm-kernel, linux-omap-u79uwXL29TY76Z2rM5mHXA,
tony-4v6yS6AI5VpBDgjK7y7TUQ, khilman-DgEjT+Ai2ygdnm+yROfE0A,
aaro.koskinen-X3B1VOXEql0,
ivo.g.dimitrov.75-Re5JQEeQqe8AvxtiuMwx3w,
patrikbachan-Re5JQEeQqe8AvxtiuMwx3w, serge-A9i7LUbDfNHQT0dZR+AlfA,
abcloriens-Re5JQEeQqe8AvxtiuMwx3w, clayton-fehKsxFhGzZIf6P1QZMOBw,
martijn-28JJ9oSIdodmR6Xm/wNWPw
In-Reply-To: <20171227180000.6ejpbqmr736nqx5i-sGAanXTfQ4777SC2UrCW1FMQynFLKtET@public.gmane.org>
Hi Sakari,
and thank you for your input - I've added a few comments below.
On 12/27/2017 07:00 PM, Sakari Ailus wrote:
> Hi Pavel,
>
> Thanks for the patch. Please see my comments below.
>
> On Wed, Dec 27, 2017 at 10:18:28AM +0100, Pavel Machek wrote:
>> From: Filip Matijević <filip.matijevic.pz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>
>> This prepares binding for light sensor used in Nokia N9.
>>
>> Signed-off-by: Filip Matijević <filip.matijevic.pz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> Signed-off-by: Pavel machek <pavel-+ZI9xUNit7I@public.gmane.org>
>>
>> ---
>>
>> Patches to convert APDS990X driver to device tree and to switch to iio
>> are available.
>>
>> diff --git a/Documentation/devicetree/bindings/misc/avago-apds990x.txt b/Documentation/devicetree/bindings/misc/avago-apds990x.txt
>> new file mode 100644
>> index 0000000..e038146
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/misc/avago-apds990x.txt
>> @@ -0,0 +1,39 @@
>> +Avago APDS990X driver
>> +
>> +Required properties:
>> +- compatible: "avago,apds990x"
>> +- reg: address on the I2C bus
>> +- interrupts: external interrupt line number
>> +- Vdd-supply: power supply for VDD
>> +- Vled-supply: power supply for LEDA
>
> AFAIK the custom is to use lower case letters for regulator supplies.
I've just used the same notation as in current driver. Datasheet calls
those VDD (with DD being in subscript) and LEDA. I see no problem in
changing those to vdd-supply and vled-supply if no one else objects.
>
>> +- ga: Glass attenuation
>> +- cf1: Clear channel factor 1
>> +- irf1: IR channel factor 1
>> +- cf2: Clear channel factor 2
>> +- irf2: IR channel factor 2
>> +- df: Device factor
>> +- pdrive: IR current, one of APDS_IRLED_CURR_XXXmA values
>> +- ppcount: Proximity pulse count
>
> Are these device specific? If so, please add the vendor prefix to them.
AFAIK yes - same as before if no one else objects, these will be changed.
>
> I might not use short abbreviations such as "df" either. I wonder what
> others think.
These are also come from current driver - some of these comes from the
datasheet itself, but not all. For example "ga" and "df" are in there
(so I I would leave those alone), but "irf1" is called "B", "cf2" is
called "C" and "irf2" is called "D" (I guess the missing "cf1" should be
"A", but there is no such thing in the datasheet).
IMHO using some other names might just add to the confusion.
>
>> +
>> +Example (Nokia N9):
>> +
>> + als_ps@39 {
>> + compatible = "avago,apds990x";
>> + reg = <0x39>;
>> +
>> + interrupt-parent = <&gpio3>;
>> + interrupts = <19 10>; /* gpio_83, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW */
>> +
>> + Vdd-supply = <&vaux1>;
>> + Vled-supply = <&vbat>;
>> +
>> + ga = <168834>;
>> + cf1 = <4096>;
>> + irf1 = <7824>;
>> + cf2 = <877>;
>> + irf2 = <1575>;
>> + df = <52>;
>> +
>> + pdrive = <0x2>; /* APDS_IRLED_CURR_25mA */
>> + ppcount = <5>;
>> + };
>>
>
Best regards,
Filip
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 2/2] PCI: mediatek: Set up class type and vendor ID for MT7622
From: Bjorn Helgaas @ 2017-12-27 18:45 UTC (permalink / raw)
To: honghui.zhang-NuS5LvNUpcJWk0Htik3J/w
Cc: bhelgaas-hpIqsD4AKlfQT0dZR+AlfA,
matthias.bgg-Re5JQEeQqe8AvxtiuMwx3w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-pci-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
yingjoe.chen-NuS5LvNUpcJWk0Htik3J/w,
eddie.huang-NuS5LvNUpcJWk0Htik3J/w,
ryder.lee-NuS5LvNUpcJWk0Htik3J/w, lorenzo.pieralisi-5wv7dgnIgG8,
hongkun.cao-NuS5LvNUpcJWk0Htik3J/w,
youlin.pei-NuS5LvNUpcJWk0Htik3J/w, yong.wu-NuS5LvNUpcJWk0Htik3J/w,
yt.shen-NuS5LvNUpcJWk0Htik3J/w, sean.wang-NuS5LvNUpcJWk0Htik3J/w,
xinping.qian-NuS5LvNUpcJWk0Htik3J/w
In-Reply-To: <1514336394-17747-3-git-send-email-honghui.zhang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
On Wed, Dec 27, 2017 at 08:59:54AM +0800, honghui.zhang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org wrote:
> From: Honghui Zhang <honghui.zhang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
>
> The hardware default value of IDs and class type is not correct,
> fix that by setup the correct values before start up.
>
> Signed-off-by: Honghui Zhang <honghui.zhang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> ---
> drivers/pci/host/pcie-mediatek.c | 12 ++++++++++++
> include/linux/pci_ids.h | 2 ++
> 2 files changed, 14 insertions(+)
>
> diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
> index fc29a9a..62aac0ea 100644
> --- a/drivers/pci/host/pcie-mediatek.c
> +++ b/drivers/pci/host/pcie-mediatek.c
> @@ -74,6 +74,10 @@
>
> /* PCIe V2 per-port registers */
> #define PCIE_MSI_VECTOR 0x0c0
> +
> +#define PCIE_CONF_ID 0x100
> +#define PCIE_CONF_CLASS 0x104
> +
> #define PCIE_INT_MASK 0x420
> #define INTX_MASK GENMASK(19, 16)
> #define INTX_SHIFT 16
> @@ -393,6 +397,14 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
> val |= PCIE_CSR_LTSSM_EN(port->slot) |
> PCIE_CSR_ASPM_L1_EN(port->slot);
> writel(val, pcie->base + PCIE_SYS_CFG_V2);
> +
> + /* Set up vendor ID and device ID for MT7622*/
> + val = PCI_VENDOR_ID_MEDIATEK;
> + writel(val, port->base + PCIE_CONF_ID);
> +
> + /* Set up class code for MT7622 */
> + val = PCI_CLASS_BRIDGE_PCI << 16;
> + writel(val, port->base + PCIE_CONF_CLASS);
1) Your comments mention MT7622 specifically, but this code is run for
both mt2712-pcie and mt7622-pcie. If this code is safe and necessary
for both mt2712-pcie and mt7622-pcie, please remove the mention of
MT7622.
2) The first comment mentions both "vendor ID and device ID" but you
don't write the device ID. Since this code applies to both
mt2712-pcie and mt7622-pcie, my guess is that you don't *want* to
write the device ID. If that's the case, please fix the comment.
3) If you only need to set the vendor ID, you're performing a 32-bit
write (writel()) to update a 16-bit value. Please use writew()
instead.
4) If you only need to set the vendor ID, please use a definition like
"PCIE_CONF_VENDOR_ID" instead of the ambiguous "PCIE_CONF_ID".
5) If you only need to set the vendor ID, please update the changelog
to mention "vendor ID" specifically instead of the ambiguous "IDs".
6) Please add a space before the closing "*/" of the first comment.
7) PCI_CLASS_BRIDGE_PCI is for a PCI-to-PCI bridge, i.e., one that has
PCI on both the primary (upstream) side and the secondary (downstream)
side. That kind of bridge has a type 1 config header (see
PCI_HEADER_TYPE) and the PCI_PRIMARY_BUS and PCI_SECONDARY_BUS
registers tell us the bus number of the primary and secondary sides.
I don't believe this device is a PCI-to-PCI bridge. I think it's a
*host* bridge that has some non-PCI interface on the upstream side and
should have a type 0 config header. If that's the case you should use
PCI_CLASS_BRIDGE_HOST instead.
> }
>
> /* Assert all reset signals */
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index ab20dc5..2480b0e 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2113,6 +2113,8 @@
>
> #define PCI_VENDOR_ID_MYRICOM 0x14c1
>
> +#define PCI_VENDOR_ID_MEDIATEK 0x14c3
> +
> #define PCI_VENDOR_ID_TITAN 0x14D2
> #define PCI_DEVICE_ID_TITAN_010L 0x8001
> #define PCI_DEVICE_ID_TITAN_100L 0x8010
> --
> 2.6.4
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 1/4] dt-bindings: usb: add DT binding for RK3328 dwc3 controller
From: Heiko Stuebner @ 2017-12-27 18:22 UTC (permalink / raw)
To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
Cc: balbi-DgEjT+Ai2ygdnm+yROfE0A, william.wu-TNX95d0MmH7DzftRWevZcw,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20171204094041.25439-1-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
Am Montag, 4. Dezember 2017, 10:40:38 CET schrieb Heiko Stuebner:
> From: William Wu <william.wu-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>
> Adds the device tree bindings description for RK3328 and
> compatible USB DWC3 controller.
>
> Signed-off-by: William Wu <william.wu-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
applied all 4 with Felipe's Ack on patch1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] Device tree binding for Avago APDS990X light sensor
From: Sakari Ailus @ 2017-12-27 18:00 UTC (permalink / raw)
To: Pavel Machek
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
pali.rohar-Re5JQEeQqe8AvxtiuMwx3w, sre-DgEjT+Ai2ygdnm+yROfE0A,
linux-arm-kernel, linux-omap-u79uwXL29TY76Z2rM5mHXA,
tony-4v6yS6AI5VpBDgjK7y7TUQ, khilman-DgEjT+Ai2ygdnm+yROfE0A,
aaro.koskinen-X3B1VOXEql0,
ivo.g.dimitrov.75-Re5JQEeQqe8AvxtiuMwx3w,
patrikbachan-Re5JQEeQqe8AvxtiuMwx3w, serge-A9i7LUbDfNHQT0dZR+AlfA,
abcloriens-Re5JQEeQqe8AvxtiuMwx3w, clayton-fehKsxFhGzZIf6P1QZMOBw,
martijn-28JJ9oSIdodmR6Xm/wNWPw, Filip Matijević
In-Reply-To: <20171227091828.GA3307@amd>
Hi Pavel,
Thanks for the patch. Please see my comments below.
On Wed, Dec 27, 2017 at 10:18:28AM +0100, Pavel Machek wrote:
> From: Filip Matijević <filip.matijevic.pz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>
> This prepares binding for light sensor used in Nokia N9.
>
> Signed-off-by: Filip Matijević <filip.matijevic.pz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Signed-off-by: Pavel machek <pavel-+ZI9xUNit7I@public.gmane.org>
>
> ---
>
> Patches to convert APDS990X driver to device tree and to switch to iio
> are available.
>
> diff --git a/Documentation/devicetree/bindings/misc/avago-apds990x.txt b/Documentation/devicetree/bindings/misc/avago-apds990x.txt
> new file mode 100644
> index 0000000..e038146
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/avago-apds990x.txt
> @@ -0,0 +1,39 @@
> +Avago APDS990X driver
> +
> +Required properties:
> +- compatible: "avago,apds990x"
> +- reg: address on the I2C bus
> +- interrupts: external interrupt line number
> +- Vdd-supply: power supply for VDD
> +- Vled-supply: power supply for LEDA
AFAIK the custom is to use lower case letters for regulator supplies.
> +- ga: Glass attenuation
> +- cf1: Clear channel factor 1
> +- irf1: IR channel factor 1
> +- cf2: Clear channel factor 2
> +- irf2: IR channel factor 2
> +- df: Device factor
> +- pdrive: IR current, one of APDS_IRLED_CURR_XXXmA values
> +- ppcount: Proximity pulse count
Are these device specific? If so, please add the vendor prefix to them.
I might not use short abbreviations such as "df" either. I wonder what
others think.
> +
> +Example (Nokia N9):
> +
> + als_ps@39 {
> + compatible = "avago,apds990x";
> + reg = <0x39>;
> +
> + interrupt-parent = <&gpio3>;
> + interrupts = <19 10>; /* gpio_83, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW */
> +
> + Vdd-supply = <&vaux1>;
> + Vled-supply = <&vbat>;
> +
> + ga = <168834>;
> + cf1 = <4096>;
> + irf1 = <7824>;
> + cf2 = <877>;
> + irf2 = <1575>;
> + df = <52>;
> +
> + pdrive = <0x2>; /* APDS_IRLED_CURR_25mA */
> + ppcount = <5>;
> + };
>
--
Kind regards,
Sakari Ailus
sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 1/5] dt-bindings: at24: consistently document the compatible property
From: Rob Herring @ 2017-12-27 17:24 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Andy Shevchenko, Mark Rutland, Javier Martinez Canillas,
David Lechner, Divagar Mohandass, Peter Rosin, Linux I2C,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
linux-kernel@vger.kernel.org
In-Reply-To: <20171227135054.29690-2-brgl@bgdev.pl>
On Wed, Dec 27, 2017 at 7:50 AM, Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> Current description of the compatible property for at24 is quite vague.
>
> State explicitly that any "<manufacturer>,<model>" pair is accepted as
> long as one of the listed strings is used as fallback.
>
> Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
> ---
> Documentation/devicetree/bindings/eeprom/at24.txt | 37 +++++++++--------------
> 1 file changed, 15 insertions(+), 22 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/eeprom/at24.txt b/Documentation/devicetree/bindings/eeprom/at24.txt
> index cbc80e194ac6..b5ce5a247554 100644
> --- a/Documentation/devicetree/bindings/eeprom/at24.txt
> +++ b/Documentation/devicetree/bindings/eeprom/at24.txt
> @@ -2,28 +2,21 @@ EEPROMs (I2C)
>
> Required properties:
>
> - - compatible : should be "<manufacturer>,<type>", like these:
> -
> - "atmel,24c00", "atmel,24c01", "atmel,24c02", "atmel,24c04",
> - "atmel,24c08", "atmel,24c16", "atmel,24c32", "atmel,24c64",
> - "atmel,24c128", "atmel,24c256", "atmel,24c512", "atmel,24c1024"
> -
> - "catalyst,24c32"
> -
> - "microchip,24c128"
> -
> - "ramtron,24c64"
> -
> - "renesas,r1ex24002"
> -
> - The following manufacturers values have been deprecated:
> - "at", "at24"
> -
> - If there is no specific driver for <manufacturer>, a generic
> - device with <type> and manufacturer "atmel" should be used.
> - Possible types are:
> - "24c00", "24c01", "24c02", "24c04", "24c08", "24c16", "24c32", "24c64",
> - "24c128", "24c256", "24c512", "24c1024", "spd"
> + - compatible: must be a "<manufacturer>,<model>" pair with one of the
Also, why do you add "must" here and then fix it with "Must" in patch
3? Just get it right here.
Rob
^ permalink raw reply
* Re: [PATCH v3 1/5] dt-bindings: at24: consistently document the compatible property
From: Rob Herring @ 2017-12-27 17:21 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Andy Shevchenko, Mark Rutland, Javier Martinez Canillas,
David Lechner, Divagar Mohandass, Peter Rosin, Linux I2C,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
linux-kernel@vger.kernel.org
In-Reply-To: <20171227135054.29690-2-brgl@bgdev.pl>
On Wed, Dec 27, 2017 at 7:50 AM, Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> Current description of the compatible property for at24 is quite vague.
>
> State explicitly that any "<manufacturer>,<model>" pair is accepted as
> long as one of the listed strings is used as fallback.
>
> Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
> ---
> Documentation/devicetree/bindings/eeprom/at24.txt | 37 +++++++++--------------
> 1 file changed, 15 insertions(+), 22 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/eeprom/at24.txt b/Documentation/devicetree/bindings/eeprom/at24.txt
> index cbc80e194ac6..b5ce5a247554 100644
> --- a/Documentation/devicetree/bindings/eeprom/at24.txt
> +++ b/Documentation/devicetree/bindings/eeprom/at24.txt
> @@ -2,28 +2,21 @@ EEPROMs (I2C)
>
> Required properties:
>
> - - compatible : should be "<manufacturer>,<type>", like these:
> -
> - "atmel,24c00", "atmel,24c01", "atmel,24c02", "atmel,24c04",
> - "atmel,24c08", "atmel,24c16", "atmel,24c32", "atmel,24c64",
> - "atmel,24c128", "atmel,24c256", "atmel,24c512", "atmel,24c1024"
> -
> - "catalyst,24c32"
> -
> - "microchip,24c128"
> -
> - "ramtron,24c64"
> -
> - "renesas,r1ex24002"
Keep this and any others that don't have an atmel model. This should
probably also state what the fallback should be (atmel,24c02 ?).
> -
> - The following manufacturers values have been deprecated:
> - "at", "at24"
> -
> - If there is no specific driver for <manufacturer>, a generic
> - device with <type> and manufacturer "atmel" should be used.
> - Possible types are:
> - "24c00", "24c01", "24c02", "24c04", "24c08", "24c16", "24c32", "24c64",
> - "24c128", "24c256", "24c512", "24c1024", "spd"
You remove "spd" here and add it back later. Just fold that in below.
I'll leave it to others to bikeshed where in the list.
> + - compatible: must be a "<manufacturer>,<model>" pair with one of the
We should enumerate known vendors/manufacturers because something like
"linaro,24c00" is not valid.
> + following values as fallback:
> +
> + "atmel,24c00",
> + "atmel,24c01",
> + "atmel,24c02",
> + "atmel,24c04",
> + "atmel,24c08",
> + "atmel,24c16",
> + "atmel,24c32",
> + "atmel,24c64",
> + "atmel,24c128",
> + "atmel,24c256",
> + "atmel,24c512",
> + "atmel,24c1024"
>
> - reg : the I2C address of the EEPROM
>
> --
> 2.15.1
>
^ permalink raw reply
* Re: [PATCH] PCI: exynos: remove the deprecated phy codes
From: Jingoo Han @ 2017-12-27 17:05 UTC (permalink / raw)
To: 'Jaehoon Chung', linux-pci-u79uwXL29TY76Z2rM5mHXA
Cc: linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, 'Bjorn Helgaas',
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, krzk-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, kgene-DgEjT+Ai2ygdnm+yROfE0A,
lorenzo.pieralisi-5wv7dgnIgG8
In-Reply-To: <20171227094327.5671-1-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
On Wednesday, December 27, 2017 1:43 AM, Jaehoon Chung wrote:
>
> pci-exynos had updated to use the PHY framework.
> (drivers/phy/samsung/phy-exynos-pcie.c)
> Removed the depreccated codes relevant to phy in pci-exynos.c.
> Instead, use the phy-exynos-pcie.c file.
>
> Modified the binding documentation.
>
> Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
(I resend my email, because Bjorn's address was wrong in the previous
email.)
Thank you for your patch.
It looks good.
Acked-by: Jingoo Han <jingoohan1-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Best regards,
Jingoo Han
> ---
> .../bindings/pci/samsung,exynos5440-pcie.txt | 58 ++----
> drivers/pci/dwc/pci-exynos.c | 219
++-------------------
> 2 files changed, 22 insertions(+), 255 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/pci/samsung,exynos5440-
> pcie.txt b/Documentation/devicetree/bindings/pci/samsung,exynos5440-
> pcie.txt
> index 34a11bfbfb60..651d957d1051 100644
> --- a/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt
> +++ b/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt
> @@ -6,9 +6,6 @@ and thus inherits all the common properties defined in
> designware-pcie.txt.
> Required properties:
> - compatible: "samsung,exynos5440-pcie"
> - reg: base addresses and lengths of the PCIe controller,
> - the PHY controller, additional register for the PHY controller.
> - (Registers for the PHY controller are DEPRECATED.
> - Use the PHY framework.)
> - reg-names : First name should be set to "elbi".
> And use the "config" instead of getting the configuration address
> space
> from "ranges".
> @@ -23,49 +20,8 @@ For other common properties, refer to
>
> Example:
>
> -SoC-specific DT Entry:
> +SoC-specific DT Entry (with using PHY framework):
>
> - pcie@290000 {
> - compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
> - reg = <0x290000 0x1000
> - 0x270000 0x1000
> - 0x271000 0x40>;
> - interrupts = <0 20 0>, <0 21 0>, <0 22 0>;
> - clocks = <&clock 28>, <&clock 27>;
> - clock-names = "pcie", "pcie_bus";
> - #address-cells = <3>;
> - #size-cells = <2>;
> - device_type = "pci";
> - ranges = <0x00000800 0 0x40000000 0x40000000 0 0x00001000
> /* configuration space */
> - 0x81000000 0 0 0x40001000 0 0x00010000
/*
> downstream I/O */
> - 0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>;
/*
> non-prefetchable memory */
> - #interrupt-cells = <1>;
> - interrupt-map-mask = <0 0 0 0>;
> - interrupt-map = <0 0 0 0 &gic GIC_SPI 21
> IRQ_TYPE_LEVEL_HIGH>;
> - num-lanes = <4>;
> - };
> -
> - pcie@2a0000 {
> - compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
> - reg = <0x2a0000 0x1000
> - 0x272000 0x1000
> - 0x271040 0x40>;
> - interrupts = <0 23 0>, <0 24 0>, <0 25 0>;
> - clocks = <&clock 29>, <&clock 27>;
> - clock-names = "pcie", "pcie_bus";
> - #address-cells = <3>;
> - #size-cells = <2>;
> - device_type = "pci";
> - ranges = <0x00000800 0 0x60000000 0x60000000 0 0x00001000
> /* configuration space */
> - 0x81000000 0 0 0x60001000 0 0x00010000
/*
> downstream I/O */
> - 0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>;
/*
> non-prefetchable memory */
> - #interrupt-cells = <1>;
> - interrupt-map-mask = <0 0 0 0>;
> - interrupt-map = <0 0 0 0 &gic GIC_SPI 24
> IRQ_TYPE_LEVEL_HIGH>;
> - num-lanes = <4>;
> - };
> -
> -With using PHY framework:
> pcie_phy0: pcie-phy@270000 {
> ...
> reg = <0x270000 0x1000>, <0x271000 0x40>;
> @@ -74,13 +30,21 @@ With using PHY framework:
> };
>
> pcie@290000 {
> - ...
> + compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
> reg = <0x290000 0x1000>, <0x40000000 0x1000>;
> reg-names = "elbi", "config";
> + clocks = <&clock 28>, <&clock 27>;
> + clock-names = "pcie", "pcie_bus";
> + #address-cells = <3>;
> + #size-cells = <2>;
> + device_type = "pci";
> phys = <&pcie_phy0>;
> ranges = <0x81000000 0 0 0x60001000 0 0x00010000
> 0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>;
> - ...
> + #interrupt-cells = <1>;
> + interrupt-map-mask = <0 0 0 0>;
> + interrupt-map = <0 0 0 0 &gic GIC_SPI 21
> IRQ_TYPE_LEVEL_HIGH>;
> + num-lanes = <4>;
> };
>
> Board-specific DT Entry:
> diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c
> index 5596fdedbb94..56f32aeebd0a 100644
> --- a/drivers/pci/dwc/pci-exynos.c
> +++ b/drivers/pci/dwc/pci-exynos.c
> @@ -55,49 +55,8 @@
> #define PCIE_ELBI_SLV_ARMISC 0x120
> #define PCIE_ELBI_SLV_DBI_ENABLE BIT(21)
>
> -/* PCIe Purple registers */
> -#define PCIE_PHY_GLOBAL_RESET 0x000
> -#define PCIE_PHY_COMMON_RESET 0x004
> -#define PCIE_PHY_CMN_REG 0x008
> -#define PCIE_PHY_MAC_RESET 0x00c
> -#define PCIE_PHY_PLL_LOCKED 0x010
> -#define PCIE_PHY_TRSVREG_RESET 0x020
> -#define PCIE_PHY_TRSV_RESET 0x024
> -
> -/* PCIe PHY registers */
> -#define PCIE_PHY_IMPEDANCE 0x004
> -#define PCIE_PHY_PLL_DIV_0 0x008
> -#define PCIE_PHY_PLL_BIAS 0x00c
> -#define PCIE_PHY_DCC_FEEDBACK 0x014
> -#define PCIE_PHY_PLL_DIV_1 0x05c
> -#define PCIE_PHY_COMMON_POWER 0x064
> -#define PCIE_PHY_COMMON_PD_CMN BIT(3)
> -#define PCIE_PHY_TRSV0_EMP_LVL 0x084
> -#define PCIE_PHY_TRSV0_DRV_LVL 0x088
> -#define PCIE_PHY_TRSV0_RXCDR 0x0ac
> -#define PCIE_PHY_TRSV0_POWER 0x0c4
> -#define PCIE_PHY_TRSV0_PD_TSV BIT(7)
> -#define PCIE_PHY_TRSV0_LVCC 0x0dc
> -#define PCIE_PHY_TRSV1_EMP_LVL 0x144
> -#define PCIE_PHY_TRSV1_RXCDR 0x16c
> -#define PCIE_PHY_TRSV1_POWER 0x184
> -#define PCIE_PHY_TRSV1_PD_TSV BIT(7)
> -#define PCIE_PHY_TRSV1_LVCC 0x19c
> -#define PCIE_PHY_TRSV2_EMP_LVL 0x204
> -#define PCIE_PHY_TRSV2_RXCDR 0x22c
> -#define PCIE_PHY_TRSV2_POWER 0x244
> -#define PCIE_PHY_TRSV2_PD_TSV BIT(7)
> -#define PCIE_PHY_TRSV2_LVCC 0x25c
> -#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4
> -#define PCIE_PHY_TRSV3_RXCDR 0x2ec
> -#define PCIE_PHY_TRSV3_POWER 0x304
> -#define PCIE_PHY_TRSV3_PD_TSV BIT(7)
> -#define PCIE_PHY_TRSV3_LVCC 0x31c
> -
> struct exynos_pcie_mem_res {
> void __iomem *elbi_base; /* DT 0th resource: PCIe CTRL */
> - void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
> - void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL
> */
> };
>
> struct exynos_pcie_clk_res {
> @@ -112,8 +71,6 @@ struct exynos_pcie {
> const struct exynos_pcie_ops *ops;
> int reset_gpio;
>
> - /* For Generic PHY Framework */
> - bool using_phy;
> struct phy *phy;
> };
>
> @@ -141,20 +98,6 @@ static int exynos5440_pcie_get_mem_resources(struct
> platform_device *pdev,
> if (IS_ERR(ep->mem_res->elbi_base))
> return PTR_ERR(ep->mem_res->elbi_base);
>
> - /* If using the PHY framework, doesn't need to get other resource
> */
> - if (ep->using_phy)
> - return 0;
> -
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> - ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(ep->mem_res->phy_base))
> - return PTR_ERR(ep->mem_res->phy_base);
> -
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> - ep->mem_res->block_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(ep->mem_res->block_base))
> - return PTR_ERR(ep->mem_res->block_base);
> -
> return 0;
> }
>
> @@ -279,111 +222,6 @@ static void exynos_pcie_deassert_core_reset(struct
> exynos_pcie *ep)
> exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
> exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
> exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
> -}
> -
> -static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
> -{
> - exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 1,
> PCIE_PHY_GLOBAL_RESET);
> -}
> -
> -static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
> -{
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> PCIE_PHY_GLOBAL_RESET);
> - exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> PCIE_PHY_COMMON_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> PCIE_PHY_TRSVREG_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
> -}
> -
> -static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
> -{
> - u32 val;
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_COMMON_POWER);
> - val &= ~PCIE_PHY_COMMON_PD_CMN;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_COMMON_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV0_POWER);
> - val &= ~PCIE_PHY_TRSV0_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV0_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV1_POWER);
> - val &= ~PCIE_PHY_TRSV1_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV1_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV2_POWER);
> - val &= ~PCIE_PHY_TRSV2_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV2_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV3_POWER);
> - val &= ~PCIE_PHY_TRSV3_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV3_POWER);
> -}
> -
> -static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
> -{
> - u32 val;
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_COMMON_POWER);
> - val |= PCIE_PHY_COMMON_PD_CMN;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_COMMON_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV0_POWER);
> - val |= PCIE_PHY_TRSV0_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV0_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV1_POWER);
> - val |= PCIE_PHY_TRSV1_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV1_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV2_POWER);
> - val |= PCIE_PHY_TRSV2_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV2_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV3_POWER);
> - val |= PCIE_PHY_TRSV3_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV3_POWER);
> -}
> -
> -static void exynos_pcie_init_phy(struct exynos_pcie *ep)
> -{
> - /* DCC feedback control off */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x29,
> PCIE_PHY_DCC_FEEDBACK);
> -
> - /* set TX/RX impedance */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
> -
> - /* set 50Mhz PHY clock */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
> -
> - /* set TX Differential output for lane 0 */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x7f,
> PCIE_PHY_TRSV0_DRV_LVL);
> -
> - /* set TX Pre-emphasis Level Control for lane 0 to minimum */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x0,
> PCIE_PHY_TRSV0_EMP_LVL);
> -
> - /* set RX clock and data recovery bandwidth */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
> PCIE_PHY_TRSV0_RXCDR);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
> PCIE_PHY_TRSV1_RXCDR);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
> PCIE_PHY_TRSV2_RXCDR);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
> PCIE_PHY_TRSV3_RXCDR);
> -
> - /* change TX Pre-emphasis Level Control for lanes */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
> PCIE_PHY_TRSV0_EMP_LVL);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
> PCIE_PHY_TRSV1_EMP_LVL);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
> PCIE_PHY_TRSV2_EMP_LVL);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
> PCIE_PHY_TRSV3_EMP_LVL);
> -
> - /* set LVCC */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x20,
> PCIE_PHY_TRSV0_LVCC);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xa0,
> PCIE_PHY_TRSV1_LVCC);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xa0,
> PCIE_PHY_TRSV2_LVCC);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xa0,
> PCIE_PHY_TRSV3_LVCC);
> }
>
> static void exynos_pcie_assert_reset(struct exynos_pcie *ep)
> @@ -401,7 +239,6 @@ static int exynos_pcie_establish_link(struct
> exynos_pcie *ep)
> struct dw_pcie *pci = ep->pci;
> struct pcie_port *pp = &pci->pp;
> struct device *dev = pci->dev;
> - u32 val;
>
> if (dw_pcie_link_up(pci)) {
> dev_err(dev, "Link already up\n");
> @@ -410,32 +247,13 @@ static int exynos_pcie_establish_link(struct
> exynos_pcie *ep)
>
> exynos_pcie_assert_core_reset(ep);
>
> - if (ep->using_phy) {
> - phy_reset(ep->phy);
> -
> - exynos_pcie_writel(ep->mem_res->elbi_base, 1,
> - PCIE_PWR_RESET);
> -
> - phy_power_on(ep->phy);
> - phy_init(ep->phy);
> - } else {
> - exynos_pcie_assert_phy_reset(ep);
> - exynos_pcie_deassert_phy_reset(ep);
> - exynos_pcie_power_on_phy(ep);
> - exynos_pcie_init_phy(ep);
> -
> - /* pulse for common reset */
> - exynos_pcie_writel(ep->mem_res->block_base, 1,
> - PCIE_PHY_COMMON_RESET);
> - udelay(500);
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> - PCIE_PHY_COMMON_RESET);
> - }
> + phy_reset(ep->phy);
>
> - /* pulse for common reset */
> - exynos_pcie_writel(ep->mem_res->block_base, 1,
> PCIE_PHY_COMMON_RESET);
> - udelay(500);
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> PCIE_PHY_COMMON_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1,
> + PCIE_PWR_RESET);
> +
> + phy_power_on(ep->phy);
> + phy_init(ep->phy);
>
> exynos_pcie_deassert_core_reset(ep);
> dw_pcie_setup_rc(pp);
> @@ -449,18 +267,7 @@ static int exynos_pcie_establish_link(struct
> exynos_pcie *ep)
> if (!dw_pcie_wait_for_link(pci))
> return 0;
>
> - if (ep->using_phy) {
> - phy_power_off(ep->phy);
> - return -ETIMEDOUT;
> - }
> -
> - while (exynos_pcie_readl(ep->mem_res->phy_base,
> - PCIE_PHY_PLL_LOCKED) == 0) {
> - val = exynos_pcie_readl(ep->mem_res->block_base,
> - PCIE_PHY_PLL_LOCKED);
> - dev_info(dev, "PLL Locked: 0x%x\n", val);
> - }
> - exynos_pcie_power_off_phy(ep);
> + phy_power_off(ep->phy);
> return -ETIMEDOUT;
> }
>
> @@ -678,16 +485,13 @@ static int __init exynos_pcie_probe(struct
> platform_device *pdev)
>
> ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>
> - /* Assume that controller doesn't use the PHY framework */
> - ep->using_phy = false;
> -
> ep->phy = devm_of_phy_get(dev, np, NULL);
> if (IS_ERR(ep->phy)) {
> if (PTR_ERR(ep->phy) == -EPROBE_DEFER)
> return PTR_ERR(ep->phy);
> - dev_warn(dev, "Use the 'phy' property. Current DT of pci-
> exynos was deprecated!!\n");
> - } else
> - ep->using_phy = true;
> +
> + ep->phy = NULL;
> + }
>
> if (ep->ops && ep->ops->get_mem_resources) {
> ret = ep->ops->get_mem_resources(pdev, ep);
> @@ -713,8 +517,7 @@ static int __init exynos_pcie_probe(struct
> platform_device *pdev)
> return 0;
>
> fail_probe:
> - if (ep->using_phy)
> - phy_exit(ep->phy);
> + phy_exit(ep->phy);
>
> if (ep->ops && ep->ops->deinit_clk_resources)
> ep->ops->deinit_clk_resources(ep);
> --
> 2.15.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] PCI: exynos: remove the deprecated phy codes
From: Jingoo Han @ 2017-12-27 17:01 UTC (permalink / raw)
To: 'Jaehoon Chung', linux-pci
Cc: linux-samsung-soc, devicetree, linux-kernel, bhelgaas, robh+dt,
krzk, mark.rutland, kgene, lorenzo.pieralisi
In-Reply-To: <20171227094327.5671-1-jh80.chung@samsung.com>
On Wednesday, December 27, 2017 1:43 AM, Jaehoon Chung wrote:
>
> pci-exynos had updated to use the PHY framework.
> (drivers/phy/samsung/phy-exynos-pcie.c)
> Removed the depreccated codes relevant to phy in pci-exynos.c.
> Instead, use the phy-exynos-pcie.c file.
>
> Modified the binding documentation.
>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Thank you for your patch.
It looks good.
Acked-by: Jingoo Han <jingoohan1@gmail.com>
Best regards,
Jingoo Han
> ---
> .../bindings/pci/samsung,exynos5440-pcie.txt | 58 ++----
> drivers/pci/dwc/pci-exynos.c | 219
++-------------------
> 2 files changed, 22 insertions(+), 255 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/pci/samsung,exynos5440-
> pcie.txt b/Documentation/devicetree/bindings/pci/samsung,exynos5440-
> pcie.txt
> index 34a11bfbfb60..651d957d1051 100644
> --- a/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt
> +++ b/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt
> @@ -6,9 +6,6 @@ and thus inherits all the common properties defined in
> designware-pcie.txt.
> Required properties:
> - compatible: "samsung,exynos5440-pcie"
> - reg: base addresses and lengths of the PCIe controller,
> - the PHY controller, additional register for the PHY controller.
> - (Registers for the PHY controller are DEPRECATED.
> - Use the PHY framework.)
> - reg-names : First name should be set to "elbi".
> And use the "config" instead of getting the configuration address
> space
> from "ranges".
> @@ -23,49 +20,8 @@ For other common properties, refer to
>
> Example:
>
> -SoC-specific DT Entry:
> +SoC-specific DT Entry (with using PHY framework):
>
> - pcie@290000 {
> - compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
> - reg = <0x290000 0x1000
> - 0x270000 0x1000
> - 0x271000 0x40>;
> - interrupts = <0 20 0>, <0 21 0>, <0 22 0>;
> - clocks = <&clock 28>, <&clock 27>;
> - clock-names = "pcie", "pcie_bus";
> - #address-cells = <3>;
> - #size-cells = <2>;
> - device_type = "pci";
> - ranges = <0x00000800 0 0x40000000 0x40000000 0 0x00001000
> /* configuration space */
> - 0x81000000 0 0 0x40001000 0 0x00010000
/*
> downstream I/O */
> - 0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>;
/*
> non-prefetchable memory */
> - #interrupt-cells = <1>;
> - interrupt-map-mask = <0 0 0 0>;
> - interrupt-map = <0 0 0 0 &gic GIC_SPI 21
> IRQ_TYPE_LEVEL_HIGH>;
> - num-lanes = <4>;
> - };
> -
> - pcie@2a0000 {
> - compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
> - reg = <0x2a0000 0x1000
> - 0x272000 0x1000
> - 0x271040 0x40>;
> - interrupts = <0 23 0>, <0 24 0>, <0 25 0>;
> - clocks = <&clock 29>, <&clock 27>;
> - clock-names = "pcie", "pcie_bus";
> - #address-cells = <3>;
> - #size-cells = <2>;
> - device_type = "pci";
> - ranges = <0x00000800 0 0x60000000 0x60000000 0 0x00001000
> /* configuration space */
> - 0x81000000 0 0 0x60001000 0 0x00010000
/*
> downstream I/O */
> - 0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>;
/*
> non-prefetchable memory */
> - #interrupt-cells = <1>;
> - interrupt-map-mask = <0 0 0 0>;
> - interrupt-map = <0 0 0 0 &gic GIC_SPI 24
> IRQ_TYPE_LEVEL_HIGH>;
> - num-lanes = <4>;
> - };
> -
> -With using PHY framework:
> pcie_phy0: pcie-phy@270000 {
> ...
> reg = <0x270000 0x1000>, <0x271000 0x40>;
> @@ -74,13 +30,21 @@ With using PHY framework:
> };
>
> pcie@290000 {
> - ...
> + compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
> reg = <0x290000 0x1000>, <0x40000000 0x1000>;
> reg-names = "elbi", "config";
> + clocks = <&clock 28>, <&clock 27>;
> + clock-names = "pcie", "pcie_bus";
> + #address-cells = <3>;
> + #size-cells = <2>;
> + device_type = "pci";
> phys = <&pcie_phy0>;
> ranges = <0x81000000 0 0 0x60001000 0 0x00010000
> 0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>;
> - ...
> + #interrupt-cells = <1>;
> + interrupt-map-mask = <0 0 0 0>;
> + interrupt-map = <0 0 0 0 &gic GIC_SPI 21
> IRQ_TYPE_LEVEL_HIGH>;
> + num-lanes = <4>;
> };
>
> Board-specific DT Entry:
> diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c
> index 5596fdedbb94..56f32aeebd0a 100644
> --- a/drivers/pci/dwc/pci-exynos.c
> +++ b/drivers/pci/dwc/pci-exynos.c
> @@ -55,49 +55,8 @@
> #define PCIE_ELBI_SLV_ARMISC 0x120
> #define PCIE_ELBI_SLV_DBI_ENABLE BIT(21)
>
> -/* PCIe Purple registers */
> -#define PCIE_PHY_GLOBAL_RESET 0x000
> -#define PCIE_PHY_COMMON_RESET 0x004
> -#define PCIE_PHY_CMN_REG 0x008
> -#define PCIE_PHY_MAC_RESET 0x00c
> -#define PCIE_PHY_PLL_LOCKED 0x010
> -#define PCIE_PHY_TRSVREG_RESET 0x020
> -#define PCIE_PHY_TRSV_RESET 0x024
> -
> -/* PCIe PHY registers */
> -#define PCIE_PHY_IMPEDANCE 0x004
> -#define PCIE_PHY_PLL_DIV_0 0x008
> -#define PCIE_PHY_PLL_BIAS 0x00c
> -#define PCIE_PHY_DCC_FEEDBACK 0x014
> -#define PCIE_PHY_PLL_DIV_1 0x05c
> -#define PCIE_PHY_COMMON_POWER 0x064
> -#define PCIE_PHY_COMMON_PD_CMN BIT(3)
> -#define PCIE_PHY_TRSV0_EMP_LVL 0x084
> -#define PCIE_PHY_TRSV0_DRV_LVL 0x088
> -#define PCIE_PHY_TRSV0_RXCDR 0x0ac
> -#define PCIE_PHY_TRSV0_POWER 0x0c4
> -#define PCIE_PHY_TRSV0_PD_TSV BIT(7)
> -#define PCIE_PHY_TRSV0_LVCC 0x0dc
> -#define PCIE_PHY_TRSV1_EMP_LVL 0x144
> -#define PCIE_PHY_TRSV1_RXCDR 0x16c
> -#define PCIE_PHY_TRSV1_POWER 0x184
> -#define PCIE_PHY_TRSV1_PD_TSV BIT(7)
> -#define PCIE_PHY_TRSV1_LVCC 0x19c
> -#define PCIE_PHY_TRSV2_EMP_LVL 0x204
> -#define PCIE_PHY_TRSV2_RXCDR 0x22c
> -#define PCIE_PHY_TRSV2_POWER 0x244
> -#define PCIE_PHY_TRSV2_PD_TSV BIT(7)
> -#define PCIE_PHY_TRSV2_LVCC 0x25c
> -#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4
> -#define PCIE_PHY_TRSV3_RXCDR 0x2ec
> -#define PCIE_PHY_TRSV3_POWER 0x304
> -#define PCIE_PHY_TRSV3_PD_TSV BIT(7)
> -#define PCIE_PHY_TRSV3_LVCC 0x31c
> -
> struct exynos_pcie_mem_res {
> void __iomem *elbi_base; /* DT 0th resource: PCIe CTRL */
> - void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
> - void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL
> */
> };
>
> struct exynos_pcie_clk_res {
> @@ -112,8 +71,6 @@ struct exynos_pcie {
> const struct exynos_pcie_ops *ops;
> int reset_gpio;
>
> - /* For Generic PHY Framework */
> - bool using_phy;
> struct phy *phy;
> };
>
> @@ -141,20 +98,6 @@ static int exynos5440_pcie_get_mem_resources(struct
> platform_device *pdev,
> if (IS_ERR(ep->mem_res->elbi_base))
> return PTR_ERR(ep->mem_res->elbi_base);
>
> - /* If using the PHY framework, doesn't need to get other resource
> */
> - if (ep->using_phy)
> - return 0;
> -
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> - ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(ep->mem_res->phy_base))
> - return PTR_ERR(ep->mem_res->phy_base);
> -
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> - ep->mem_res->block_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(ep->mem_res->block_base))
> - return PTR_ERR(ep->mem_res->block_base);
> -
> return 0;
> }
>
> @@ -279,111 +222,6 @@ static void exynos_pcie_deassert_core_reset(struct
> exynos_pcie *ep)
> exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
> exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
> exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
> -}
> -
> -static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
> -{
> - exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 1,
> PCIE_PHY_GLOBAL_RESET);
> -}
> -
> -static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
> -{
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> PCIE_PHY_GLOBAL_RESET);
> - exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> PCIE_PHY_COMMON_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> PCIE_PHY_TRSVREG_RESET);
> - exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
> -}
> -
> -static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
> -{
> - u32 val;
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_COMMON_POWER);
> - val &= ~PCIE_PHY_COMMON_PD_CMN;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_COMMON_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV0_POWER);
> - val &= ~PCIE_PHY_TRSV0_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV0_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV1_POWER);
> - val &= ~PCIE_PHY_TRSV1_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV1_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV2_POWER);
> - val &= ~PCIE_PHY_TRSV2_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV2_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV3_POWER);
> - val &= ~PCIE_PHY_TRSV3_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV3_POWER);
> -}
> -
> -static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
> -{
> - u32 val;
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_COMMON_POWER);
> - val |= PCIE_PHY_COMMON_PD_CMN;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_COMMON_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV0_POWER);
> - val |= PCIE_PHY_TRSV0_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV0_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV1_POWER);
> - val |= PCIE_PHY_TRSV1_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV1_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV2_POWER);
> - val |= PCIE_PHY_TRSV2_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV2_POWER);
> -
> - val = exynos_pcie_readl(ep->mem_res->phy_base,
> PCIE_PHY_TRSV3_POWER);
> - val |= PCIE_PHY_TRSV3_PD_TSV;
> - exynos_pcie_writel(ep->mem_res->phy_base, val,
> PCIE_PHY_TRSV3_POWER);
> -}
> -
> -static void exynos_pcie_init_phy(struct exynos_pcie *ep)
> -{
> - /* DCC feedback control off */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x29,
> PCIE_PHY_DCC_FEEDBACK);
> -
> - /* set TX/RX impedance */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
> -
> - /* set 50Mhz PHY clock */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
> -
> - /* set TX Differential output for lane 0 */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x7f,
> PCIE_PHY_TRSV0_DRV_LVL);
> -
> - /* set TX Pre-emphasis Level Control for lane 0 to minimum */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x0,
> PCIE_PHY_TRSV0_EMP_LVL);
> -
> - /* set RX clock and data recovery bandwidth */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
> PCIE_PHY_TRSV0_RXCDR);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
> PCIE_PHY_TRSV1_RXCDR);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
> PCIE_PHY_TRSV2_RXCDR);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
> PCIE_PHY_TRSV3_RXCDR);
> -
> - /* change TX Pre-emphasis Level Control for lanes */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
> PCIE_PHY_TRSV0_EMP_LVL);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
> PCIE_PHY_TRSV1_EMP_LVL);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
> PCIE_PHY_TRSV2_EMP_LVL);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
> PCIE_PHY_TRSV3_EMP_LVL);
> -
> - /* set LVCC */
> - exynos_pcie_writel(ep->mem_res->phy_base, 0x20,
> PCIE_PHY_TRSV0_LVCC);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xa0,
> PCIE_PHY_TRSV1_LVCC);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xa0,
> PCIE_PHY_TRSV2_LVCC);
> - exynos_pcie_writel(ep->mem_res->phy_base, 0xa0,
> PCIE_PHY_TRSV3_LVCC);
> }
>
> static void exynos_pcie_assert_reset(struct exynos_pcie *ep)
> @@ -401,7 +239,6 @@ static int exynos_pcie_establish_link(struct
> exynos_pcie *ep)
> struct dw_pcie *pci = ep->pci;
> struct pcie_port *pp = &pci->pp;
> struct device *dev = pci->dev;
> - u32 val;
>
> if (dw_pcie_link_up(pci)) {
> dev_err(dev, "Link already up\n");
> @@ -410,32 +247,13 @@ static int exynos_pcie_establish_link(struct
> exynos_pcie *ep)
>
> exynos_pcie_assert_core_reset(ep);
>
> - if (ep->using_phy) {
> - phy_reset(ep->phy);
> -
> - exynos_pcie_writel(ep->mem_res->elbi_base, 1,
> - PCIE_PWR_RESET);
> -
> - phy_power_on(ep->phy);
> - phy_init(ep->phy);
> - } else {
> - exynos_pcie_assert_phy_reset(ep);
> - exynos_pcie_deassert_phy_reset(ep);
> - exynos_pcie_power_on_phy(ep);
> - exynos_pcie_init_phy(ep);
> -
> - /* pulse for common reset */
> - exynos_pcie_writel(ep->mem_res->block_base, 1,
> - PCIE_PHY_COMMON_RESET);
> - udelay(500);
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> - PCIE_PHY_COMMON_RESET);
> - }
> + phy_reset(ep->phy);
>
> - /* pulse for common reset */
> - exynos_pcie_writel(ep->mem_res->block_base, 1,
> PCIE_PHY_COMMON_RESET);
> - udelay(500);
> - exynos_pcie_writel(ep->mem_res->block_base, 0,
> PCIE_PHY_COMMON_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1,
> + PCIE_PWR_RESET);
> +
> + phy_power_on(ep->phy);
> + phy_init(ep->phy);
>
> exynos_pcie_deassert_core_reset(ep);
> dw_pcie_setup_rc(pp);
> @@ -449,18 +267,7 @@ static int exynos_pcie_establish_link(struct
> exynos_pcie *ep)
> if (!dw_pcie_wait_for_link(pci))
> return 0;
>
> - if (ep->using_phy) {
> - phy_power_off(ep->phy);
> - return -ETIMEDOUT;
> - }
> -
> - while (exynos_pcie_readl(ep->mem_res->phy_base,
> - PCIE_PHY_PLL_LOCKED) == 0) {
> - val = exynos_pcie_readl(ep->mem_res->block_base,
> - PCIE_PHY_PLL_LOCKED);
> - dev_info(dev, "PLL Locked: 0x%x\n", val);
> - }
> - exynos_pcie_power_off_phy(ep);
> + phy_power_off(ep->phy);
> return -ETIMEDOUT;
> }
>
> @@ -678,16 +485,13 @@ static int __init exynos_pcie_probe(struct
> platform_device *pdev)
>
> ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>
> - /* Assume that controller doesn't use the PHY framework */
> - ep->using_phy = false;
> -
> ep->phy = devm_of_phy_get(dev, np, NULL);
> if (IS_ERR(ep->phy)) {
> if (PTR_ERR(ep->phy) == -EPROBE_DEFER)
> return PTR_ERR(ep->phy);
> - dev_warn(dev, "Use the 'phy' property. Current DT of pci-
> exynos was deprecated!!\n");
> - } else
> - ep->using_phy = true;
> +
> + ep->phy = NULL;
> + }
>
> if (ep->ops && ep->ops->get_mem_resources) {
> ret = ep->ops->get_mem_resources(pdev, ep);
> @@ -713,8 +517,7 @@ static int __init exynos_pcie_probe(struct
> platform_device *pdev)
> return 0;
>
> fail_probe:
> - if (ep->using_phy)
> - phy_exit(ep->phy);
> + phy_exit(ep->phy);
>
> if (ep->ops && ep->ops->deinit_clk_resources)
> ep->ops->deinit_clk_resources(ep);
> --
> 2.15.1
^ permalink raw reply
* [PATCH RFC 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
From: Karthikeyan Ramasubramanian @ 2017-12-27 16:27 UTC (permalink / raw)
To: linux-arm-msm, linux-i2c, linux-serial
Cc: Karthikeyan Ramasubramanian, linux-doc, devicetree, andy.gross,
david.brown, robh+dt, mark.rutland, corbet, wsa, gregkh, jslaby,
Girish Mahadevan, Sagar Dharia
In-Reply-To: <1514392046-30602-1-git-send-email-kramasub@codeaurora.org>
This driver supports GENI based UART Controller in the Qualcomm SOCs. The
Qualcomm Generic Interface (GENI) is a programmable module supporting a
wide range of serial interfaces including UART. This driver support console
operations using FIFO mode of transfer.
Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
---
drivers/tty/serial/Kconfig | 10 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/qcom_geni_serial.c | 1447 +++++++++++++++++++++++++++++++++
3 files changed, 1458 insertions(+)
create mode 100644 drivers/tty/serial/qcom_geni_serial.c
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index b788fee..1be30e5 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1098,6 +1098,16 @@ config SERIAL_MSM_CONSOLE
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
+config SERIAL_QCOM_GENI
+ tristate "QCOM on-chip GENI based serial port support"
+ depends on ARCH_QCOM
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Serial driver for Qualcomm Technologies Inc's GENI based QUP
+ hardware.
+
config SERIAL_VT8500
bool "VIA VT8500 on-chip serial port support"
depends on ARCH_VT8500
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 842d185..64a8d82 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
+obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
new file mode 100644
index 0000000..8703fa6
--- /dev/null
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -0,0 +1,1447 @@
+/*
+ * Copyright (c) 2017, The Linux foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/qcom-geni-se.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+/* UART specific GENI registers */
+#define SE_UART_TX_TRANS_CFG (0x25C)
+#define SE_UART_TX_WORD_LEN (0x268)
+#define SE_UART_TX_STOP_BIT_LEN (0x26C)
+#define SE_UART_TX_TRANS_LEN (0x270)
+#define SE_UART_RX_TRANS_CFG (0x280)
+#define SE_UART_RX_WORD_LEN (0x28C)
+#define SE_UART_RX_STALE_CNT (0x294)
+#define SE_UART_TX_PARITY_CFG (0x2A4)
+#define SE_UART_RX_PARITY_CFG (0x2A8)
+
+/* SE_UART_TRANS_CFG */
+#define UART_TX_PAR_EN (BIT(0))
+#define UART_CTS_MASK (BIT(1))
+
+/* SE_UART_TX_WORD_LEN */
+#define TX_WORD_LEN_MSK (GENMASK(9, 0))
+
+/* SE_UART_TX_STOP_BIT_LEN */
+#define TX_STOP_BIT_LEN_MSK (GENMASK(23, 0))
+#define TX_STOP_BIT_LEN_1 (0)
+#define TX_STOP_BIT_LEN_1_5 (1)
+#define TX_STOP_BIT_LEN_2 (2)
+
+/* SE_UART_TX_TRANS_LEN */
+#define TX_TRANS_LEN_MSK (GENMASK(23, 0))
+
+/* SE_UART_RX_TRANS_CFG */
+#define UART_RX_INS_STATUS_BIT (BIT(2))
+#define UART_RX_PAR_EN (BIT(3))
+
+/* SE_UART_RX_WORD_LEN */
+#define RX_WORD_LEN_MASK (GENMASK(9, 0))
+
+/* SE_UART_RX_STALE_CNT */
+#define RX_STALE_CNT (GENMASK(23, 0))
+
+/* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */
+#define PAR_CALC_EN (BIT(0))
+#define PAR_MODE_MSK (GENMASK(2, 1))
+#define PAR_MODE_SHFT (1)
+#define PAR_EVEN (0x00)
+#define PAR_ODD (0x01)
+#define PAR_SPACE (0x10)
+#define PAR_MARK (0x11)
+
+/* UART M_CMD OP codes */
+#define UART_START_TX (0x1)
+#define UART_START_BREAK (0x4)
+#define UART_STOP_BREAK (0x5)
+/* UART S_CMD OP codes */
+#define UART_START_READ (0x1)
+#define UART_PARAM (0x1)
+
+#define UART_OVERSAMPLING (32)
+#define STALE_TIMEOUT (16)
+#define DEFAULT_BITS_PER_CHAR (10)
+#define GENI_UART_NR_PORTS (15)
+#define GENI_UART_CONS_PORTS (1)
+#define DEF_FIFO_DEPTH_WORDS (16)
+#define DEF_TX_WM (2)
+#define DEF_FIFO_WIDTH_BITS (32)
+#define UART_CORE2X_VOTE (10000)
+#define UART_CONSOLE_RX_WM (2)
+
+static int owr;
+module_param(owr, int, 0644);
+
+struct qcom_geni_serial_port {
+ struct uart_port uport;
+ char name[20];
+ unsigned int tx_fifo_depth;
+ unsigned int tx_fifo_width;
+ unsigned int rx_fifo_depth;
+ unsigned int tx_wm;
+ unsigned int rx_wm;
+ unsigned int rx_rfr;
+ int xfer_mode;
+ bool port_setup;
+ unsigned int *rx_fifo;
+ int (*handle_rx)(struct uart_port *uport,
+ unsigned int rx_fifo_wc,
+ unsigned int rx_last_byte_valid,
+ unsigned int rx_last,
+ bool drop_rx);
+ struct device *wrapper_dev;
+ struct geni_se_rsc serial_rsc;
+ unsigned int xmit_size;
+ void *rx_buf;
+ unsigned int cur_baud;
+};
+
+static const struct uart_ops qcom_geni_serial_pops;
+static struct uart_driver qcom_geni_console_driver;
+static int handle_rx_console(struct uart_port *uport,
+ unsigned int rx_fifo_wc,
+ unsigned int rx_last_byte_valid,
+ unsigned int rx_last,
+ bool drop_rx);
+static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
+static int qcom_geni_serial_poll_bit(struct uart_port *uport,
+ int offset, int bit_field, bool set);
+static void qcom_geni_serial_stop_rx(struct uart_port *uport);
+
+static atomic_t uart_line_id = ATOMIC_INIT(0);
+
+#define GET_DEV_PORT(uport) \
+ container_of(uport, struct qcom_geni_serial_port, uport)
+
+static struct qcom_geni_serial_port qcom_geni_console_port;
+
+static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
+{
+ if (cfg_flags & UART_CONFIG_TYPE)
+ uport->type = PORT_MSM;
+}
+
+static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport)
+{
+ return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+}
+
+static void qcom_geni_cons_set_mctrl(struct uart_port *uport,
+ unsigned int mctrl)
+{
+}
+
+static const char *qcom_geni_serial_get_type(struct uart_port *uport)
+{
+ return "MSM";
+}
+
+static struct qcom_geni_serial_port *get_port_from_line(int line)
+{
+ struct qcom_geni_serial_port *port = NULL;
+
+ if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
+ port = ERR_PTR(-ENXIO);
+ port = &qcom_geni_console_port;
+ return port;
+}
+
+static int qcom_geni_serial_poll_bit(struct uart_port *uport,
+ int offset, int bit_field, bool set)
+{
+ int iter = 0;
+ unsigned int reg;
+ bool met = false;
+ struct qcom_geni_serial_port *port = NULL;
+ bool cond = false;
+ unsigned int baud = 115200;
+ unsigned int fifo_bits = DEF_FIFO_DEPTH_WORDS * DEF_FIFO_WIDTH_BITS;
+ unsigned long total_iter = 2000;
+
+
+ if (uport->private_data) {
+ port = GET_DEV_PORT(uport);
+ baud = (port->cur_baud ? port->cur_baud : 115200);
+ fifo_bits = port->tx_fifo_depth * port->tx_fifo_width;
+ /*
+ * Total polling iterations based on FIFO worth of bytes to be
+ * sent at current baud .Add a little fluff to the wait.
+ */
+ total_iter = ((fifo_bits * USEC_PER_SEC) / baud) / 10;
+ total_iter += 50;
+ }
+
+ while (iter < total_iter) {
+ reg = geni_read_reg_nolog(uport->membase, offset);
+ cond = reg & bit_field;
+ if (cond == set) {
+ met = true;
+ break;
+ }
+ udelay(10);
+ iter++;
+ }
+ return met;
+}
+
+static void qcom_geni_serial_setup_tx(struct uart_port *uport,
+ unsigned int xmit_size)
+{
+ u32 m_cmd = 0;
+
+ geni_write_reg_nolog(xmit_size, uport->membase, SE_UART_TX_TRANS_LEN);
+ m_cmd |= (UART_START_TX << M_OPCODE_SHFT);
+ geni_write_reg_nolog(m_cmd, uport->membase, SE_GENI_M_CMD0);
+ /*
+ * Writes to enable the primary sequencer should go through before
+ * exiting this function.
+ */
+ mb();
+}
+
+static void qcom_geni_serial_poll_cancel_tx(struct uart_port *uport)
+{
+ int done = 0;
+ unsigned int irq_clear = M_CMD_DONE_EN;
+ unsigned int geni_status = 0;
+
+ done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_DONE_EN, true);
+ if (!done) {
+ geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase,
+ SE_GENI_M_CMD_CTRL_REG);
+ owr++;
+ irq_clear |= M_CMD_ABORT_EN;
+ qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_ABORT_EN, true);
+ }
+ geni_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_STATUS);
+ if (geni_status & M_GENI_CMD_ACTIVE)
+ owr++;
+ geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_serial_abort_rx(struct uart_port *uport)
+{
+ unsigned int irq_clear = S_CMD_DONE_EN;
+
+ geni_se_abort_s_cmd(uport->membase);
+ /* Ensure this goes through before polling. */
+ mb();
+ irq_clear |= S_CMD_ABORT_EN;
+ qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+ S_GENI_CMD_ABORT, false);
+ geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
+ geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int qcom_geni_serial_get_char(struct uart_port *uport)
+{
+ unsigned int rx_fifo;
+ unsigned int m_irq_status;
+ unsigned int s_irq_status;
+
+ if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_SEC_IRQ_EN, true)))
+ return -ENXIO;
+
+ m_irq_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_M_IRQ_STATUS);
+ s_irq_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_S_IRQ_STATUS);
+ geni_write_reg_nolog(m_irq_status, uport->membase,
+ SE_GENI_M_IRQ_CLEAR);
+ geni_write_reg_nolog(s_irq_status, uport->membase,
+ SE_GENI_S_IRQ_CLEAR);
+
+ if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_RX_FIFO_STATUS,
+ RX_FIFO_WC_MSK, true)))
+ return -ENXIO;
+
+ /*
+ * Read the Rx FIFO only after clearing the interrupt registers and
+ * getting valid RX fifo status.
+ */
+ mb();
+ rx_fifo = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
+ rx_fifo &= 0xFF;
+ return rx_fifo;
+}
+
+static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
+ unsigned char c)
+{
+ int b = (int) c;
+ struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+
+ geni_write_reg_nolog(port->tx_wm, uport->membase,
+ SE_GENI_TX_WATERMARK_REG);
+ qcom_geni_serial_setup_tx(uport, 1);
+ if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_TX_FIFO_WATERMARK_EN, true))
+ WARN_ON(1);
+ geni_write_reg_nolog(b, uport->membase, SE_GENI_TX_FIFOn);
+ geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
+ SE_GENI_M_IRQ_CLEAR);
+ /*
+ * Ensure FIFO write goes through before polling for status but.
+ */
+ mb();
+ qcom_geni_serial_poll_cancel_tx(uport);
+}
+#endif
+
+#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
+static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
+{
+ geni_write_reg_nolog(ch, uport->membase, SE_GENI_TX_FIFOn);
+ /*
+ * Ensure FIFO write clear goes through before
+ * next iteration.
+ */
+ mb();
+
+}
+
+static void
+__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
+ unsigned int count)
+{
+ int new_line = 0;
+ int i;
+ int bytes_to_send = count;
+ int fifo_depth = DEF_FIFO_DEPTH_WORDS;
+ int tx_wm = DEF_TX_WM;
+
+ for (i = 0; i < count; i++) {
+ if (s[i] == '\n')
+ new_line++;
+ }
+
+ bytes_to_send += new_line;
+ geni_write_reg_nolog(tx_wm, uport->membase,
+ SE_GENI_TX_WATERMARK_REG);
+ qcom_geni_serial_setup_tx(uport, bytes_to_send);
+ i = 0;
+ while (i < count) {
+ u32 chars_to_write = 0;
+ u32 avail_fifo_bytes = (fifo_depth - tx_wm);
+
+ /*
+ * If the WM bit never set, then the Tx state machine is not
+ * in a valid state, so break, cancel/abort any existing
+ * command. Unfortunately the current data being written is
+ * lost.
+ */
+ while (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_TX_FIFO_WATERMARK_EN, true))
+ break;
+ chars_to_write = min((unsigned int)(count - i),
+ avail_fifo_bytes);
+ if ((chars_to_write << 1) > avail_fifo_bytes)
+ chars_to_write = (avail_fifo_bytes >> 1);
+ uart_console_write(uport, (s + i), chars_to_write,
+ qcom_geni_serial_wr_char);
+ geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
+ SE_GENI_M_IRQ_CLEAR);
+ /* Ensure this goes through before polling for WM IRQ again.*/
+ mb();
+ i += chars_to_write;
+ }
+ qcom_geni_serial_poll_cancel_tx(uport);
+}
+
+static void qcom_geni_serial_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_port *uport;
+ struct qcom_geni_serial_port *port;
+ int locked = 1;
+ unsigned long flags;
+
+ WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
+
+ port = get_port_from_line(co->index);
+ if (IS_ERR_OR_NULL(port))
+ return;
+
+ uport = &port->uport;
+ if (oops_in_progress)
+ locked = spin_trylock_irqsave(&uport->lock, flags);
+ else
+ spin_lock_irqsave(&uport->lock, flags);
+
+ if (locked) {
+ __qcom_geni_serial_console_write(uport, s, count);
+ spin_unlock_irqrestore(&uport->lock, flags);
+ }
+}
+
+static int handle_rx_console(struct uart_port *uport,
+ unsigned int rx_fifo_wc,
+ unsigned int rx_last_byte_valid,
+ unsigned int rx_last,
+ bool drop_rx)
+{
+ int i, c;
+ unsigned char *rx_char;
+ struct tty_port *tport;
+ struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+
+ tport = &uport->state->port;
+ for (i = 0; i < rx_fifo_wc; i++) {
+ int bytes = 4;
+
+ *(qcom_port->rx_fifo) =
+ geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
+ if (drop_rx)
+ continue;
+ rx_char = (unsigned char *)qcom_port->rx_fifo;
+
+ if (i == (rx_fifo_wc - 1)) {
+ if (rx_last && rx_last_byte_valid)
+ bytes = rx_last_byte_valid;
+ }
+ for (c = 0; c < bytes; c++) {
+ char flag = TTY_NORMAL;
+ int sysrq;
+
+ uport->icount.rx++;
+ sysrq = uart_handle_sysrq_char(uport, rx_char[c]);
+ if (!sysrq)
+ tty_insert_flip_char(tport, rx_char[c], flag);
+ }
+ }
+ if (!drop_rx)
+ tty_flip_buffer_push(tport);
+ return 0;
+}
+#else
+static int handle_rx_console(struct uart_port *uport,
+ unsigned int rx_fifo_wc,
+ unsigned int rx_last_byte_valid,
+ unsigned int rx_last,
+ bool drop_rx)
+{
+ return -EPERM;
+}
+
+#endif /* (CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)) */
+
+static void qcom_geni_serial_start_tx(struct uart_port *uport)
+{
+ unsigned int geni_m_irq_en;
+ struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+ unsigned int geni_status;
+
+ if (qcom_port->xfer_mode == FIFO_MODE) {
+ geni_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_STATUS);
+ if (geni_status & M_GENI_CMD_ACTIVE)
+ goto exit_start_tx;
+
+ if (!qcom_geni_serial_tx_empty(uport))
+ goto exit_start_tx;
+
+ geni_m_irq_en = geni_read_reg_nolog(uport->membase,
+ SE_GENI_M_IRQ_EN);
+ geni_m_irq_en |= (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN);
+
+ geni_write_reg_nolog(qcom_port->tx_wm, uport->membase,
+ SE_GENI_TX_WATERMARK_REG);
+ geni_write_reg_nolog(geni_m_irq_en, uport->membase,
+ SE_GENI_M_IRQ_EN);
+ /* Geni command setup should complete before returning.*/
+ mb();
+ }
+exit_start_tx:
+ return;
+}
+
+static void stop_tx_sequencer(struct uart_port *uport)
+{
+ unsigned int geni_m_irq_en;
+ unsigned int geni_status;
+ struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+
+ geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
+ geni_m_irq_en &= ~M_CMD_DONE_EN;
+ if (port->xfer_mode == FIFO_MODE) {
+ geni_m_irq_en &= ~M_TX_FIFO_WATERMARK_EN;
+ geni_write_reg_nolog(0, uport->membase,
+ SE_GENI_TX_WATERMARK_REG);
+ }
+ port->xmit_size = 0;
+ geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN);
+ geni_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_STATUS);
+ /* Possible stop tx is called multiple times. */
+ if (!(geni_status & M_GENI_CMD_ACTIVE))
+ return;
+
+ geni_se_cancel_m_cmd(uport->membase);
+ if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_CANCEL_EN, true)) {
+ geni_se_abort_m_cmd(uport->membase);
+ qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_ABORT_EN, true);
+ geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase,
+ SE_GENI_M_IRQ_CLEAR);
+ }
+ geni_write_reg_nolog(M_CMD_CANCEL_EN, uport, SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_serial_stop_tx(struct uart_port *uport)
+{
+ stop_tx_sequencer(uport);
+}
+
+static void start_rx_sequencer(struct uart_port *uport)
+{
+ unsigned int geni_s_irq_en;
+ unsigned int geni_m_irq_en;
+ unsigned int geni_status;
+ struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+
+ geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+ if (geni_status & S_GENI_CMD_ACTIVE)
+ qcom_geni_serial_stop_rx(uport);
+
+ geni_se_setup_s_cmd(uport->membase, UART_START_READ, 0);
+
+ if (port->xfer_mode == FIFO_MODE) {
+ geni_s_irq_en = geni_read_reg_nolog(uport->membase,
+ SE_GENI_S_IRQ_EN);
+ geni_m_irq_en = geni_read_reg_nolog(uport->membase,
+ SE_GENI_M_IRQ_EN);
+
+ geni_s_irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
+ geni_m_irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
+
+ geni_write_reg_nolog(geni_s_irq_en, uport->membase,
+ SE_GENI_S_IRQ_EN);
+ geni_write_reg_nolog(geni_m_irq_en, uport->membase,
+ SE_GENI_M_IRQ_EN);
+ }
+ /*
+ * Ensure the writes to the secondary sequencer and interrupt enables
+ * go through.
+ */
+ mb();
+ geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+}
+
+static void qcom_geni_serial_start_rx(struct uart_port *uport)
+{
+ start_rx_sequencer(uport);
+}
+
+static void stop_rx_sequencer(struct uart_port *uport)
+{
+ unsigned int geni_s_irq_en;
+ unsigned int geni_m_irq_en;
+ unsigned int geni_status;
+ struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+ u32 irq_clear = S_CMD_DONE_EN;
+ bool done;
+
+ if (port->xfer_mode == FIFO_MODE) {
+ geni_s_irq_en = geni_read_reg_nolog(uport->membase,
+ SE_GENI_S_IRQ_EN);
+ geni_m_irq_en = geni_read_reg_nolog(uport->membase,
+ SE_GENI_M_IRQ_EN);
+ geni_s_irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
+ geni_m_irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
+
+ geni_write_reg_nolog(geni_s_irq_en, uport->membase,
+ SE_GENI_S_IRQ_EN);
+ geni_write_reg_nolog(geni_m_irq_en, uport->membase,
+ SE_GENI_M_IRQ_EN);
+ }
+
+ geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+ /* Possible stop rx is called multiple times. */
+ if (!(geni_status & S_GENI_CMD_ACTIVE))
+ return;
+ geni_se_cancel_s_cmd(uport->membase);
+ /*
+ * Ensure that the cancel goes through before polling for the
+ * cancel control bit.
+ */
+ mb();
+ done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+ S_GENI_CMD_CANCEL, false);
+ geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+ geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
+ if ((geni_status & S_GENI_CMD_ACTIVE))
+ qcom_geni_serial_abort_rx(uport);
+}
+
+static void qcom_geni_serial_stop_rx(struct uart_port *uport)
+{
+ stop_rx_sequencer(uport);
+}
+
+static int qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
+{
+ int ret = 0;
+ unsigned int rx_fifo_status;
+ unsigned int rx_fifo_wc = 0;
+ unsigned int rx_last_byte_valid = 0;
+ unsigned int rx_last = 0;
+ struct tty_port *tport;
+ struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+
+ tport = &uport->state->port;
+ rx_fifo_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_RX_FIFO_STATUS);
+ rx_fifo_wc = rx_fifo_status & RX_FIFO_WC_MSK;
+ rx_last_byte_valid = ((rx_fifo_status & RX_LAST_BYTE_VALID_MSK) >>
+ RX_LAST_BYTE_VALID_SHFT);
+ rx_last = rx_fifo_status & RX_LAST;
+ if (rx_fifo_wc)
+ port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
+ rx_last, drop_rx);
+ return ret;
+}
+
+static int qcom_geni_serial_handle_tx(struct uart_port *uport)
+{
+ int ret = 0;
+ struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+ struct circ_buf *xmit = &uport->state->xmit;
+ unsigned int avail_fifo_bytes = 0;
+ unsigned int bytes_remaining = 0;
+ int i = 0;
+ unsigned int tx_fifo_status;
+ unsigned int xmit_size;
+ unsigned int fifo_width_bytes = 1;
+ int temp_tail = 0;
+
+ xmit_size = uart_circ_chars_pending(xmit);
+ tx_fifo_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_TX_FIFO_STATUS);
+ /* Both FIFO and framework buffer are drained */
+ if ((xmit_size == qcom_port->xmit_size) && !tx_fifo_status) {
+ qcom_port->xmit_size = 0;
+ uart_circ_clear(xmit);
+ qcom_geni_serial_stop_tx(uport);
+ goto exit_handle_tx;
+ }
+ xmit_size -= qcom_port->xmit_size;
+
+ avail_fifo_bytes = (qcom_port->tx_fifo_depth - qcom_port->tx_wm) *
+ fifo_width_bytes;
+ temp_tail = (xmit->tail + qcom_port->xmit_size) & (UART_XMIT_SIZE - 1);
+ if (xmit_size > (UART_XMIT_SIZE - temp_tail))
+ xmit_size = (UART_XMIT_SIZE - temp_tail);
+ if (xmit_size > avail_fifo_bytes)
+ xmit_size = avail_fifo_bytes;
+
+ if (!xmit_size)
+ goto exit_handle_tx;
+
+ qcom_geni_serial_setup_tx(uport, xmit_size);
+
+ bytes_remaining = xmit_size;
+ while (i < xmit_size) {
+ unsigned int tx_bytes;
+ unsigned int buf = 0;
+ int c;
+
+ tx_bytes = ((bytes_remaining < fifo_width_bytes) ?
+ bytes_remaining : fifo_width_bytes);
+
+ for (c = 0; c < tx_bytes ; c++)
+ buf |= (xmit->buf[temp_tail + c] << (c * 8));
+ geni_write_reg_nolog(buf, uport->membase, SE_GENI_TX_FIFOn);
+ i += tx_bytes;
+ temp_tail = (temp_tail + tx_bytes) & (UART_XMIT_SIZE - 1);
+ uport->icount.tx += tx_bytes;
+ bytes_remaining -= tx_bytes;
+ /* Ensure FIFO write goes through */
+ wmb();
+ }
+ qcom_geni_serial_poll_cancel_tx(uport);
+ qcom_port->xmit_size += xmit_size;
+exit_handle_tx:
+ uart_write_wakeup(uport);
+ return ret;
+}
+
+static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
+{
+ unsigned int m_irq_status;
+ unsigned int s_irq_status;
+ struct uart_port *uport = dev;
+ unsigned long flags;
+ unsigned int m_irq_en;
+ bool drop_rx = false;
+ struct tty_port *tport = &uport->state->port;
+
+ spin_lock_irqsave(&uport->lock, flags);
+ if (uport->suspended)
+ goto exit_geni_serial_isr;
+ m_irq_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_M_IRQ_STATUS);
+ s_irq_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_S_IRQ_STATUS);
+ m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
+ geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR);
+ geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR);
+
+ if ((m_irq_status & M_ILLEGAL_CMD_EN)) {
+ WARN_ON(1);
+ goto exit_geni_serial_isr;
+ }
+
+ if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
+ uport->icount.overrun++;
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+ }
+
+ if ((m_irq_status & m_irq_en) &
+ (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
+ qcom_geni_serial_handle_tx(uport);
+
+ if ((s_irq_status & S_GP_IRQ_0_EN) || (s_irq_status & S_GP_IRQ_1_EN)) {
+ if (s_irq_status & S_GP_IRQ_0_EN)
+ uport->icount.parity++;
+ drop_rx = true;
+ } else if ((s_irq_status & S_GP_IRQ_2_EN) ||
+ (s_irq_status & S_GP_IRQ_3_EN)) {
+ uport->icount.brk++;
+ }
+
+ if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
+ (s_irq_status & S_RX_FIFO_LAST_EN))
+ qcom_geni_serial_handle_rx(uport, drop_rx);
+
+exit_geni_serial_isr:
+ spin_unlock_irqrestore(&uport->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int get_tx_fifo_size(struct qcom_geni_serial_port *port)
+{
+ struct uart_port *uport;
+
+ if (!port)
+ return -ENODEV;
+
+ uport = &port->uport;
+ port->tx_fifo_depth = geni_se_get_tx_fifo_depth(uport->membase);
+ if (!port->tx_fifo_depth) {
+ dev_err(uport->dev, "%s:Invalid TX FIFO depth read\n",
+ __func__);
+ return -ENXIO;
+ }
+
+ port->tx_fifo_width = geni_se_get_tx_fifo_width(uport->membase);
+ if (!port->tx_fifo_width) {
+ dev_err(uport->dev, "%s:Invalid TX FIFO width read\n",
+ __func__);
+ return -ENXIO;
+ }
+
+ port->rx_fifo_depth = geni_se_get_rx_fifo_depth(uport->membase);
+ if (!port->rx_fifo_depth) {
+ dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
+ __func__);
+ return -ENXIO;
+ }
+
+ uport->fifosize =
+ ((port->tx_fifo_depth * port->tx_fifo_width) >> 3);
+ return 0;
+}
+
+static void set_rfr_wm(struct qcom_geni_serial_port *port)
+{
+ /*
+ * Set RFR (Flow off) to FIFO_DEPTH - 2.
+ * RX WM level at 10% RX_FIFO_DEPTH.
+ * TX WM level at 10% TX_FIFO_DEPTH.
+ */
+ port->rx_rfr = port->rx_fifo_depth - 2;
+ port->rx_wm = UART_CONSOLE_RX_WM;
+ port->tx_wm = 2;
+}
+
+static void qcom_geni_serial_shutdown(struct uart_port *uport)
+{
+ unsigned long flags;
+
+ /* Stop the console before stopping the current tx */
+ console_stop(uport->cons);
+
+ disable_irq(uport->irq);
+ free_irq(uport->irq, uport);
+ spin_lock_irqsave(&uport->lock, flags);
+ qcom_geni_serial_stop_tx(uport);
+ qcom_geni_serial_stop_rx(uport);
+ spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+static int qcom_geni_serial_port_setup(struct uart_port *uport)
+{
+ int ret = 0;
+ struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+ unsigned long cfg0, cfg1;
+ unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
+
+ set_rfr_wm(qcom_port);
+ geni_write_reg_nolog(rxstale, uport->membase, SE_UART_RX_STALE_CNT);
+ /*
+ * Make an unconditional cancel on the main sequencer to reset
+ * it else we could end up in data loss scenarios.
+ */
+ qcom_port->xfer_mode = FIFO_MODE;
+ qcom_geni_serial_poll_cancel_tx(uport);
+ geni_se_get_packing_config(8, 1, false, &cfg0, &cfg1);
+ geni_write_reg_nolog(cfg0, uport->membase,
+ SE_GENI_TX_PACKING_CFG0);
+ geni_write_reg_nolog(cfg1, uport->membase,
+ SE_GENI_TX_PACKING_CFG1);
+ geni_se_get_packing_config(8, 4, false, &cfg0, &cfg1);
+ geni_write_reg_nolog(cfg0, uport->membase,
+ SE_GENI_RX_PACKING_CFG0);
+ geni_write_reg_nolog(cfg1, uport->membase,
+ SE_GENI_RX_PACKING_CFG1);
+ ret = geni_se_init(uport->membase, qcom_port->rx_wm, qcom_port->rx_rfr);
+ if (ret) {
+ dev_err(uport->dev, "%s: Fail\n", __func__);
+ goto exit_portsetup;
+ }
+
+ ret = geni_se_select_mode(uport->membase, qcom_port->xfer_mode);
+ if (ret)
+ goto exit_portsetup;
+
+ qcom_port->port_setup = true;
+ /*
+ * Ensure Port setup related IO completes before returning to
+ * framework.
+ */
+ mb();
+exit_portsetup:
+ return ret;
+}
+
+static int qcom_geni_serial_startup(struct uart_port *uport)
+{
+ int ret = 0;
+ struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+
+ scnprintf(qcom_port->name, sizeof(qcom_port->name),
+ "qcom_serial_geni%d", uport->line);
+
+ if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
+ dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
+ __func__, geni_se_get_proto(uport->membase));
+ ret = -ENXIO;
+ goto exit_startup;
+ }
+
+ get_tx_fifo_size(qcom_port);
+ if (!qcom_port->port_setup) {
+ if (qcom_geni_serial_port_setup(uport))
+ goto exit_startup;
+ }
+
+ /*
+ * Ensure that all the port configuration writes complete
+ * before returning to the framework.
+ */
+ mb();
+ ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
+ qcom_port->name, uport);
+ if (unlikely(ret)) {
+ dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n",
+ __func__, ret);
+ goto exit_startup;
+ }
+
+exit_startup:
+ return ret;
+}
+
+static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
+{
+ unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
+ 32000000, 48000000, 64000000, 80000000, 96000000, 100000000};
+ int i;
+ int match = -1;
+
+ for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
+ if (clk_freq > root_freq[i])
+ continue;
+
+ if (!(root_freq[i] % clk_freq)) {
+ match = i;
+ break;
+ }
+ }
+ if (match != -1)
+ *ser_clk = root_freq[match];
+ else
+ pr_err("clk_freq %ld\n", clk_freq);
+ return match;
+}
+
+static void geni_serial_write_term_regs(struct uart_port *uport,
+ u32 tx_trans_cfg, u32 tx_parity_cfg, u32 rx_trans_cfg,
+ u32 rx_parity_cfg, u32 bits_per_char, u32 stop_bit_len,
+ u32 s_clk_cfg)
+{
+ geni_write_reg_nolog(tx_trans_cfg, uport->membase,
+ SE_UART_TX_TRANS_CFG);
+ geni_write_reg_nolog(tx_parity_cfg, uport->membase,
+ SE_UART_TX_PARITY_CFG);
+ geni_write_reg_nolog(rx_trans_cfg, uport->membase,
+ SE_UART_RX_TRANS_CFG);
+ geni_write_reg_nolog(rx_parity_cfg, uport->membase,
+ SE_UART_RX_PARITY_CFG);
+ geni_write_reg_nolog(bits_per_char, uport->membase,
+ SE_UART_TX_WORD_LEN);
+ geni_write_reg_nolog(bits_per_char, uport->membase,
+ SE_UART_RX_WORD_LEN);
+ geni_write_reg_nolog(stop_bit_len, uport->membase,
+ SE_UART_TX_STOP_BIT_LEN);
+ geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG);
+ geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG);
+}
+
+static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
+{
+ unsigned long ser_clk;
+ int dfs_index;
+ int clk_div = 0;
+
+ *desired_clk_rate = baud * UART_OVERSAMPLING;
+ dfs_index = get_clk_cfg(*desired_clk_rate, &ser_clk);
+ if (dfs_index < 0) {
+ pr_err("%s: Can't find matching DFS entry for baud %d\n",
+ __func__, baud);
+ clk_div = -EINVAL;
+ goto exit_get_clk_div_rate;
+ }
+
+ clk_div = ser_clk / *desired_clk_rate;
+ *desired_clk_rate = ser_clk;
+exit_get_clk_div_rate:
+ return clk_div;
+}
+
+static void qcom_geni_serial_set_termios(struct uart_port *uport,
+ struct ktermios *termios, struct ktermios *old)
+{
+ unsigned int baud;
+ unsigned int bits_per_char = 0;
+ unsigned int tx_trans_cfg;
+ unsigned int tx_parity_cfg;
+ unsigned int rx_trans_cfg;
+ unsigned int rx_parity_cfg;
+ unsigned int stop_bit_len;
+ unsigned int clk_div;
+ unsigned long ser_clk_cfg = 0;
+ struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+ unsigned long clk_rate;
+
+ qcom_geni_serial_stop_rx(uport);
+ /* baud rate */
+ baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
+ port->cur_baud = baud;
+ clk_div = get_clk_div_rate(baud, &clk_rate);
+ if (clk_div <= 0)
+ goto exit_set_termios;
+
+ uport->uartclk = clk_rate;
+ clk_set_rate(port->serial_rsc.se_clk, clk_rate);
+ ser_clk_cfg |= SER_CLK_EN;
+ ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
+
+ /* parity */
+ tx_trans_cfg = geni_read_reg_nolog(uport->membase,
+ SE_UART_TX_TRANS_CFG);
+ tx_parity_cfg = geni_read_reg_nolog(uport->membase,
+ SE_UART_TX_PARITY_CFG);
+ rx_trans_cfg = geni_read_reg_nolog(uport->membase,
+ SE_UART_RX_TRANS_CFG);
+ rx_parity_cfg = geni_read_reg_nolog(uport->membase,
+ SE_UART_RX_PARITY_CFG);
+ if (termios->c_cflag & PARENB) {
+ tx_trans_cfg |= UART_TX_PAR_EN;
+ rx_trans_cfg |= UART_RX_PAR_EN;
+ tx_parity_cfg |= PAR_CALC_EN;
+ rx_parity_cfg |= PAR_CALC_EN;
+ if (termios->c_cflag & PARODD) {
+ tx_parity_cfg |= PAR_ODD;
+ rx_parity_cfg |= PAR_ODD;
+ } else if (termios->c_cflag & CMSPAR) {
+ tx_parity_cfg |= PAR_SPACE;
+ rx_parity_cfg |= PAR_SPACE;
+ } else {
+ tx_parity_cfg |= PAR_EVEN;
+ rx_parity_cfg |= PAR_EVEN;
+ }
+ } else {
+ tx_trans_cfg &= ~UART_TX_PAR_EN;
+ rx_trans_cfg &= ~UART_RX_PAR_EN;
+ tx_parity_cfg &= ~PAR_CALC_EN;
+ rx_parity_cfg &= ~PAR_CALC_EN;
+ }
+
+ /* bits per char */
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ bits_per_char = 5;
+ break;
+ case CS6:
+ bits_per_char = 6;
+ break;
+ case CS7:
+ bits_per_char = 7;
+ break;
+ case CS8:
+ default:
+ bits_per_char = 8;
+ break;
+ }
+
+ /* stop bits */
+ if (termios->c_cflag & CSTOPB)
+ stop_bit_len = TX_STOP_BIT_LEN_2;
+ else
+ stop_bit_len = TX_STOP_BIT_LEN_1;
+
+ /* flow control, clear the CTS_MASK bit if using flow control. */
+ if (termios->c_cflag & CRTSCTS)
+ tx_trans_cfg &= ~UART_CTS_MASK;
+ else
+ tx_trans_cfg |= UART_CTS_MASK;
+
+ if (likely(baud))
+ uart_update_timeout(uport, termios->c_cflag, baud);
+
+ geni_serial_write_term_regs(uport, tx_trans_cfg, tx_parity_cfg,
+ rx_trans_cfg, rx_parity_cfg, bits_per_char, stop_bit_len,
+ ser_clk_cfg);
+exit_set_termios:
+ qcom_geni_serial_start_rx(uport);
+ return;
+
+}
+
+static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
+{
+ unsigned int tx_fifo_status;
+ unsigned int is_tx_empty = 1;
+
+ tx_fifo_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_TX_FIFO_STATUS);
+ if (tx_fifo_status)
+ is_tx_empty = 0;
+
+ return is_tx_empty;
+}
+
+#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
+static int __init qcom_geni_console_setup(struct console *co, char *options)
+{
+ struct uart_port *uport;
+ struct qcom_geni_serial_port *dev_port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ int ret = 0;
+
+ if (unlikely(co->index >= GENI_UART_NR_PORTS || co->index < 0))
+ return -ENXIO;
+
+ dev_port = get_port_from_line(co->index);
+ if (IS_ERR_OR_NULL(dev_port)) {
+ ret = PTR_ERR(dev_port);
+ pr_err("Invalid line %d(%d)\n", co->index, ret);
+ return ret;
+ }
+
+ uport = &dev_port->uport;
+
+ if (unlikely(!uport->membase))
+ return -ENXIO;
+
+ if (geni_se_resources_on(&dev_port->serial_rsc))
+ WARN_ON(1);
+
+ if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
+ geni_se_resources_off(&dev_port->serial_rsc);
+ return -ENXIO;
+ }
+
+ if (!dev_port->port_setup) {
+ qcom_geni_serial_stop_rx(uport);
+ qcom_geni_serial_port_setup(uport);
+ }
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(uport, co, baud, parity, bits, flow);
+}
+
+static int console_register(struct uart_driver *drv)
+{
+ return uart_register_driver(drv);
+}
+static void console_unregister(struct uart_driver *drv)
+{
+ uart_unregister_driver(drv);
+}
+
+static struct console cons_ops = {
+ .name = "ttyMSM",
+ .write = qcom_geni_serial_console_write,
+ .device = uart_console_device,
+ .setup = qcom_geni_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &qcom_geni_console_driver,
+};
+
+static struct uart_driver qcom_geni_console_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "qcom_geni_console",
+ .dev_name = "ttyMSM",
+ .nr = GENI_UART_NR_PORTS,
+ .cons = &cons_ops,
+};
+#else
+static int console_register(struct uart_driver *drv)
+{
+ return 0;
+}
+
+static void console_unregister(struct uart_driver *drv)
+{
+}
+#endif /* defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) */
+
+static void qcom_geni_serial_cons_pm(struct uart_port *uport,
+ unsigned int new_state, unsigned int old_state)
+{
+ struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+
+ if (unlikely(!uart_console(uport)))
+ return;
+
+ if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
+ geni_se_resources_on(&qcom_port->serial_rsc);
+ else if (new_state == UART_PM_STATE_OFF &&
+ old_state == UART_PM_STATE_ON)
+ geni_se_resources_off(&qcom_port->serial_rsc);
+}
+
+static const struct uart_ops qcom_geni_console_pops = {
+ .tx_empty = qcom_geni_serial_tx_empty,
+ .stop_tx = qcom_geni_serial_stop_tx,
+ .start_tx = qcom_geni_serial_start_tx,
+ .stop_rx = qcom_geni_serial_stop_rx,
+ .set_termios = qcom_geni_serial_set_termios,
+ .startup = qcom_geni_serial_startup,
+ .config_port = qcom_geni_serial_config_port,
+ .shutdown = qcom_geni_serial_shutdown,
+ .type = qcom_geni_serial_get_type,
+ .set_mctrl = qcom_geni_cons_set_mctrl,
+ .get_mctrl = qcom_geni_cons_get_mctrl,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = qcom_geni_serial_get_char,
+ .poll_put_char = qcom_geni_serial_poll_put_char,
+#endif
+ .pm = qcom_geni_serial_cons_pm,
+};
+
+static const struct of_device_id qcom_geni_device_tbl[] = {
+ { .compatible = "qcom,geni-console",
+ .data = (void *)&qcom_geni_console_driver},
+};
+
+static int qcom_geni_serial_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int line = -1;
+ struct qcom_geni_serial_port *dev_port;
+ struct uart_port *uport;
+ struct resource *res;
+ struct uart_driver *drv;
+ const struct of_device_id *id;
+ struct platform_device *wrapper_pdev;
+ struct device_node *wrapper_ph_node;
+
+ id = of_match_device(qcom_geni_device_tbl, &pdev->dev);
+ if (id) {
+ dev_dbg(&pdev->dev, "%s: %s\n", __func__, id->compatible);
+ drv = (struct uart_driver *)id->data;
+ } else {
+ dev_err(&pdev->dev, "%s: No matching device found", __func__);
+ return -ENODEV;
+ }
+
+ if (pdev->dev.of_node) {
+ if (drv->cons)
+ line = of_alias_get_id(pdev->dev.of_node, "serial");
+ } else {
+ line = pdev->id;
+ }
+
+ if (line < 0)
+ line = atomic_inc_return(&uart_line_id) - 1;
+
+ if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
+ return -ENXIO;
+ dev_port = get_port_from_line(line);
+ if (IS_ERR_OR_NULL(dev_port)) {
+ ret = PTR_ERR(dev_port);
+ dev_err(&pdev->dev, "Invalid line %d(%d)\n",
+ line, ret);
+ goto exit_geni_serial_probe;
+ }
+
+ uport = &dev_port->uport;
+
+ /* Don't allow 2 drivers to access the same port */
+ if (uport->private_data) {
+ ret = -ENODEV;
+ goto exit_geni_serial_probe;
+ }
+
+ uport->dev = &pdev->dev;
+ wrapper_ph_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,wrapper-core", 0);
+ if (IS_ERR_OR_NULL(wrapper_ph_node)) {
+ ret = PTR_ERR(wrapper_ph_node);
+ goto exit_geni_serial_probe;
+ }
+ wrapper_pdev = of_find_device_by_node(wrapper_ph_node);
+ of_node_put(wrapper_ph_node);
+ if (IS_ERR_OR_NULL(wrapper_pdev)) {
+ ret = PTR_ERR(wrapper_pdev);
+ goto exit_geni_serial_probe;
+ }
+ dev_port->wrapper_dev = &wrapper_pdev->dev;
+ dev_port->serial_rsc.wrapper_dev = &wrapper_pdev->dev;
+ dev_port->serial_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
+ if (IS_ERR(dev_port->serial_rsc.se_clk)) {
+ ret = PTR_ERR(dev_port->serial_rsc.se_clk);
+ dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
+ goto exit_geni_serial_probe;
+ }
+
+ dev_port->serial_rsc.m_ahb_clk = devm_clk_get(&pdev->dev, "m-ahb");
+ if (IS_ERR(dev_port->serial_rsc.m_ahb_clk)) {
+ ret = PTR_ERR(dev_port->serial_rsc.m_ahb_clk);
+ dev_err(&pdev->dev, "Err getting M AHB clk %d\n", ret);
+ goto exit_geni_serial_probe;
+ }
+
+ dev_port->serial_rsc.s_ahb_clk = devm_clk_get(&pdev->dev, "s-ahb");
+ if (IS_ERR(dev_port->serial_rsc.s_ahb_clk)) {
+ ret = PTR_ERR(dev_port->serial_rsc.s_ahb_clk);
+ dev_err(&pdev->dev, "Err getting S AHB clk %d\n", ret);
+ goto exit_geni_serial_probe;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "se_phys");
+ if (!res) {
+ ret = -ENXIO;
+ dev_err(&pdev->dev, "Err getting IO region\n");
+ goto exit_geni_serial_probe;
+ }
+
+ uport->mapbase = res->start;
+ uport->membase = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!uport->membase) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "Err IO mapping serial iomem");
+ goto exit_geni_serial_probe;
+ }
+
+ dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_pinctrl)) {
+ dev_err(&pdev->dev, "No pinctrl config specified!\n");
+ ret = PTR_ERR(dev_port->serial_rsc.geni_pinctrl);
+ goto exit_geni_serial_probe;
+ }
+ dev_port->serial_rsc.geni_gpio_active =
+ pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
+ PINCTRL_DEFAULT);
+ if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_active)) {
+ dev_err(&pdev->dev, "No default config specified!\n");
+ ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_active);
+ goto exit_geni_serial_probe;
+ }
+
+ dev_port->serial_rsc.geni_gpio_sleep =
+ pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
+ PINCTRL_SLEEP);
+ if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_sleep)) {
+ dev_err(&pdev->dev, "No sleep config specified!\n");
+ ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_sleep);
+ goto exit_geni_serial_probe;
+ }
+
+ dev_port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
+ dev_port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
+ dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
+ uport->fifosize =
+ ((dev_port->tx_fifo_depth * dev_port->tx_fifo_width) >> 3);
+
+ uport->irq = platform_get_irq(pdev, 0);
+ if (uport->irq < 0) {
+ ret = uport->irq;
+ dev_err(&pdev->dev, "Failed to get IRQ %d\n", ret);
+ goto exit_geni_serial_probe;
+ }
+
+ uport->private_data = (void *)drv;
+ platform_set_drvdata(pdev, dev_port);
+ dev_port->handle_rx = handle_rx_console;
+ dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32),
+ GFP_KERNEL);
+ dev_port->port_setup = false;
+ return uart_add_one_port(drv, uport);
+
+exit_geni_serial_probe:
+ return ret;
+}
+
+static int qcom_geni_serial_remove(struct platform_device *pdev)
+{
+ struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+ struct uart_driver *drv =
+ (struct uart_driver *)port->uport.private_data;
+
+ uart_remove_one_port(drv, &port->uport);
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+ struct uart_port *uport = &port->uport;
+
+ uart_suspend_port((struct uart_driver *)uport->private_data,
+ uport);
+ return 0;
+}
+
+static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+ struct uart_port *uport = &port->uport;
+
+ if (console_suspend_enabled && uport->suspended) {
+ uart_resume_port((struct uart_driver *)uport->private_data,
+ uport);
+ disable_irq(uport->irq);
+ }
+ return 0;
+}
+#else
+static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
+{
+ return 0;
+}
+
+static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
+ .suspend_noirq = qcom_geni_serial_sys_suspend_noirq,
+ .resume_noirq = qcom_geni_serial_sys_resume_noirq,
+};
+
+static const struct of_device_id qcom_geni_serial_match_table[] = {
+ { .compatible = "qcom,geni-uart"},
+ {},
+};
+
+static struct platform_driver qcom_geni_serial_platform_driver = {
+ .remove = qcom_geni_serial_remove,
+ .probe = qcom_geni_serial_probe,
+ .driver = {
+ .name = "qcom_geni_serial",
+ .of_match_table = qcom_geni_serial_match_table,
+ .pm = &qcom_geni_serial_pm_ops,
+ },
+};
+
+static int __init qcom_geni_serial_init(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < GENI_UART_CONS_PORTS; i++) {
+ qcom_geni_console_port.uport.iotype = UPIO_MEM;
+ qcom_geni_console_port.uport.ops = &qcom_geni_console_pops;
+ qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
+ qcom_geni_console_port.uport.line = i;
+ }
+
+ ret = console_register(&qcom_geni_console_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&qcom_geni_serial_platform_driver);
+ if (ret) {
+ console_unregister(&qcom_geni_console_driver);
+ return ret;
+ }
+ return ret;
+}
+module_init(qcom_geni_serial_init);
+
+static void __exit qcom_geni_serial_exit(void)
+{
+ platform_driver_unregister(&qcom_geni_serial_platform_driver);
+ console_unregister(&qcom_geni_console_driver);
+}
+module_exit(qcom_geni_serial_exit);
+
+MODULE_DESCRIPTION("Serial driver for GENI based QUP cores");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("tty:qcom_geni_serial");
--
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH RFC 6/7] serial: Add device tree bindings for GENI based UART Controller
From: Karthikeyan Ramasubramanian @ 2017-12-27 16:27 UTC (permalink / raw)
To: linux-arm-msm, linux-i2c, linux-serial
Cc: Karthikeyan Ramasubramanian, linux-doc, devicetree, andy.gross,
david.brown, robh+dt, mark.rutland, corbet, wsa, gregkh, jslaby,
Girish Mahadevan
In-Reply-To: <1514392046-30602-1-git-send-email-kramasub@codeaurora.org>
Add device tree binding support for GENI based UART Controller in the
QUP Wrapper.
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
---
.../devicetree/bindings/serial/qcom,geni-uart.txt | 31 ++++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
diff --git a/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
new file mode 100644
index 0000000..e60ec6a
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
@@ -0,0 +1,31 @@
+Qualcomm Technologies Inc. GENI based Serial UART Controller driver
+
+This serial UART driver supports console use-cases. This driver is meant
+only for Generic Interface (GENI) based Qualcomm Universal Peripheral (QUP)
+cores and isn't backwards compatible.
+
+Required properties:
+- compatible: should contain "qcom,geni-uart, qcom,geni-console"
+- reg: Should contain UART register location and length.
+- interrupts: Should contain UART core interrupts.
+- clocks: clocks needed for UART, includes the core and AHB clock.
+- pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
+ Should be "active" and "sleep" for the pin confuguration when core is active
+ or when entering sleep state.
+- qcom,wrapper-core: Wrapper QUP core containing this UART controller.
+
+Example:
+qup_uart11: qcom,qup_uart@0xa88000 {
+ compatible = "qcom,geni-uart";
+ reg = <0xa88000 0x7000>;
+ reg-names = "se_phys";
+ clock-names = "se-clk", "m-ahb", "s-ahb";
+ clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>,
+ <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
+ <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&qup_1_uart_3_active>;
+ pinctrl-1 = <&qup_1_uart_3_sleep>;
+ interrupts = <0 355 0>;
+ qcom,wrapper-core = <&qup_0>;
+};
--
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH RFC 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
From: Karthikeyan Ramasubramanian @ 2017-12-27 16:27 UTC (permalink / raw)
To: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA
Cc: Karthikeyan Ramasubramanian, linux-doc-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
andy.gross-QSEj5FYQhm4dnm+yROfE0A,
david.brown-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
corbet-T1hC0tSOHrs, wsa-z923LK4zBo2bacvFa/9K2g,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, jslaby-IBi9RG/b67k,
Sagar Dharia, Girish Mahadevan
In-Reply-To: <1514392046-30602-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
This bus driver supports the GENI based i2c hardware controller in the
Qualcomm SOCs. The Qualcomm Generic Interface (GENI) is a programmable
module supporting a wide range of serial interfaces including I2C. The
driver supports FIFO mode and DMA mode of transfer and switches modes
dynamically depending on the size of the transfer.
Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Girish Mahadevan <girishm-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-qcom-geni.c | 685 +++++++++++++++++++++++++++++++++++++
3 files changed, 696 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 009345d..caef309 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -838,6 +838,16 @@ config I2C_PXA_SLAVE
is necessary for systems where the PXA may be a target on the
I2C bus.
+config I2C_QCOM_GENI
+ tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
+ depends on ARCH_QCOM
+ help
+ If you say yes to this option, support will be included for the
+ built-in I2C interface on the Qualcomm Technologies Inc.'s SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-qcom-geni.
+
config I2C_QUP
tristate "Qualcomm QUP based I2C controller"
depends on ARCH_QCOM
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 2ce8576..201fce1 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
+obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-geni.o
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
new file mode 100644
index 0000000..6ffe17e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/qcom-geni-se.h>
+
+#define SE_I2C_TX_TRANS_LEN (0x26C)
+#define SE_I2C_RX_TRANS_LEN (0x270)
+#define SE_I2C_SCL_COUNTERS (0x278)
+#define SE_GENI_IOS (0x908)
+
+#define SE_I2C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
+ M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
+#define SE_I2C_ABORT (1U << 1)
+/* M_CMD OP codes for I2C */
+#define I2C_WRITE (0x1)
+#define I2C_READ (0x2)
+#define I2C_WRITE_READ (0x3)
+#define I2C_ADDR_ONLY (0x4)
+#define I2C_BUS_CLEAR (0x6)
+#define I2C_STOP_ON_BUS (0x7)
+/* M_CMD params for I2C */
+#define PRE_CMD_DELAY (BIT(0))
+#define TIMESTAMP_BEFORE (BIT(1))
+#define STOP_STRETCH (BIT(2))
+#define TIMESTAMP_AFTER (BIT(3))
+#define POST_COMMAND_DELAY (BIT(4))
+#define IGNORE_ADD_NACK (BIT(6))
+#define READ_FINISHED_WITH_ACK (BIT(7))
+#define BYPASS_ADDR_PHASE (BIT(8))
+#define SLV_ADDR_MSK (GENMASK(15, 9))
+#define SLV_ADDR_SHFT (9)
+
+#define I2C_CORE2X_VOTE (10000)
+#define GP_IRQ0 0
+#define GP_IRQ1 1
+#define GP_IRQ2 2
+#define GP_IRQ3 3
+#define GP_IRQ4 4
+#define GP_IRQ5 5
+#define GENI_OVERRUN 6
+#define GENI_ILLEGAL_CMD 7
+#define GENI_ABORT_DONE 8
+#define GENI_TIMEOUT 9
+
+#define I2C_NACK GP_IRQ1
+#define I2C_BUS_PROTO GP_IRQ3
+#define I2C_ARB_LOST GP_IRQ4
+#define DM_I2C_CB_ERR ((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
+ << 5)
+
+#define I2C_AUTO_SUSPEND_DELAY 250
+#define KHz(freq) (1000 * freq)
+
+struct geni_i2c_dev {
+ struct device *dev;
+ void __iomem *base;
+ unsigned int tx_wm;
+ int irq;
+ int err;
+ struct i2c_adapter adap;
+ struct completion xfer;
+ struct i2c_msg *cur;
+ struct geni_se_rsc i2c_rsc;
+ int cur_wr;
+ int cur_rd;
+ struct device *wrapper_dev;
+ u32 clk_freq_out;
+ int clk_fld_idx;
+};
+
+struct geni_i2c_err_log {
+ int err;
+ const char *msg;
+};
+
+static struct geni_i2c_err_log gi2c_log[] = {
+ [GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
+ [I2C_NACK] = {-ENOTCONN,
+ "NACK: slv unresponsive, check its power/reset-ln"},
+ [GP_IRQ2] = {-EINVAL, "Unknown I2C err GP IRQ2"},
+ [I2C_BUS_PROTO] = {-EPROTO,
+ "Bus proto err, noisy/unepxected start/stop"},
+ [I2C_ARB_LOST] = {-EBUSY,
+ "Bus arbitration lost, clock line undriveable"},
+ [GP_IRQ5] = {-EINVAL, "Unknown I2C err GP IRQ5"},
+ [GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
+ [GENI_ILLEGAL_CMD] = {-EILSEQ,
+ "Illegal cmd, check GENI cmd-state machine"},
+ [GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
+ [GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
+};
+
+struct geni_i2c_clk_fld {
+ u32 clk_freq_out;
+ u8 clk_div;
+ u8 t_high;
+ u8 t_low;
+ u8 t_cycle;
+};
+
+static struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
+ {KHz(100), 7, 10, 11, 26},
+ {KHz(400), 2, 5, 12, 24},
+ {KHz(1000), 1, 3, 9, 18},
+};
+
+static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
+{
+ int i;
+ int ret = 0;
+ bool clk_map_present = false;
+ struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
+
+ for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
+ if (itr->clk_freq_out == gi2c->clk_freq_out) {
+ clk_map_present = true;
+ break;
+ }
+ }
+
+ if (clk_map_present)
+ gi2c->clk_fld_idx = i;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs)
+{
+ struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
+
+ geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL);
+
+ geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG);
+ geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) |
+ itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS);
+
+ /* Ensure Clk config completes before return */
+ mb();
+}
+
+static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
+{
+ u32 m_cmd = readl_relaxed(gi2c->base + SE_GENI_M_CMD0);
+ u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
+ u32 geni_s = readl_relaxed(gi2c->base + SE_GENI_STATUS);
+ u32 geni_ios = readl_relaxed(gi2c->base + SE_GENI_IOS);
+ u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
+ u32 rx_st, tx_st;
+
+ if (gi2c->cur)
+ dev_dbg(gi2c->dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
+ gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
+
+ if (err == I2C_NACK || err == GENI_ABORT_DONE) {
+ dev_dbg(gi2c->dev, "%s\n", gi2c_log[err].msg);
+ goto err_ret;
+ } else {
+ dev_err(gi2c->dev, "%s\n", gi2c_log[err].msg);
+ }
+
+ if (dma) {
+ rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
+ tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
+ } else {
+ rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
+ tx_st = readl_relaxed(gi2c->base + SE_GENI_TX_FIFO_STATUS);
+ }
+ dev_dbg(gi2c->dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n",
+ dma, tx_st, rx_st, m_stat);
+ dev_dbg(gi2c->dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
+ m_cmd, geni_s, geni_ios);
+err_ret:
+ gi2c->err = gi2c_log[err].err;
+}
+
+static irqreturn_t geni_i2c_irq(int irq, void *dev)
+{
+ struct geni_i2c_dev *gi2c = dev;
+ int i, j;
+ u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
+ u32 rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
+ u32 dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
+ u32 dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
+ u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
+ struct i2c_msg *cur = gi2c->cur;
+
+ if (!cur || (m_stat & M_CMD_FAILURE_EN) ||
+ (dm_rx_st & (DM_I2C_CB_ERR)) ||
+ (m_stat & M_CMD_ABORT_EN)) {
+
+ if (m_stat & M_GP_IRQ_1_EN)
+ geni_i2c_err(gi2c, I2C_NACK);
+ if (m_stat & M_GP_IRQ_3_EN)
+ geni_i2c_err(gi2c, I2C_BUS_PROTO);
+ if (m_stat & M_GP_IRQ_4_EN)
+ geni_i2c_err(gi2c, I2C_ARB_LOST);
+ if (m_stat & M_CMD_OVERRUN_EN)
+ geni_i2c_err(gi2c, GENI_OVERRUN);
+ if (m_stat & M_ILLEGAL_CMD_EN)
+ geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
+ if (m_stat & M_CMD_ABORT_EN)
+ geni_i2c_err(gi2c, GENI_ABORT_DONE);
+ if (m_stat & M_GP_IRQ_0_EN)
+ geni_i2c_err(gi2c, GP_IRQ0);
+
+ if (!dma)
+ writel_relaxed(0, (gi2c->base +
+ SE_GENI_TX_WATERMARK_REG));
+ goto irqret;
+ }
+
+ if (dma) {
+ dev_dbg(gi2c->dev, "i2c dma tx:0x%x, dma rx:0x%x\n", dm_tx_st,
+ dm_rx_st);
+ goto irqret;
+ }
+
+ if (((m_stat & M_RX_FIFO_WATERMARK_EN) ||
+ (m_stat & M_RX_FIFO_LAST_EN)) && (cur->flags & I2C_M_RD)) {
+ u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
+
+ for (j = 0; j < rxcnt; j++) {
+ u32 temp;
+ int p;
+
+ temp = readl_relaxed(gi2c->base + SE_GENI_RX_FIFOn);
+ for (i = gi2c->cur_rd, p = 0; (i < cur->len && p < 4);
+ i++, p++)
+ cur->buf[i] = (u8) ((temp >> (p * 8)) & 0xff);
+ gi2c->cur_rd = i;
+ if (gi2c->cur_rd == cur->len) {
+ dev_dbg(gi2c->dev, "FIFO i:%d,read 0x%x\n",
+ i, temp);
+ break;
+ }
+ }
+ } else if ((m_stat & M_TX_FIFO_WATERMARK_EN) &&
+ !(cur->flags & I2C_M_RD)) {
+ for (j = 0; j < gi2c->tx_wm; j++) {
+ u32 temp = 0;
+ int p;
+
+ for (i = gi2c->cur_wr, p = 0; (i < cur->len && p < 4);
+ i++, p++)
+ temp |= (((u32)(cur->buf[i]) << (p * 8)));
+ writel_relaxed(temp, gi2c->base + SE_GENI_TX_FIFOn);
+ gi2c->cur_wr = i;
+ dev_dbg(gi2c->dev, "FIFO i:%d,wrote 0x%x\n", i, temp);
+ if (gi2c->cur_wr == cur->len) {
+ dev_dbg(gi2c->dev, "FIFO i2c bytes done writing\n");
+ writel_relaxed(0,
+ (gi2c->base + SE_GENI_TX_WATERMARK_REG));
+ break;
+ }
+ }
+ }
+irqret:
+ if (m_stat)
+ writel_relaxed(m_stat, gi2c->base + SE_GENI_M_IRQ_CLEAR);
+
+ if (dma) {
+ if (dm_tx_st)
+ writel_relaxed(dm_tx_st, gi2c->base +
+ SE_DMA_TX_IRQ_CLR);
+ if (dm_rx_st)
+ writel_relaxed(dm_rx_st, gi2c->base +
+ SE_DMA_RX_IRQ_CLR);
+ /* Ensure all writes are done before returning from ISR. */
+ wmb();
+ }
+ /* if this is err with done-bit not set, handle that thr' timeout. */
+ if (m_stat & M_CMD_DONE_EN)
+ complete(&gi2c->xfer);
+ else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
+ complete(&gi2c->xfer);
+
+ return IRQ_HANDLED;
+}
+
+static int geni_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[],
+ int num)
+{
+ struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
+ int i, ret = 0, timeout = 0;
+
+ gi2c->err = 0;
+ gi2c->cur = &msgs[0];
+ reinit_completion(&gi2c->xfer);
+ ret = pm_runtime_get_sync(gi2c->dev);
+ if (ret < 0) {
+ dev_err(gi2c->dev, "error turning SE resources:%d\n", ret);
+ pm_runtime_put_noidle(gi2c->dev);
+ /* Set device in suspended since resume failed */
+ pm_runtime_set_suspended(gi2c->dev);
+ return ret;
+ }
+
+ qcom_geni_i2c_conf(gi2c, 0);
+ dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n",
+ num, msgs[0].len, msgs[0].flags);
+ for (i = 0; i < num; i++) {
+ int stretch = (i < (num - 1));
+ u32 m_param = 0;
+ u32 m_cmd = 0;
+ dma_addr_t tx_dma = 0;
+ dma_addr_t rx_dma = 0;
+ enum geni_se_xfer_mode mode = FIFO_MODE;
+
+ m_param |= (stretch ? STOP_STRETCH : 0);
+ m_param |= ((msgs[i].addr & 0x7F) << SLV_ADDR_SHFT);
+
+ gi2c->cur = &msgs[i];
+ mode = msgs[i].len > 32 ? SE_DMA : FIFO_MODE;
+ ret = geni_se_select_mode(gi2c->base, mode);
+ if (ret) {
+ dev_err(gi2c->dev, "%s: Error mode init %d:%d:%d\n",
+ __func__, mode, i, msgs[i].len);
+ break;
+ }
+ if (msgs[i].flags & I2C_M_RD) {
+ dev_dbg(gi2c->dev,
+ "READ,n:%d,i:%d len:%d, stretch:%d\n",
+ num, i, msgs[i].len, stretch);
+ geni_write_reg(msgs[i].len,
+ gi2c->base, SE_I2C_RX_TRANS_LEN);
+ m_cmd = I2C_READ;
+ geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
+ if (mode == SE_DMA) {
+ ret = geni_se_rx_dma_prep(gi2c->wrapper_dev,
+ gi2c->base, msgs[i].buf,
+ msgs[i].len, &rx_dma);
+ if (ret) {
+ mode = FIFO_MODE;
+ ret = geni_se_select_mode(gi2c->base,
+ mode);
+ }
+ }
+ } else {
+ dev_dbg(gi2c->dev,
+ "WRITE:n:%d,i:%d len:%d, stretch:%d, m_param:0x%x\n",
+ num, i, msgs[i].len, stretch, m_param);
+ geni_write_reg(msgs[i].len, gi2c->base,
+ SE_I2C_TX_TRANS_LEN);
+ m_cmd = I2C_WRITE;
+ geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
+ if (mode == SE_DMA) {
+ ret = geni_se_tx_dma_prep(gi2c->wrapper_dev,
+ gi2c->base, msgs[i].buf,
+ msgs[i].len, &tx_dma);
+ if (ret) {
+ mode = FIFO_MODE;
+ ret = geni_se_select_mode(gi2c->base,
+ mode);
+ }
+ }
+ if (mode == FIFO_MODE) /* Get FIFO IRQ */
+ geni_write_reg(1, gi2c->base,
+ SE_GENI_TX_WATERMARK_REG);
+ }
+ /* Ensure FIFO write go through before waiting for Done evet */
+ mb();
+ timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
+ if (!timeout) {
+ geni_i2c_err(gi2c, GENI_TIMEOUT);
+ gi2c->cur = NULL;
+ geni_se_abort_m_cmd(gi2c->base);
+ timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
+ }
+ gi2c->cur_wr = 0;
+ gi2c->cur_rd = 0;
+ if (mode == SE_DMA) {
+ if (gi2c->err) {
+ if (msgs[i].flags != I2C_M_RD)
+ writel_relaxed(1, gi2c->base +
+ SE_DMA_TX_FSM_RST);
+ else
+ writel_relaxed(1, gi2c->base +
+ SE_DMA_RX_FSM_RST);
+ wait_for_completion_timeout(&gi2c->xfer, HZ);
+ }
+ geni_se_rx_dma_unprep(gi2c->wrapper_dev, rx_dma,
+ msgs[i].len);
+ geni_se_tx_dma_unprep(gi2c->wrapper_dev, tx_dma,
+ msgs[i].len);
+ }
+ ret = gi2c->err;
+ if (gi2c->err) {
+ dev_err(gi2c->dev, "i2c error :%d\n", gi2c->err);
+ break;
+ }
+ }
+ if (ret == 0)
+ ret = num;
+
+ pm_runtime_mark_last_busy(gi2c->dev);
+ pm_runtime_put_autosuspend(gi2c->dev);
+ gi2c->cur = NULL;
+ gi2c->err = 0;
+ dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
+ return ret;
+}
+
+static u32 geni_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm geni_i2c_algo = {
+ .master_xfer = geni_i2c_xfer,
+ .functionality = geni_i2c_func,
+};
+
+static int geni_i2c_probe(struct platform_device *pdev)
+{
+ struct geni_i2c_dev *gi2c;
+ struct resource *res;
+ struct platform_device *wrapper_pdev;
+ struct device_node *wrapper_ph_node;
+ int ret;
+
+ gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
+ if (!gi2c)
+ return -ENOMEM;
+
+ gi2c->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ wrapper_ph_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,wrapper-core", 0);
+ if (IS_ERR_OR_NULL(wrapper_ph_node)) {
+ ret = PTR_ERR(wrapper_ph_node);
+ dev_err(&pdev->dev, "No wrapper core defined\n");
+ return ret;
+ }
+ wrapper_pdev = of_find_device_by_node(wrapper_ph_node);
+ of_node_put(wrapper_ph_node);
+ if (IS_ERR_OR_NULL(wrapper_pdev)) {
+ ret = PTR_ERR(wrapper_pdev);
+ dev_err(&pdev->dev, "Cannot retrieve wrapper device\n");
+ return ret;
+ }
+ gi2c->wrapper_dev = &wrapper_pdev->dev;
+ gi2c->i2c_rsc.wrapper_dev = &wrapper_pdev->dev;
+ gi2c->i2c_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
+ if (IS_ERR(gi2c->i2c_rsc.se_clk)) {
+ ret = PTR_ERR(gi2c->i2c_rsc.se_clk);
+ dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
+ return ret;
+ }
+
+ gi2c->i2c_rsc.m_ahb_clk = devm_clk_get(&pdev->dev, "m-ahb");
+ if (IS_ERR(gi2c->i2c_rsc.m_ahb_clk)) {
+ ret = PTR_ERR(gi2c->i2c_rsc.m_ahb_clk);
+ dev_err(&pdev->dev, "Err getting M AHB clk %d\n", ret);
+ return ret;
+ }
+
+ gi2c->i2c_rsc.s_ahb_clk = devm_clk_get(&pdev->dev, "s-ahb");
+ if (IS_ERR(gi2c->i2c_rsc.s_ahb_clk)) {
+ ret = PTR_ERR(gi2c->i2c_rsc.s_ahb_clk);
+ dev_err(&pdev->dev, "Err getting S AHB clk %d\n", ret);
+ return ret;
+ }
+
+ gi2c->base = devm_ioremap_resource(gi2c->dev, res);
+ if (IS_ERR(gi2c->base))
+ return PTR_ERR(gi2c->base);
+
+ gi2c->i2c_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_pinctrl)) {
+ dev_err(&pdev->dev, "No pinctrl config specified\n");
+ ret = PTR_ERR(gi2c->i2c_rsc.geni_pinctrl);
+ return ret;
+ }
+ gi2c->i2c_rsc.geni_gpio_active =
+ pinctrl_lookup_state(gi2c->i2c_rsc.geni_pinctrl,
+ PINCTRL_DEFAULT);
+ if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_gpio_active)) {
+ dev_err(&pdev->dev, "No default config specified\n");
+ ret = PTR_ERR(gi2c->i2c_rsc.geni_gpio_active);
+ return ret;
+ }
+ gi2c->i2c_rsc.geni_gpio_sleep =
+ pinctrl_lookup_state(gi2c->i2c_rsc.geni_pinctrl,
+ PINCTRL_SLEEP);
+ if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_gpio_sleep)) {
+ dev_err(&pdev->dev, "No sleep config specified\n");
+ ret = PTR_ERR(gi2c->i2c_rsc.geni_gpio_sleep);
+ return ret;
+ }
+
+ if (of_property_read_u32(pdev->dev.of_node, "qcom,clk-freq-out",
+ &gi2c->clk_freq_out)) {
+ dev_info(&pdev->dev,
+ "Bus frequency not specified, default to 400KHz.\n");
+ gi2c->clk_freq_out = KHz(400);
+ }
+
+ gi2c->irq = platform_get_irq(pdev, 0);
+ if (gi2c->irq < 0) {
+ dev_err(gi2c->dev, "IRQ error for i2c-geni\n");
+ return gi2c->irq;
+ }
+
+ ret = geni_i2c_clk_map_idx(gi2c);
+ if (ret) {
+ dev_err(gi2c->dev, "Invalid clk frequency %d KHz: %d\n",
+ gi2c->clk_freq_out, ret);
+ return ret;
+ }
+
+ gi2c->adap.algo = &geni_i2c_algo;
+ init_completion(&gi2c->xfer);
+ platform_set_drvdata(pdev, gi2c);
+ ret = devm_request_irq(gi2c->dev, gi2c->irq, geni_i2c_irq,
+ IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
+ if (ret) {
+ dev_err(gi2c->dev, "Request_irq failed:%d: err:%d\n",
+ gi2c->irq, ret);
+ return ret;
+ }
+ disable_irq(gi2c->irq);
+ i2c_set_adapdata(&gi2c->adap, gi2c);
+ gi2c->adap.dev.parent = gi2c->dev;
+ gi2c->adap.dev.of_node = pdev->dev.of_node;
+
+ strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
+
+ pm_runtime_set_suspended(gi2c->dev);
+ pm_runtime_set_autosuspend_delay(gi2c->dev, I2C_AUTO_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(gi2c->dev);
+ pm_runtime_enable(gi2c->dev);
+ i2c_add_adapter(&gi2c->adap);
+
+ dev_dbg(gi2c->dev, "I2C probed\n");
+ return 0;
+}
+
+static int geni_i2c_remove(struct platform_device *pdev)
+{
+ struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(gi2c->dev);
+ i2c_del_adapter(&gi2c->adap);
+ return 0;
+}
+
+static int geni_i2c_resume_noirq(struct device *device)
+{
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int geni_i2c_runtime_suspend(struct device *dev)
+{
+ struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
+
+ disable_irq(gi2c->irq);
+ geni_se_resources_off(&gi2c->i2c_rsc);
+ return 0;
+}
+
+static int geni_i2c_runtime_resume(struct device *dev)
+{
+ int ret;
+ struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
+
+ ret = geni_se_resources_on(&gi2c->i2c_rsc);
+ if (ret)
+ return ret;
+
+ if (!unlikely(gi2c->tx_wm)) {
+ int proto = geni_se_get_proto(gi2c->base);
+ int gi2c_tx_depth = geni_se_get_tx_fifo_depth(gi2c->base);
+
+ if (unlikely(proto != I2C)) {
+ dev_err(gi2c->dev, "Invalid proto %d\n", proto);
+ geni_se_resources_off(&gi2c->i2c_rsc);
+ return -ENXIO;
+ }
+
+ gi2c->tx_wm = gi2c_tx_depth - 1;
+ geni_se_init(gi2c->base, gi2c->tx_wm, gi2c_tx_depth);
+ geni_se_config_packing(gi2c->base, 8, 4, true);
+ dev_dbg(gi2c->dev, "i2c fifo/se-dma mode. fifo depth:%d\n",
+ gi2c_tx_depth);
+ }
+ enable_irq(gi2c->irq);
+
+ return 0;
+}
+
+static int geni_i2c_suspend_noirq(struct device *device)
+{
+ struct geni_i2c_dev *gi2c = dev_get_drvdata(device);
+ int ret;
+
+ /* Make sure no transactions are pending */
+ ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
+ if (!ret) {
+ dev_err(gi2c->dev, "late I2C transaction request\n");
+ return -EBUSY;
+ }
+ if (!pm_runtime_status_suspended(device)) {
+ geni_i2c_runtime_suspend(device);
+ pm_runtime_disable(device);
+ pm_runtime_set_suspended(device);
+ pm_runtime_enable(device);
+ }
+ i2c_unlock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
+ return 0;
+}
+#else
+static int geni_i2c_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int geni_i2c_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+
+static int geni_i2c_suspend_noirq(struct device *device)
+{
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops geni_i2c_pm_ops = {
+ .suspend_noirq = geni_i2c_suspend_noirq,
+ .resume_noirq = geni_i2c_resume_noirq,
+ .runtime_suspend = geni_i2c_runtime_suspend,
+ .runtime_resume = geni_i2c_runtime_resume,
+};
+
+static const struct of_device_id geni_i2c_dt_match[] = {
+ { .compatible = "qcom,i2c-geni" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
+
+static struct platform_driver geni_i2c_driver = {
+ .probe = geni_i2c_probe,
+ .remove = geni_i2c_remove,
+ .driver = {
+ .name = "i2c_geni",
+ .pm = &geni_i2c_pm_ops,
+ .of_match_table = geni_i2c_dt_match,
+ },
+};
+
+module_platform_driver(geni_i2c_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:i2c_geni");
--
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH RFC 4/7] i2c: Add device tree bindings for GENI I2C Controller
From: Karthikeyan Ramasubramanian @ 2017-12-27 16:27 UTC (permalink / raw)
To: linux-arm-msm, linux-i2c, linux-serial
Cc: Karthikeyan Ramasubramanian, linux-doc, devicetree, andy.gross,
david.brown, robh+dt, mark.rutland, corbet, wsa, gregkh, jslaby,
Sagar Dharia
In-Reply-To: <1514392046-30602-1-git-send-email-kramasub@codeaurora.org>
Add device tree binding support for I2C Controller in GENI based
QUP Wrapper.
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
---
.../devicetree/bindings/i2c/i2c-qcom-geni.txt | 39 ++++++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
diff --git a/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
new file mode 100644
index 0000000..d2fa9ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
@@ -0,0 +1,39 @@
+Qualcomm Technologies Inc. GENI based I2C Controller driver
+
+Required properties:
+ - compatible: Should be:
+ * "qcom,i2c-geni.
+ - reg: Should contain QUP register address and length.
+ - interrupts: Should contain I2C interrupt.
+ - clocks: Serial engine core clock, and AHB clocks needed by the device.
+ - pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
+ should be "active" and "sleep" for the pin confuguration when core is active
+ or when entering sleep state.
+ - #address-cells: Should be <1> Address cells for i2c device address
+ - #size-cells: Should be <0> as i2c addresses have no size component
+ - qcom,wrapper-core: Wrapper QUP core containing this I2C controller.
+
+Optional property:
+ - qcom,clk-freq-out : Desired I2C bus clock frequency in Hz.
+ When missing default to 400000Hz.
+
+Child nodes should conform to i2c bus binding.
+
+Example:
+
+i2c@a94000 {
+ compatible = "qcom,i2c-geni";
+ reg = <0xa94000 0x4000>;
+ interrupts = <GIC_SPI 358 0>;
+ clock-names = "se-clk", "m-ahb", "s-ahb";
+ clocks = <&clock_gcc GCC_QUPV3_WRAP0_S5_CLK>,
+ <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
+ <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&qup_1_i2c_5_active>;
+ pinctrl-1 = <&qup_1_i2c_5_sleep>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ qcom,wrapper-core = <&qup_0>;
+ qcom,clk-freq-out = <400000>;
+};
--
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH RFC 3/7] soc: qcom: Add GENI based QUP Wrapper driver
From: Karthikeyan Ramasubramanian @ 2017-12-27 16:27 UTC (permalink / raw)
To: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA
Cc: Karthikeyan Ramasubramanian, linux-doc-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
andy.gross-QSEj5FYQhm4dnm+yROfE0A,
david.brown-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
corbet-T1hC0tSOHrs, wsa-z923LK4zBo2bacvFa/9K2g,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, jslaby-IBi9RG/b67k,
Sagar Dharia, Girish Mahadevan
In-Reply-To: <1514392046-30602-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
This driver manages the Generic Interface (GENI) firmware based Qualcomm
Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
programmable module composed of multiple Serial Engines (SE) and supports
a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
driver also enables managing the serial interface independent aspects of
Serial Engines.
Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Girish Mahadevan <girishm-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
drivers/soc/qcom/Kconfig | 8 +
drivers/soc/qcom/Makefile | 1 +
drivers/soc/qcom/qcom-geni-se.c | 973 ++++++++++++++++++++++++++++++++++++++++
include/linux/qcom-geni-se.h | 811 +++++++++++++++++++++++++++++++++
4 files changed, 1793 insertions(+)
create mode 100644 drivers/soc/qcom/qcom-geni-se.c
create mode 100644 include/linux/qcom-geni-se.h
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index b81374b..b306d51 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -3,6 +3,14 @@
#
menu "Qualcomm SoC drivers"
+config QCOM_GENI_SE
+ tristate "QCOM GENI Serial Engine Driver"
+ help
+ This module is used to manage Generic Interface (GENI) firmware based
+ Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
+ module is also used to manage the common aspects of multiple Serial
+ Engines present in the QUP.
+
config QCOM_GLINK_SSR
tristate "Qualcomm Glink SSR driver"
depends on RPMSG
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 40c56f6..74d5db8 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
new file mode 100644
index 0000000..76aecfb
--- /dev/null
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -0,0 +1,973 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/qcom-geni-se.h>
+#include <linux/spinlock.h>
+
+#define MAX_CLK_PERF_LEVEL 32
+
+/**
+ * @struct geni_se_device - Data structure to represent the QUP Wrapper Core
+ * @dev: Device pointer of the QUP wrapper core.
+ * @base: Base address of this instance of QUP wrapper core.
+ * @geni_dev_lock: Lock to protect the device elements.
+ * @num_clk_levels: Number of valid clock levels in clk_perf_tbl.
+ * @clk_perf_tbl: Table of clock frequency input to Serial Engine clock.
+ */
+struct geni_se_device {
+ struct device *dev;
+ void __iomem *base;
+ struct mutex geni_dev_lock;
+ unsigned int num_clk_levels;
+ unsigned long *clk_perf_tbl;
+};
+
+/* Offset of QUP Hardware Version Register */
+#define QUP_HW_VER (0x4)
+
+#define HW_VER_MAJOR_MASK GENMASK(31, 28)
+#define HW_VER_MAJOR_SHFT 28
+#define HW_VER_MINOR_MASK GENMASK(27, 16)
+#define HW_VER_MINOR_SHFT 16
+#define HW_VER_STEP_MASK GENMASK(15, 0)
+
+/**
+ * geni_read_reg_nolog() - Helper function to read from a GENI register
+ * @base: Base address of the serial engine's register block.
+ * @offset: Offset within the serial engine's register block.
+ *
+ * Return: Return the contents of the register.
+ */
+unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
+{
+ return readl_relaxed(base + offset);
+}
+EXPORT_SYMBOL(geni_read_reg_nolog);
+
+/**
+ * geni_write_reg_nolog() - Helper function to write into a GENI register
+ * @value: Value to be written into the register.
+ * @base: Base address of the serial engine's register block.
+ * @offset: Offset within the serial engine's register block.
+ */
+void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset)
+{
+ return writel_relaxed(value, (base + offset));
+}
+EXPORT_SYMBOL(geni_write_reg_nolog);
+
+/**
+ * geni_read_reg() - Helper function to read from a GENI register
+ * @base: Base address of the serial engine's register block.
+ * @offset: Offset within the serial engine's register block.
+ *
+ * Return: Return the contents of the register.
+ */
+unsigned int geni_read_reg(void __iomem *base, int offset)
+{
+ return readl_relaxed(base + offset);
+}
+EXPORT_SYMBOL(geni_read_reg);
+
+/**
+ * geni_write_reg() - Helper function to write into a GENI register
+ * @value: Value to be written into the register.
+ * @base: Base address of the serial engine's register block.
+ * @offset: Offset within the serial engine's register block.
+ */
+void geni_write_reg(unsigned int value, void __iomem *base, int offset)
+{
+ return writel_relaxed(value, (base + offset));
+}
+EXPORT_SYMBOL(geni_write_reg);
+
+/**
+ * geni_get_qup_hw_version() - Read the QUP wrapper Hardware version
+ * @wrapper_dev: Pointer to the corresponding QUP wrapper core.
+ * @major: Buffer for Major Version field.
+ * @minor: Buffer for Minor Version field.
+ * @step: Buffer for Step Version field.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_get_qup_hw_version(struct device *wrapper_dev, unsigned int *major,
+ unsigned int *minor, unsigned int *step)
+{
+ unsigned int version;
+ struct geni_se_device *geni_se_dev;
+
+ if (!wrapper_dev || !major || !minor || !step)
+ return -EINVAL;
+
+ geni_se_dev = dev_get_drvdata(wrapper_dev);
+ if (unlikely(!geni_se_dev))
+ return -ENODEV;
+
+ version = geni_read_reg(geni_se_dev->base, QUP_HW_VER);
+ *major = (version & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT;
+ *minor = (version & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT;
+ *step = version & HW_VER_STEP_MASK;
+ return 0;
+}
+EXPORT_SYMBOL(geni_get_qup_hw_version);
+
+/**
+ * geni_se_get_proto() - Read the protocol configured for a serial engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * Return: Protocol value as configured in the serial engine.
+ */
+int geni_se_get_proto(void __iomem *base)
+{
+ int proto;
+
+ proto = ((geni_read_reg(base, GENI_FW_REVISION_RO)
+ & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT);
+ return proto;
+}
+EXPORT_SYMBOL(geni_se_get_proto);
+
+static int geni_se_irq_en(void __iomem *base)
+{
+ unsigned int common_geni_m_irq_en;
+ unsigned int common_geni_s_irq_en;
+
+ common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
+ common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
+ /* Common to all modes */
+ common_geni_m_irq_en |= M_COMMON_GENI_M_IRQ_EN;
+ common_geni_s_irq_en |= S_COMMON_GENI_S_IRQ_EN;
+
+ geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
+ geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
+ return 0;
+}
+
+
+static void geni_se_set_rx_rfr_wm(void __iomem *base, unsigned int rx_wm,
+ unsigned int rx_rfr)
+{
+ geni_write_reg(rx_wm, base, SE_GENI_RX_WATERMARK_REG);
+ geni_write_reg(rx_rfr, base, SE_GENI_RX_RFR_WATERMARK_REG);
+}
+
+static int geni_se_io_set_mode(void __iomem *base)
+{
+ unsigned int io_mode;
+ unsigned int geni_dma_mode;
+
+ io_mode = geni_read_reg(base, SE_IRQ_EN);
+ geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
+
+ io_mode |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
+ io_mode |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
+ geni_dma_mode &= ~GENI_DMA_MODE_EN;
+
+ geni_write_reg(io_mode, base, SE_IRQ_EN);
+ geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
+ geni_write_reg(0, base, SE_GSI_EVENT_EN);
+ return 0;
+}
+
+static void geni_se_io_init(void __iomem *base)
+{
+ unsigned int io_op_ctrl;
+ unsigned int geni_cgc_ctrl;
+ unsigned int dma_general_cfg;
+
+ geni_cgc_ctrl = geni_read_reg(base, GENI_CGC_CTRL);
+ dma_general_cfg = geni_read_reg(base, SE_DMA_GENERAL_CFG);
+ geni_cgc_ctrl |= DEFAULT_CGC_EN;
+ dma_general_cfg |= (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON |
+ DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON);
+ io_op_ctrl = DEFAULT_IO_OUTPUT_CTRL_MSK;
+ geni_write_reg(geni_cgc_ctrl, base, GENI_CGC_CTRL);
+ geni_write_reg(dma_general_cfg, base, SE_DMA_GENERAL_CFG);
+
+ geni_write_reg(io_op_ctrl, base, GENI_OUTPUT_CTRL);
+ geni_write_reg(FORCE_DEFAULT, base, GENI_FORCE_DEFAULT_REG);
+}
+
+/**
+ * geni_se_init() - Initialize the GENI Serial Engine
+ * @base: Base address of the serial engine's register block.
+ * @rx_wm: Receive watermark to be configured.
+ * @rx_rfr_wm: Ready-for-receive watermark to be configured.
+ *
+ * This function is used to initialize the GENI serial engine, configure
+ * receive watermark and ready-for-receive watermarks.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_init(void __iomem *base, unsigned int rx_wm, unsigned int rx_rfr)
+{
+ int ret;
+
+ geni_se_io_init(base);
+ ret = geni_se_io_set_mode(base);
+ if (ret)
+ return ret;
+
+ geni_se_set_rx_rfr_wm(base, rx_wm, rx_rfr);
+ ret = geni_se_irq_en(base);
+ return ret;
+}
+EXPORT_SYMBOL(geni_se_init);
+
+static int geni_se_select_fifo_mode(void __iomem *base)
+{
+ int proto = geni_se_get_proto(base);
+ unsigned int common_geni_m_irq_en;
+ unsigned int common_geni_s_irq_en;
+ unsigned int geni_dma_mode;
+
+ geni_write_reg(0, base, SE_GSI_EVENT_EN);
+ geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
+ geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
+ geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
+ geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
+ geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
+
+ common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
+ common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
+ geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
+ if (proto != UART) {
+ common_geni_m_irq_en |=
+ (M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN |
+ M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
+ common_geni_s_irq_en |= S_CMD_DONE_EN;
+ }
+ geni_dma_mode &= ~GENI_DMA_MODE_EN;
+
+ geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
+ geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
+ geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
+ return 0;
+}
+
+static int geni_se_select_dma_mode(void __iomem *base)
+{
+ unsigned int geni_dma_mode = 0;
+
+ geni_write_reg(0, base, SE_GSI_EVENT_EN);
+ geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
+ geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
+ geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
+ geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
+ geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
+
+ geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
+ geni_dma_mode |= GENI_DMA_MODE_EN;
+ geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
+ return 0;
+}
+
+/**
+ * geni_se_select_mode() - Select the serial engine transfer mode
+ * @base: Base address of the serial engine's register block.
+ * @mode: Transfer mode to be selected.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+int geni_se_select_mode(void __iomem *base, int mode)
+{
+ int ret = 0;
+
+ switch (mode) {
+ case FIFO_MODE:
+ geni_se_select_fifo_mode(base);
+ break;
+ case SE_DMA:
+ geni_se_select_dma_mode(base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(geni_se_select_mode);
+
+/**
+ * geni_se_setup_m_cmd() - Setup the primary sequencer
+ * @base: Base address of the serial engine's register block.
+ * @cmd: Command/Operation to setup in the primary sequencer.
+ * @params: Parameter for the sequencer command.
+ *
+ * This function is used to configure the primary sequencer with the
+ * command and its assoicated parameters.
+ */
+void geni_se_setup_m_cmd(void __iomem *base, u32 cmd, u32 params)
+{
+ u32 m_cmd = (cmd << M_OPCODE_SHFT);
+
+ m_cmd |= (params & M_PARAMS_MSK);
+ geni_write_reg(m_cmd, base, SE_GENI_M_CMD0);
+}
+EXPORT_SYMBOL(geni_se_setup_m_cmd);
+
+/**
+ * geni_se_setup_s_cmd() - Setup the secondary sequencer
+ * @base: Base address of the serial engine's register block.
+ * @cmd: Command/Operation to setup in the secondary sequencer.
+ * @params: Parameter for the sequencer command.
+ *
+ * This function is used to configure the secondary sequencer with the
+ * command and its assoicated parameters.
+ */
+void geni_se_setup_s_cmd(void __iomem *base, u32 cmd, u32 params)
+{
+ u32 s_cmd = geni_read_reg(base, SE_GENI_S_CMD0);
+
+ s_cmd &= ~(S_OPCODE_MSK | S_PARAMS_MSK);
+ s_cmd |= (cmd << S_OPCODE_SHFT);
+ s_cmd |= (params & S_PARAMS_MSK);
+ geni_write_reg(s_cmd, base, SE_GENI_S_CMD0);
+}
+EXPORT_SYMBOL(geni_se_setup_s_cmd);
+
+/**
+ * geni_se_cancel_m_cmd() - Cancel the command configured in the primary
+ * sequencer
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to cancel the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_cancel_m_cmd(void __iomem *base)
+{
+ geni_write_reg(M_GENI_CMD_CANCEL, base, SE_GENI_M_CMD_CTRL_REG);
+}
+EXPORT_SYMBOL(geni_se_cancel_m_cmd);
+
+/**
+ * geni_se_cancel_s_cmd() - Cancel the command configured in the secondary
+ * sequencer
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to cancel the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_cancel_s_cmd(void __iomem *base)
+{
+ geni_write_reg(S_GENI_CMD_CANCEL, base, SE_GENI_S_CMD_CTRL_REG);
+}
+EXPORT_SYMBOL(geni_se_cancel_s_cmd);
+
+/**
+ * geni_se_abort_m_cmd() - Abort the command configured in the primary sequencer
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to force abort the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_abort_m_cmd(void __iomem *base)
+{
+ geni_write_reg(M_GENI_CMD_ABORT, base, SE_GENI_M_CMD_CTRL_REG);
+}
+EXPORT_SYMBOL(geni_se_abort_m_cmd);
+
+/**
+ * geni_se_abort_s_cmd() - Abort the command configured in the secondary
+ * sequencer
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to force abort the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_abort_s_cmd(void __iomem *base)
+{
+ geni_write_reg(S_GENI_CMD_ABORT, base, SE_GENI_S_CMD_CTRL_REG);
+}
+EXPORT_SYMBOL(geni_se_abort_s_cmd);
+
+/**
+ * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to get the depth i.e. number of elements in the
+ * TX fifo of the serial engine.
+ *
+ * Return: TX fifo depth in units of FIFO words.
+ */
+int geni_se_get_tx_fifo_depth(void __iomem *base)
+{
+ int tx_fifo_depth;
+
+ tx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_0)
+ & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT);
+ return tx_fifo_depth;
+}
+EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
+
+/**
+ * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to get the width i.e. word size per element in the
+ * TX fifo of the serial engine.
+ *
+ * Return: TX fifo width in bits
+ */
+int geni_se_get_tx_fifo_width(void __iomem *base)
+{
+ int tx_fifo_width;
+
+ tx_fifo_width = ((geni_read_reg(base, SE_HW_PARAM_0)
+ & TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT);
+ return tx_fifo_width;
+}
+EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
+
+/**
+ * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to get the depth i.e. number of elements in the
+ * RX fifo of the serial engine.
+ *
+ * Return: RX fifo depth in units of FIFO words
+ */
+int geni_se_get_rx_fifo_depth(void __iomem *base)
+{
+ int rx_fifo_depth;
+
+ rx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_1)
+ & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT);
+ return rx_fifo_depth;
+}
+EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
+
+/**
+ * geni_se_get_packing_config() - Get the packing configuration based on input
+ * @bpw: Bits of data per transfer word.
+ * @pack_words: Number of words per fifo element.
+ * @msb_to_lsb: Transfer from MSB to LSB or vice-versa.
+ * @cfg0: Output buffer to hold the first half of configuration.
+ * @cfg1: Output buffer to hold the second half of configuration.
+ *
+ * This function is used to calculate the packing configuration based on
+ * the input packing requirement and the configuration logic.
+ */
+void geni_se_get_packing_config(int bpw, int pack_words, bool msb_to_lsb,
+ unsigned long *cfg0, unsigned long *cfg1)
+{
+ u32 cfg[4] = {0};
+ int len;
+ int temp_bpw = bpw;
+ int idx_start = (msb_to_lsb ? (bpw - 1) : 0);
+ int idx = idx_start;
+ int idx_delta = (msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE);
+ int ceil_bpw = ((bpw & (BITS_PER_BYTE - 1)) ?
+ ((bpw & ~(BITS_PER_BYTE - 1)) + BITS_PER_BYTE) : bpw);
+ int iter = (ceil_bpw * pack_words) >> 3;
+ int i;
+
+ if (unlikely(iter <= 0 || iter > 4)) {
+ *cfg0 = 0;
+ *cfg1 = 0;
+ return;
+ }
+
+ for (i = 0; i < iter; i++) {
+ len = (temp_bpw < BITS_PER_BYTE) ?
+ (temp_bpw - 1) : BITS_PER_BYTE - 1;
+ cfg[i] = ((idx << 5) | (msb_to_lsb << 4) | (len << 1));
+ idx = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
+ ((i + 1) * BITS_PER_BYTE) + idx_start :
+ idx + idx_delta;
+ temp_bpw = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
+ bpw : (temp_bpw - BITS_PER_BYTE);
+ }
+ cfg[iter - 1] |= 1;
+ *cfg0 = cfg[0] | (cfg[1] << 10);
+ *cfg1 = cfg[2] | (cfg[3] << 10);
+}
+EXPORT_SYMBOL(geni_se_get_packing_config);
+
+/**
+ * geni_se_config_packing() - Packing configuration of the serial engine
+ * @base: Base address of the serial engine's register block.
+ * @bpw: Bits of data per transfer word.
+ * @pack_words: Number of words per fifo element.
+ * @msb_to_lsb: Transfer from MSB to LSB or vice-versa.
+ *
+ * This function is used to configure the packing rules for the current
+ * transfer.
+ */
+void geni_se_config_packing(void __iomem *base, int bpw,
+ int pack_words, bool msb_to_lsb)
+{
+ unsigned long cfg0, cfg1;
+
+ geni_se_get_packing_config(bpw, pack_words, msb_to_lsb, &cfg0, &cfg1);
+ geni_write_reg(cfg0, base, SE_GENI_TX_PACKING_CFG0);
+ geni_write_reg(cfg1, base, SE_GENI_TX_PACKING_CFG1);
+ geni_write_reg(cfg0, base, SE_GENI_RX_PACKING_CFG0);
+ geni_write_reg(cfg1, base, SE_GENI_RX_PACKING_CFG1);
+ if (pack_words || bpw == 32)
+ geni_write_reg((bpw >> 4), base, SE_GENI_BYTE_GRAN);
+}
+EXPORT_SYMBOL(geni_se_config_packing);
+
+static void geni_se_clks_off(struct geni_se_rsc *rsc)
+{
+ clk_disable_unprepare(rsc->se_clk);
+ clk_disable_unprepare(rsc->s_ahb_clk);
+ clk_disable_unprepare(rsc->m_ahb_clk);
+}
+
+/**
+ * geni_se_resources_off() - Turn off resources associated with the serial
+ * engine
+ * @rsc: Handle to resources associated with the serial engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_off(struct geni_se_rsc *rsc)
+{
+ int ret = 0;
+ struct geni_se_device *geni_se_dev;
+
+ if (unlikely(!rsc || !rsc->wrapper_dev))
+ return -EINVAL;
+
+ geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+ if (unlikely(!geni_se_dev))
+ return -ENODEV;
+
+ ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
+ if (ret)
+ return ret;
+
+ geni_se_clks_off(rsc);
+ return 0;
+}
+EXPORT_SYMBOL(geni_se_resources_off);
+
+static int geni_se_clks_on(struct geni_se_rsc *rsc)
+{
+ int ret;
+
+ ret = clk_prepare_enable(rsc->m_ahb_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(rsc->s_ahb_clk);
+ if (ret) {
+ clk_disable_unprepare(rsc->m_ahb_clk);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(rsc->se_clk);
+ if (ret) {
+ clk_disable_unprepare(rsc->s_ahb_clk);
+ clk_disable_unprepare(rsc->m_ahb_clk);
+ }
+ return ret;
+}
+
+/**
+ * geni_se_resources_on() - Turn on resources associated with the serial
+ * engine
+ * @rsc: Handle to resources associated with the serial engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_on(struct geni_se_rsc *rsc)
+{
+ int ret = 0;
+ struct geni_se_device *geni_se_dev;
+
+ if (unlikely(!rsc || !rsc->wrapper_dev))
+ return -EINVAL;
+
+ geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+ if (unlikely(!geni_se_dev))
+ return -EPROBE_DEFER;
+
+ ret = geni_se_clks_on(rsc);
+ if (ret)
+ return ret;
+
+ ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_active);
+ if (ret)
+ geni_se_clks_off(rsc);
+
+ return ret;
+}
+EXPORT_SYMBOL(geni_se_resources_on);
+
+/**
+ * geni_se_clk_tbl_get() - Get the clock table to program DFS
+ * @rsc: Resource for which the clock table is requested.
+ * @tbl: Table in which the output is returned.
+ *
+ * This function is called by the protocol drivers to determine the different
+ * clock frequencies supported by Serail Engine Core Clock. The protocol
+ * drivers use the output to determine the clock frequency index to be
+ * programmed into DFS.
+ *
+ * Return: number of valid performance levels in the table on success,
+ * standard Linux error codes on failure.
+ */
+int geni_se_clk_tbl_get(struct geni_se_rsc *rsc, unsigned long **tbl)
+{
+ struct geni_se_device *geni_se_dev;
+ int i;
+ unsigned long prev_freq = 0;
+ int ret = 0;
+
+ if (unlikely(!rsc || !rsc->wrapper_dev || !rsc->se_clk || !tbl))
+ return -EINVAL;
+
+ *tbl = NULL;
+ geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+ if (unlikely(!geni_se_dev))
+ return -EPROBE_DEFER;
+
+ mutex_lock(&geni_se_dev->geni_dev_lock);
+ if (geni_se_dev->clk_perf_tbl) {
+ *tbl = geni_se_dev->clk_perf_tbl;
+ ret = geni_se_dev->num_clk_levels;
+ goto exit_se_clk_tbl_get;
+ }
+
+ geni_se_dev->clk_perf_tbl = kzalloc(sizeof(*geni_se_dev->clk_perf_tbl) *
+ MAX_CLK_PERF_LEVEL, GFP_KERNEL);
+ if (!geni_se_dev->clk_perf_tbl) {
+ ret = -ENOMEM;
+ goto exit_se_clk_tbl_get;
+ }
+
+ for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
+ geni_se_dev->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk,
+ prev_freq + 1);
+ if (geni_se_dev->clk_perf_tbl[i] == prev_freq) {
+ geni_se_dev->clk_perf_tbl[i] = 0;
+ break;
+ }
+ prev_freq = geni_se_dev->clk_perf_tbl[i];
+ }
+ geni_se_dev->num_clk_levels = i;
+ *tbl = geni_se_dev->clk_perf_tbl;
+ ret = geni_se_dev->num_clk_levels;
+exit_se_clk_tbl_get:
+ mutex_unlock(&geni_se_dev->geni_dev_lock);
+ return ret;
+}
+EXPORT_SYMBOL(geni_se_clk_tbl_get);
+
+/**
+ * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
+ * @rsc: Resource for which the clock frequency is requested.
+ * @req_freq: Requested clock frequency.
+ * @index: Index of the resultant frequency in the table.
+ * @res_freq: Resultant frequency which matches or is closer to the
+ * requested frequency.
+ * @exact: Flag to indicate exact multiple requirement of the requested
+ * frequency .
+ *
+ * This function is called by the protocol drivers to determine the matching
+ * or closest frequency of the Serial Engine clock to be selected in order
+ * to meet the performance requirements.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+int geni_se_clk_freq_match(struct geni_se_rsc *rsc, unsigned long req_freq,
+ unsigned int *index, unsigned long *res_freq,
+ bool exact)
+{
+ unsigned long *tbl;
+ int num_clk_levels;
+ int i;
+
+ num_clk_levels = geni_se_clk_tbl_get(rsc, &tbl);
+ if (num_clk_levels < 0)
+ return num_clk_levels;
+
+ if (num_clk_levels == 0)
+ return -EFAULT;
+
+ *res_freq = 0;
+ for (i = 0; i < num_clk_levels; i++) {
+ if (!(tbl[i] % req_freq)) {
+ *index = i;
+ *res_freq = tbl[i];
+ return 0;
+ }
+
+ if (!(*res_freq) || ((tbl[i] > *res_freq) &&
+ (tbl[i] < req_freq))) {
+ *index = i;
+ *res_freq = tbl[i];
+ }
+ }
+
+ if (exact || !(*res_freq))
+ return -ENOKEY;
+
+ return 0;
+}
+EXPORT_SYMBOL(geni_se_clk_freq_match);
+
+/**
+ * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
+ * @wrapper_dev: QUP Wrapper Device to which the TX buffer is mapped.
+ * @base: Base address of the SE register block.
+ * @tx_buf: Pointer to the TX buffer.
+ * @tx_len: Length of the TX buffer.
+ * @tx_dma: Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the buffers for DMA TX.
+ *
+ * Return: 0 on success, standard Linux error codes on error/failure.
+ */
+int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
+ void *tx_buf, int tx_len, dma_addr_t *tx_dma)
+{
+ int ret;
+
+ if (unlikely(!wrapper_dev || !base || !tx_buf || !tx_len || !tx_dma))
+ return -EINVAL;
+
+ ret = geni_se_map_buf(wrapper_dev, tx_dma, tx_buf, tx_len,
+ DMA_TO_DEVICE);
+ if (ret)
+ return ret;
+
+ geni_write_reg(7, base, SE_DMA_TX_IRQ_EN_SET);
+ geni_write_reg((u32)(*tx_dma), base, SE_DMA_TX_PTR_L);
+ geni_write_reg((u32)((*tx_dma) >> 32), base, SE_DMA_TX_PTR_H);
+ geni_write_reg(1, base, SE_DMA_TX_ATTR);
+ geni_write_reg(tx_len, base, SE_DMA_TX_LEN);
+ return 0;
+}
+EXPORT_SYMBOL(geni_se_tx_dma_prep);
+
+/**
+ * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
+ * @wrapper_dev: QUP Wrapper Device to which the RX buffer is mapped.
+ * @base: Base address of the SE register block.
+ * @rx_buf: Pointer to the RX buffer.
+ * @rx_len: Length of the RX buffer.
+ * @rx_dma: Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the buffers for DMA RX.
+ *
+ * Return: 0 on success, standard Linux error codes on error/failure.
+ */
+int geni_se_rx_dma_prep(struct device *wrapper_dev, void __iomem *base,
+ void *rx_buf, int rx_len, dma_addr_t *rx_dma)
+{
+ int ret;
+
+ if (unlikely(!wrapper_dev || !base || !rx_buf || !rx_len || !rx_dma))
+ return -EINVAL;
+
+ ret = geni_se_map_buf(wrapper_dev, rx_dma, rx_buf, rx_len,
+ DMA_FROM_DEVICE);
+ if (ret)
+ return ret;
+
+ geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);
+ geni_write_reg((u32)(*rx_dma), base, SE_DMA_RX_PTR_L);
+ geni_write_reg((u32)((*rx_dma) >> 32), base, SE_DMA_RX_PTR_H);
+ /* RX does not have EOT bit */
+ geni_write_reg(0, base, SE_DMA_RX_ATTR);
+ geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
+ return 0;
+}
+EXPORT_SYMBOL(geni_se_rx_dma_prep);
+
+/**
+ * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
+ * @wrapper_dev: QUP Wrapper Device to which the RX buffer is mapped.
+ * @tx_dma: DMA address of the TX buffer.
+ * @tx_len: Length of the TX buffer.
+ *
+ * This function is used to unprepare the DMA buffers after DMA TX.
+ */
+void geni_se_tx_dma_unprep(struct device *wrapper_dev,
+ dma_addr_t tx_dma, int tx_len)
+{
+ if (tx_dma)
+ geni_se_unmap_buf(wrapper_dev, &tx_dma, tx_len,
+ DMA_TO_DEVICE);
+}
+EXPORT_SYMBOL(geni_se_tx_dma_unprep);
+
+/**
+ * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
+ * @wrapper_dev: QUP Wrapper Device to which the RX buffer is mapped.
+ * @rx_dma: DMA address of the RX buffer.
+ * @rx_len: Length of the RX buffer.
+ *
+ * This function is used to unprepare the DMA buffers after DMA RX.
+ */
+void geni_se_rx_dma_unprep(struct device *wrapper_dev,
+ dma_addr_t rx_dma, int rx_len)
+{
+ if (rx_dma)
+ geni_se_unmap_buf(wrapper_dev, &rx_dma, rx_len,
+ DMA_FROM_DEVICE);
+}
+EXPORT_SYMBOL(geni_se_rx_dma_unprep);
+
+/**
+ * geni_se_map_buf() - Map a single buffer into QUP wrapper device
+ * @wrapper_dev: Pointer to the corresponding QUP wrapper core.
+ * @iova: Pointer in which the mapped virtual address is stored.
+ * @buf: Address of the buffer that needs to be mapped.
+ * @size: Size of the buffer.
+ * @dir: Direction of the DMA transfer.
+ *
+ * This function is used to map an already allocated buffer into the
+ * QUP device space.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_map_buf(struct device *wrapper_dev, dma_addr_t *iova,
+ void *buf, size_t size, enum dma_data_direction dir)
+{
+ struct device *dev_p;
+ struct geni_se_device *geni_se_dev;
+
+ if (!wrapper_dev || !iova || !buf || !size)
+ return -EINVAL;
+
+ geni_se_dev = dev_get_drvdata(wrapper_dev);
+ if (!geni_se_dev || !geni_se_dev->dev)
+ return -ENODEV;
+
+ dev_p = geni_se_dev->dev;
+
+ *iova = dma_map_single(dev_p, buf, size, dir);
+ if (dma_mapping_error(dev_p, *iova))
+ return -EIO;
+ return 0;
+}
+EXPORT_SYMBOL(geni_se_map_buf);
+
+/**
+ * geni_se_unmap_buf() - Unmap a single buffer from QUP wrapper device
+ * @wrapper_dev: Pointer to the corresponding QUP wrapper core.
+ * @iova: Pointer in which the mapped virtual address is stored.
+ * @size: Size of the buffer.
+ * @dir: Direction of the DMA transfer.
+ *
+ * This function is used to unmap an already mapped buffer from the
+ * QUP device space.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_unmap_buf(struct device *wrapper_dev, dma_addr_t *iova,
+ size_t size, enum dma_data_direction dir)
+{
+ struct device *dev_p;
+ struct geni_se_device *geni_se_dev;
+
+ if (!wrapper_dev || !iova || !size)
+ return -EINVAL;
+
+ geni_se_dev = dev_get_drvdata(wrapper_dev);
+ if (!geni_se_dev || !geni_se_dev->dev)
+ return -ENODEV;
+
+ dev_p = geni_se_dev->dev;
+ dma_unmap_single(dev_p, *iova, size, dir);
+ return 0;
+}
+EXPORT_SYMBOL(geni_se_unmap_buf);
+
+static const struct of_device_id geni_se_dt_match[] = {
+ { .compatible = "qcom,geni-se-qup", },
+ {}
+};
+
+static int geni_se_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct geni_se_device *geni_se_dev;
+
+ geni_se_dev = devm_kzalloc(dev, sizeof(*geni_se_dev), GFP_KERNEL);
+ if (!geni_se_dev)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "%s: Mandatory resource info not found\n",
+ __func__);
+ devm_kfree(dev, geni_se_dev);
+ return -EINVAL;
+ }
+
+ geni_se_dev->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR_OR_NULL(geni_se_dev->base)) {
+ dev_err(dev, "%s: Error mapping the resource\n", __func__);
+ devm_kfree(dev, geni_se_dev);
+ return -EFAULT;
+ }
+
+ geni_se_dev->dev = dev;
+ mutex_init(&geni_se_dev->geni_dev_lock);
+ dev_set_drvdata(dev, geni_se_dev);
+ dev_dbg(dev, "GENI SE Driver probed\n");
+ return 0;
+}
+
+static int geni_se_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct geni_se_device *geni_se_dev = dev_get_drvdata(dev);
+
+ devm_iounmap(dev, geni_se_dev->base);
+ devm_kfree(dev, geni_se_dev);
+ return 0;
+}
+
+static struct platform_driver geni_se_driver = {
+ .driver = {
+ .name = "geni_se_qup",
+ .of_match_table = geni_se_dt_match,
+ },
+ .probe = geni_se_probe,
+ .remove = geni_se_remove,
+};
+
+static int __init geni_se_driver_init(void)
+{
+ return platform_driver_register(&geni_se_driver);
+}
+arch_initcall(geni_se_driver_init);
+
+static void __exit geni_se_driver_exit(void)
+{
+ platform_driver_unregister(&geni_se_driver);
+}
+module_exit(geni_se_driver_exit);
+
+MODULE_DESCRIPTION("GENI Serial Engine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
new file mode 100644
index 0000000..7b60839
--- /dev/null
+++ b/include/linux/qcom-geni-se.h
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_QCOM_GENI_SE
+#define _LINUX_QCOM_GENI_SE
+#include <linux/clk.h>
+#include <linux/dma-direction.h>
+#include <linux/io.h>
+#include <linux/list.h>
+
+/* Transfer mode supported by GENI Serial Engines */
+enum geni_se_xfer_mode {
+ INVALID,
+ FIFO_MODE,
+ SE_DMA,
+};
+
+/* Protocols supported by GENI Serial Engines */
+enum geni_se_protocol_types {
+ NONE,
+ SPI,
+ UART,
+ I2C,
+ I3C
+};
+
+/**
+ * struct geni_se_rsc - GENI Serial Engine Resource
+ * @wrapper_dev: Pointer to the parent QUP Wrapper core.
+ * @se_clk: Handle to the core serial engine clock.
+ * @m_ahb_clk: Handle to the primary AHB clock.
+ * @s_ahb_clk: Handle to the secondary AHB clock.
+ * @geni_pinctrl: Handle to the pinctrl configuration.
+ * @geni_gpio_active: Handle to the default/active pinctrl state.
+ * @geni_gpi_sleep: Handle to the sleep pinctrl state.
+ */
+struct geni_se_rsc {
+ struct device *wrapper_dev;
+ struct clk *se_clk;
+ struct clk *m_ahb_clk;
+ struct clk *s_ahb_clk;
+ struct pinctrl *geni_pinctrl;
+ struct pinctrl_state *geni_gpio_active;
+ struct pinctrl_state *geni_gpio_sleep;
+};
+
+#define PINCTRL_DEFAULT "default"
+#define PINCTRL_SLEEP "sleep"
+
+/* Common SE registers */
+#define GENI_INIT_CFG_REVISION (0x0)
+#define GENI_S_INIT_CFG_REVISION (0x4)
+#define GENI_FORCE_DEFAULT_REG (0x20)
+#define GENI_OUTPUT_CTRL (0x24)
+#define GENI_CGC_CTRL (0x28)
+#define SE_GENI_STATUS (0x40)
+#define GENI_SER_M_CLK_CFG (0x48)
+#define GENI_SER_S_CLK_CFG (0x4C)
+#define GENI_CLK_CTRL_RO (0x60)
+#define GENI_IF_DISABLE_RO (0x64)
+#define GENI_FW_REVISION_RO (0x68)
+#define GENI_FW_S_REVISION_RO (0x6C)
+#define SE_GENI_CLK_SEL (0x7C)
+#define SE_GENI_BYTE_GRAN (0x254)
+#define SE_GENI_DMA_MODE_EN (0x258)
+#define SE_GENI_TX_PACKING_CFG0 (0x260)
+#define SE_GENI_TX_PACKING_CFG1 (0x264)
+#define SE_GENI_RX_PACKING_CFG0 (0x284)
+#define SE_GENI_RX_PACKING_CFG1 (0x288)
+#define SE_GENI_M_CMD0 (0x600)
+#define SE_GENI_M_CMD_CTRL_REG (0x604)
+#define SE_GENI_M_IRQ_STATUS (0x610)
+#define SE_GENI_M_IRQ_EN (0x614)
+#define SE_GENI_M_IRQ_CLEAR (0x618)
+#define SE_GENI_S_CMD0 (0x630)
+#define SE_GENI_S_CMD_CTRL_REG (0x634)
+#define SE_GENI_S_IRQ_STATUS (0x640)
+#define SE_GENI_S_IRQ_EN (0x644)
+#define SE_GENI_S_IRQ_CLEAR (0x648)
+#define SE_GENI_TX_FIFOn (0x700)
+#define SE_GENI_RX_FIFOn (0x780)
+#define SE_GENI_TX_FIFO_STATUS (0x800)
+#define SE_GENI_RX_FIFO_STATUS (0x804)
+#define SE_GENI_TX_WATERMARK_REG (0x80C)
+#define SE_GENI_RX_WATERMARK_REG (0x810)
+#define SE_GENI_RX_RFR_WATERMARK_REG (0x814)
+#define SE_GENI_IOS (0x908)
+#define SE_GENI_M_GP_LENGTH (0x910)
+#define SE_GENI_S_GP_LENGTH (0x914)
+#define SE_GSI_EVENT_EN (0xE18)
+#define SE_IRQ_EN (0xE1C)
+#define SE_HW_PARAM_0 (0xE24)
+#define SE_HW_PARAM_1 (0xE28)
+#define SE_DMA_GENERAL_CFG (0xE30)
+
+/* GENI_OUTPUT_CTRL fields */
+#define DEFAULT_IO_OUTPUT_CTRL_MSK (GENMASK(6, 0))
+
+/* GENI_FORCE_DEFAULT_REG fields */
+#define FORCE_DEFAULT (BIT(0))
+
+/* GENI_CGC_CTRL fields */
+#define CFG_AHB_CLK_CGC_ON (BIT(0))
+#define CFG_AHB_WR_ACLK_CGC_ON (BIT(1))
+#define DATA_AHB_CLK_CGC_ON (BIT(2))
+#define SCLK_CGC_ON (BIT(3))
+#define TX_CLK_CGC_ON (BIT(4))
+#define RX_CLK_CGC_ON (BIT(5))
+#define EXT_CLK_CGC_ON (BIT(6))
+#define PROG_RAM_HCLK_OFF (BIT(8))
+#define PROG_RAM_SCLK_OFF (BIT(9))
+#define DEFAULT_CGC_EN (GENMASK(6, 0))
+
+/* GENI_STATUS fields */
+#define M_GENI_CMD_ACTIVE (BIT(0))
+#define S_GENI_CMD_ACTIVE (BIT(12))
+
+/* GENI_SER_M_CLK_CFG/GENI_SER_S_CLK_CFG */
+#define SER_CLK_EN (BIT(0))
+#define CLK_DIV_MSK (GENMASK(15, 4))
+#define CLK_DIV_SHFT (4)
+
+/* CLK_CTRL_RO fields */
+
+/* IF_DISABLE_RO fields */
+
+/* FW_REVISION_RO fields */
+#define FW_REV_PROTOCOL_MSK (GENMASK(15, 8))
+#define FW_REV_PROTOCOL_SHFT (8)
+
+/* GENI_CLK_SEL fields */
+#define CLK_SEL_MSK (GENMASK(2, 0))
+
+/* SE_GENI_DMA_MODE_EN */
+#define GENI_DMA_MODE_EN (BIT(0))
+
+/* GENI_M_CMD0 fields */
+#define M_OPCODE_MSK (GENMASK(31, 27))
+#define M_OPCODE_SHFT (27)
+#define M_PARAMS_MSK (GENMASK(26, 0))
+
+/* GENI_M_CMD_CTRL_REG */
+#define M_GENI_CMD_CANCEL BIT(2)
+#define M_GENI_CMD_ABORT BIT(1)
+#define M_GENI_DISABLE BIT(0)
+
+/* GENI_S_CMD0 fields */
+#define S_OPCODE_MSK (GENMASK(31, 27))
+#define S_OPCODE_SHFT (27)
+#define S_PARAMS_MSK (GENMASK(26, 0))
+
+/* GENI_S_CMD_CTRL_REG */
+#define S_GENI_CMD_CANCEL (BIT(2))
+#define S_GENI_CMD_ABORT (BIT(1))
+#define S_GENI_DISABLE (BIT(0))
+
+/* GENI_M_IRQ_EN fields */
+#define M_CMD_DONE_EN (BIT(0))
+#define M_CMD_OVERRUN_EN (BIT(1))
+#define M_ILLEGAL_CMD_EN (BIT(2))
+#define M_CMD_FAILURE_EN (BIT(3))
+#define M_CMD_CANCEL_EN (BIT(4))
+#define M_CMD_ABORT_EN (BIT(5))
+#define M_TIMESTAMP_EN (BIT(6))
+#define M_RX_IRQ_EN (BIT(7))
+#define M_GP_SYNC_IRQ_0_EN (BIT(8))
+#define M_GP_IRQ_0_EN (BIT(9))
+#define M_GP_IRQ_1_EN (BIT(10))
+#define M_GP_IRQ_2_EN (BIT(11))
+#define M_GP_IRQ_3_EN (BIT(12))
+#define M_GP_IRQ_4_EN (BIT(13))
+#define M_GP_IRQ_5_EN (BIT(14))
+#define M_IO_DATA_DEASSERT_EN (BIT(22))
+#define M_IO_DATA_ASSERT_EN (BIT(23))
+#define M_RX_FIFO_RD_ERR_EN (BIT(24))
+#define M_RX_FIFO_WR_ERR_EN (BIT(25))
+#define M_RX_FIFO_WATERMARK_EN (BIT(26))
+#define M_RX_FIFO_LAST_EN (BIT(27))
+#define M_TX_FIFO_RD_ERR_EN (BIT(28))
+#define M_TX_FIFO_WR_ERR_EN (BIT(29))
+#define M_TX_FIFO_WATERMARK_EN (BIT(30))
+#define M_SEC_IRQ_EN (BIT(31))
+#define M_COMMON_GENI_M_IRQ_EN (GENMASK(6, 1) | \
+ M_IO_DATA_DEASSERT_EN | \
+ M_IO_DATA_ASSERT_EN | M_RX_FIFO_RD_ERR_EN | \
+ M_RX_FIFO_WR_ERR_EN | M_TX_FIFO_RD_ERR_EN | \
+ M_TX_FIFO_WR_ERR_EN)
+
+/* GENI_S_IRQ_EN fields */
+#define S_CMD_DONE_EN (BIT(0))
+#define S_CMD_OVERRUN_EN (BIT(1))
+#define S_ILLEGAL_CMD_EN (BIT(2))
+#define S_CMD_FAILURE_EN (BIT(3))
+#define S_CMD_CANCEL_EN (BIT(4))
+#define S_CMD_ABORT_EN (BIT(5))
+#define S_GP_SYNC_IRQ_0_EN (BIT(8))
+#define S_GP_IRQ_0_EN (BIT(9))
+#define S_GP_IRQ_1_EN (BIT(10))
+#define S_GP_IRQ_2_EN (BIT(11))
+#define S_GP_IRQ_3_EN (BIT(12))
+#define S_GP_IRQ_4_EN (BIT(13))
+#define S_GP_IRQ_5_EN (BIT(14))
+#define S_IO_DATA_DEASSERT_EN (BIT(22))
+#define S_IO_DATA_ASSERT_EN (BIT(23))
+#define S_RX_FIFO_RD_ERR_EN (BIT(24))
+#define S_RX_FIFO_WR_ERR_EN (BIT(25))
+#define S_RX_FIFO_WATERMARK_EN (BIT(26))
+#define S_RX_FIFO_LAST_EN (BIT(27))
+#define S_COMMON_GENI_S_IRQ_EN (GENMASK(5, 1) | GENMASK(13, 9) | \
+ S_RX_FIFO_RD_ERR_EN | S_RX_FIFO_WR_ERR_EN)
+
+/* GENI_/TX/RX/RX_RFR/_WATERMARK_REG fields */
+#define WATERMARK_MSK (GENMASK(5, 0))
+
+/* GENI_TX_FIFO_STATUS fields */
+#define TX_FIFO_WC (GENMASK(27, 0))
+
+/* GENI_RX_FIFO_STATUS fields */
+#define RX_LAST (BIT(31))
+#define RX_LAST_BYTE_VALID_MSK (GENMASK(30, 28))
+#define RX_LAST_BYTE_VALID_SHFT (28)
+#define RX_FIFO_WC_MSK (GENMASK(24, 0))
+
+/* SE_GSI_EVENT_EN fields */
+#define DMA_RX_EVENT_EN (BIT(0))
+#define DMA_TX_EVENT_EN (BIT(1))
+#define GENI_M_EVENT_EN (BIT(2))
+#define GENI_S_EVENT_EN (BIT(3))
+
+/* SE_GENI_IOS fields */
+#define IO2_DATA_IN (BIT(1))
+#define RX_DATA_IN (BIT(0))
+
+/* SE_IRQ_EN fields */
+#define DMA_RX_IRQ_EN (BIT(0))
+#define DMA_TX_IRQ_EN (BIT(1))
+#define GENI_M_IRQ_EN (BIT(2))
+#define GENI_S_IRQ_EN (BIT(3))
+
+/* SE_HW_PARAM_0 fields */
+#define TX_FIFO_WIDTH_MSK (GENMASK(29, 24))
+#define TX_FIFO_WIDTH_SHFT (24)
+#define TX_FIFO_DEPTH_MSK (GENMASK(21, 16))
+#define TX_FIFO_DEPTH_SHFT (16)
+
+/* SE_HW_PARAM_1 fields */
+#define RX_FIFO_WIDTH_MSK (GENMASK(29, 24))
+#define RX_FIFO_WIDTH_SHFT (24)
+#define RX_FIFO_DEPTH_MSK (GENMASK(21, 16))
+#define RX_FIFO_DEPTH_SHFT (16)
+
+/* SE_DMA_GENERAL_CFG */
+#define DMA_RX_CLK_CGC_ON (BIT(0))
+#define DMA_TX_CLK_CGC_ON (BIT(1))
+#define DMA_AHB_SLV_CFG_ON (BIT(2))
+#define AHB_SEC_SLV_CLK_CGC_ON (BIT(3))
+#define DUMMY_RX_NON_BUFFERABLE (BIT(4))
+#define RX_DMA_ZERO_PADDING_EN (BIT(5))
+#define RX_DMA_IRQ_DELAY_MSK (GENMASK(8, 6))
+#define RX_DMA_IRQ_DELAY_SHFT (6)
+
+#define SE_DMA_TX_PTR_L (0xC30)
+#define SE_DMA_TX_PTR_H (0xC34)
+#define SE_DMA_TX_ATTR (0xC38)
+#define SE_DMA_TX_LEN (0xC3C)
+#define SE_DMA_TX_IRQ_STAT (0xC40)
+#define SE_DMA_TX_IRQ_CLR (0xC44)
+#define SE_DMA_TX_IRQ_EN (0xC48)
+#define SE_DMA_TX_IRQ_EN_SET (0xC4C)
+#define SE_DMA_TX_IRQ_EN_CLR (0xC50)
+#define SE_DMA_TX_LEN_IN (0xC54)
+#define SE_DMA_TX_FSM_RST (0xC58)
+#define SE_DMA_TX_MAX_BURST (0xC5C)
+
+#define SE_DMA_RX_PTR_L (0xD30)
+#define SE_DMA_RX_PTR_H (0xD34)
+#define SE_DMA_RX_ATTR (0xD38)
+#define SE_DMA_RX_LEN (0xD3C)
+#define SE_DMA_RX_IRQ_STAT (0xD40)
+#define SE_DMA_RX_IRQ_CLR (0xD44)
+#define SE_DMA_RX_IRQ_EN (0xD48)
+#define SE_DMA_RX_IRQ_EN_SET (0xD4C)
+#define SE_DMA_RX_IRQ_EN_CLR (0xD50)
+#define SE_DMA_RX_LEN_IN (0xD54)
+#define SE_DMA_RX_FSM_RST (0xD58)
+#define SE_DMA_RX_MAX_BURST (0xD5C)
+#define SE_DMA_RX_FLUSH (0xD60)
+
+/* SE_DMA_TX_IRQ_STAT Register fields */
+#define TX_DMA_DONE (BIT(0))
+#define TX_EOT (BIT(1))
+#define TX_SBE (BIT(2))
+#define TX_RESET_DONE (BIT(3))
+
+/* SE_DMA_RX_IRQ_STAT Register fields */
+#define RX_DMA_DONE (BIT(0))
+#define RX_EOT (BIT(1))
+#define RX_SBE (BIT(2))
+#define RX_RESET_DONE (BIT(3))
+#define RX_FLUSH_DONE (BIT(4))
+#define RX_GENI_GP_IRQ (GENMASK(10, 5))
+#define RX_GENI_CANCEL_IRQ (BIT(11))
+#define RX_GENI_GP_IRQ_EXT (GENMASK(13, 12))
+
+#ifdef CONFIG_QCOM_GENI_SE
+/**
+ * geni_read_reg_nolog() - Helper function to read from a GENI register
+ * @base: Base address of the serial engine's register block.
+ * @offset: Offset within the serial engine's register block.
+ *
+ * Return: Return the contents of the register.
+ */
+unsigned int geni_read_reg_nolog(void __iomem *base, int offset);
+
+/**
+ * geni_write_reg_nolog() - Helper function to write into a GENI register
+ * @value: Value to be written into the register.
+ * @base: Base address of the serial engine's register block.
+ * @offset: Offset within the serial engine's register block.
+ */
+void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset);
+
+/**
+ * geni_read_reg() - Helper function to read from a GENI register
+ * @base: Base address of the serial engine's register block.
+ * @offset: Offset within the serial engine's register block.
+ *
+ * Return: Return the contents of the register.
+ */
+unsigned int geni_read_reg(void __iomem *base, int offset);
+
+/**
+ * geni_write_reg() - Helper function to write into a GENI register
+ * @value: Value to be written into the register.
+ * @base: Base address of the serial engine's register block.
+ * @offset: Offset within the serial engine's register block.
+ */
+void geni_write_reg(unsigned int value, void __iomem *base, int offset);
+
+/**
+ * geni_get_qup_hw_version() - Read the QUP Wrapper Hardware version
+ * @wrapper_dev: Pointer to the corresponding QUP wrapper core.
+ * @major: Buffer for Major Version field.
+ * @minor: Buffer for Minor Version field.
+ * @step: Buffer for Step Version field.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_get_qup_hw_version(struct device *wrapper_dev, unsigned int *major,
+ unsigned int *minor, unsigned int *step);
+
+/**
+ * geni_se_get_proto() - Read the protocol configured for a serial engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * Return: Protocol value as configured in the serial engine.
+ */
+int geni_se_get_proto(void __iomem *base);
+
+/**
+ * geni_se_init() - Initialize the GENI Serial Engine
+ * @base: Base address of the serial engine's register block.
+ * @rx_wm: Receive watermark to be configured.
+ * @rx_rfr_wm: Ready-for-receive watermark to be configured.
+ *
+ * This function is used to initialize the GENI serial engine, configure
+ * the transfer mode, receive watermark and ready-for-receive watermarks.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_init(void __iomem *base, unsigned int rx_wm, unsigned int rx_rfr);
+
+/**
+ * geni_se_select_mode() - Select the serial engine transfer mode
+ * @base: Base address of the serial engine's register block.
+ * @mode: Transfer mode to be selected.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+int geni_se_select_mode(void __iomem *base, int mode);
+
+/**
+ * geni_se_setup_m_cmd() - Setup the primary sequencer
+ * @base: Base address of the serial engine's register block.
+ * @cmd: Command/Operation to setup in the primary sequencer.
+ * @params: Parameter for the sequencer command.
+ *
+ * This function is used to configure the primary sequencer with the
+ * command and its assoicated parameters.
+ */
+void geni_se_setup_m_cmd(void __iomem *base, u32 cmd, u32 params);
+
+/**
+ * geni_se_setup_s_cmd() - Setup the secondary sequencer
+ * @base: Base address of the serial engine's register block.
+ * @cmd: Command/Operation to setup in the secondary sequencer.
+ * @params: Parameter for the sequencer command.
+ *
+ * This function is used to configure the secondary sequencer with the
+ * command and its assoicated parameters.
+ */
+void geni_se_setup_s_cmd(void __iomem *base, u32 cmd, u32 params);
+
+/**
+ * geni_se_cancel_m_cmd() - Cancel the command configured in the primary
+ * sequencer
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to cancel the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_cancel_m_cmd(void __iomem *base);
+
+/**
+ * geni_se_cancel_s_cmd() - Cancel the command configured in the secondary
+ * sequencer
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to cancel the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_cancel_s_cmd(void __iomem *base);
+
+/**
+ * geni_se_abort_m_cmd() - Abort the command configured in the primary sequencer
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to force abort the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_abort_m_cmd(void __iomem *base);
+
+/**
+ * geni_se_abort_s_cmd() - Abort the command configured in the secondary
+ * sequencer
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to force abort the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_abort_s_cmd(void __iomem *base);
+
+/**
+ * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to get the depth i.e. number of elements in the
+ * TX fifo of the serial engine.
+ *
+ * Return: TX fifo depth in units of FIFO words.
+ */
+int geni_se_get_tx_fifo_depth(void __iomem *base);
+
+/**
+ * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to get the width i.e. word size per element in the
+ * TX fifo of the serial engine.
+ *
+ * Return: TX fifo width in bits.
+ */
+int geni_se_get_tx_fifo_width(void __iomem *base);
+
+/**
+ * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * This function is used to get the depth i.e. number of elements in the
+ * RX fifo of the serial engine.
+ *
+ * Return: RX fifo depth in units of FIFO words.
+ */
+int geni_se_get_rx_fifo_depth(void __iomem *base);
+
+/**
+ * geni_se_get_packing_config() - Get the packing configuration based on input
+ * @bpw: Bits of data per transfer word.
+ * @pack_words: Number of words per fifo element.
+ * @msb_to_lsb: Transfer from MSB to LSB or vice-versa.
+ * @cfg0: Output buffer to hold the first half of configuration.
+ * @cfg1: Output buffer to hold the second half of configuration.
+ *
+ * This function is used to calculate the packing configuration based on
+ * the input packing requirement and the configuration logic.
+ */
+void geni_se_get_packing_config(int bpw, int pack_words, bool msb_to_lsb,
+ unsigned long *cfg0, unsigned long *cfg1);
+
+/**
+ * geni_se_config_packing() - Packing configuration of the serial engine
+ * @base: Base address of the serial engine's register block.
+ * @bpw: Bits of data per transfer word.
+ * @pack_words: Number of words per fifo element.
+ * @msb_to_lsb: Transfer from MSB to LSB or vice-versa.
+ *
+ * This function is used to configure the packing rules for the current
+ * transfer.
+ */
+void geni_se_config_packing(void __iomem *base, int bpw, int pack_words,
+ bool msb_to_lsb);
+
+/**
+ * geni_se_resources_off() - Turn off resources associated with the serial
+ * engine
+ * @rsc: Handle to resources associated with the serial engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_off(struct geni_se_rsc *rsc);
+
+/**
+ * geni_se_resources_on() - Turn on resources associated with the serial
+ * engine
+ * @rsc: Handle to resources associated with the serial engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_on(struct geni_se_rsc *rsc);
+
+/**
+ * geni_se_clk_tbl_get() - Get the clock table to program DFS
+ * @rsc: Resource for which the clock table is requested.
+ * @tbl: Table in which the output is returned.
+ *
+ * This function is called by the protocol drivers to determine the different
+ * clock frequencies supported by Serail Engine Core Clock. The protocol
+ * drivers use the output to determine the clock frequency index to be
+ * programmed into DFS.
+ *
+ * Return: number of valid performance levels in the table on success,
+ * standard Linux error codes on failure.
+ */
+int geni_se_clk_tbl_get(struct geni_se_rsc *rsc, unsigned long **tbl);
+
+/**
+ * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
+ * @rsc: Resource for which the clock frequency is requested.
+ * @req_freq: Requested clock frequency.
+ * @index: Index of the resultant frequency in the table.
+ * @res_freq: Resultant frequency which matches or is closer to the
+ * requested frequency.
+ * @exact: Flag to indicate exact multiple requirement of the requested
+ * frequency .
+ *
+ * This function is called by the protocol drivers to determine the matching
+ * or closest frequency of the Serial Engine clock to be selected in order
+ * to meet the performance requirements.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+int geni_se_clk_freq_match(struct geni_se_rsc *rsc, unsigned long req_freq,
+ unsigned int *index, unsigned long *res_freq,
+ bool exact);
+
+/**
+ * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
+ * @wrapper_dev: QUP Wrapper Device to which the TX buffer is mapped.
+ * @base: Base address of the SE register block.
+ * @tx_buf: Pointer to the TX buffer.
+ * @tx_len: Length of the TX buffer.
+ * @tx_dma: Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the buffers for DMA TX.
+ *
+ * Return: 0 on success, standard Linux error codes on error/failure.
+ */
+int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
+ void *tx_buf, int tx_len, dma_addr_t *tx_dma);
+
+/**
+ * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
+ * @wrapper_dev: QUP Wrapper Device to which the TX buffer is mapped.
+ * @base: Base address of the SE register block.
+ * @rx_buf: Pointer to the RX buffer.
+ * @rx_len: Length of the RX buffer.
+ * @rx_dma: Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the buffers for DMA RX.
+ *
+ * Return: 0 on success, standard Linux error codes on error/failure.
+ */
+int geni_se_rx_dma_prep(struct device *wrapper_dev, void __iomem *base,
+ void *rx_buf, int rx_len, dma_addr_t *rx_dma);
+
+/**
+ * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
+ * @wrapper_dev: QUP Wrapper Device to which the TX buffer is mapped.
+ * @tx_dma: DMA address of the TX buffer.
+ * @tx_len: Length of the TX buffer.
+ *
+ * This function is used to unprepare the DMA buffers after DMA TX.
+ */
+void geni_se_tx_dma_unprep(struct device *wrapper_dev,
+ dma_addr_t tx_dma, int tx_len);
+
+/**
+ * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
+ * @wrapper_dev: QUP Wrapper Device to which the TX buffer is mapped.
+ * @rx_dma: DMA address of the RX buffer.
+ * @rx_len: Length of the RX buffer.
+ *
+ * This function is used to unprepare the DMA buffers after DMA RX.
+ */
+void geni_se_rx_dma_unprep(struct device *wrapper_dev,
+ dma_addr_t rx_dma, int rx_len);
+
+/**
+ * geni_se_map_buf() - Map a single buffer into QUP wrapper device
+ * @wrapper_dev: Pointer to the corresponding QUP wrapper core.
+ * @iova: Pointer in which the mapped virtual address is stored.
+ * @buf: Address of the buffer that needs to be mapped.
+ * @size: Size of the buffer.
+ * @dir: Direction of the DMA transfer.
+ *
+ * This function is used to map an already allocated buffer into the
+ * QUP device space.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_map_buf(struct device *wrapper_dev, dma_addr_t *iova,
+ void *buf, size_t size, enum dma_data_direction dir);
+
+/**
+ * geni_se_unmap_buf() - Unmap a single buffer from QUP wrapper device
+ * @wrapper_dev: Pointer to the corresponding QUP wrapper core.
+ * @iova: Pointer in which the mapped virtual address is stored.
+ * @size: Size of the buffer.
+ * @dir: Direction of the DMA transfer.
+ *
+ * This function is used to unmap an already mapped buffer from the
+ * QUP device space.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_unmap_buf(struct device *wrapper_dev, dma_addr_t *iova,
+ size_t size, enum dma_data_direction dir);
+#else
+static inline unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
+{
+ return 0;
+}
+
+static inline void geni_write_reg_nolog(unsigned int value,
+ void __iomem *base, int offset)
+{
+}
+
+static inline unsigned int geni_read_reg(void __iomem *base, int offset)
+{
+ return 0;
+}
+
+static inline void geni_write_reg(unsigned int value, void __iomem *base,
+ int offset)
+{
+}
+
+static inline int geni_get_qup_hw_version(struct device *wrapper_dev,
+ unsigned int *major,
+ unsigned int *minor,
+ unsigned int *step)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_get_proto(void __iomem *base)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_init(void __iomem *base,
+ unsigned int rx_wm, unsigned int rx_rfr)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_select_mode(void __iomem *base, int mode)
+{
+ return -ENXIO;
+}
+
+static inline void geni_se_setup_m_cmd(void __iomem *base, u32 cmd,
+ u32 params)
+{
+}
+
+static inline void geni_se_setup_s_cmd(void __iomem *base, u32 cmd,
+ u32 params)
+{
+}
+
+static inline void geni_se_cancel_m_cmd(void __iomem *base)
+{
+}
+
+static inline void geni_se_cancel_s_cmd(void __iomem *base)
+{
+}
+
+static inline void geni_se_abort_m_cmd(void __iomem *base)
+{
+}
+
+static inline void geni_se_abort_s_cmd(void __iomem *base)
+{
+}
+
+static inline int geni_se_get_tx_fifo_depth(void __iomem *base)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_get_tx_fifo_width(void __iomem *base)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_get_rx_fifo_depth(void __iomem *base)
+{
+ return -ENXIO;
+}
+
+static inline void geni_se_get_packing_config(int bpw, int pack_words,
+ bool msb_to_lsb,
+ unsigned long *cfg0,
+ unsigned long *cfg1)
+{
+}
+
+static inline void geni_se_config_packing(void __iomem *base, int bpw,
+ int pack_words, bool msb_to_lsb)
+{
+}
+
+static inline int geni_se_resources_on(struct geni_se_rsc *rsc)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_resources_off(struct geni_se_rsc *rsc)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_clk_tbl_get(struct geni_se_rsc *rsc,
+ unsigned long **tbl)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_clk_freq_match(struct geni_se_rsc *rsc,
+ unsigned long req_freq,
+ unsigned int *index,
+ unsigned long *res_freq, bool exact)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_tx_dma_prep(struct device *wrapper_dev,
+ void __iomem *base, void *tx_buf,
+ int tx_len, dma_addr_t *tx_dma)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_rx_dma_prep(struct device *wrapper_dev,
+ void __iomem *base, void *rx_buf,
+ int rx_len, dma_addr_t *rx_dma)
+{
+ return -ENXIO;
+}
+
+static inline void geni_se_tx_dma_unprep(struct device *wrapper_dev,
+ dma_addr_t tx_dma, int tx_len)
+{
+}
+
+static inline void geni_se_rx_dma_unprep(struct device *wrapper_dev,
+ dma_addr_t rx_dma, int rx_len)
+{
+}
+
+static inline int geni_se_map_buf(struct device *wrapper_dev,
+ dma_addr_t *iova, void *buf, size_t size,
+ enum dma_data_direction dir)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_unmap_buf(struct device *wrapper_dev,
+ dma_addr_t *iova, size_t size,
+ enum dma_data_direction dir)
+{
+ return -ENXIO;
+
+}
+
+#endif
+#endif
--
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH RFC 2/7] soc: qcom: Add device tree binding for GENI SE
From: Karthikeyan Ramasubramanian @ 2017-12-27 16:27 UTC (permalink / raw)
To: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA
Cc: Karthikeyan Ramasubramanian, linux-doc-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
andy.gross-QSEj5FYQhm4dnm+yROfE0A,
david.brown-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
corbet-T1hC0tSOHrs, wsa-z923LK4zBo2bacvFa/9K2g,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, jslaby-IBi9RG/b67k
In-Reply-To: <1514392046-30602-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Add device tree binding support for the QCOM GENI SE driver.
Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
.../devicetree/bindings/soc/qcom/qcom,geni-se.txt | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
new file mode 100644
index 0000000..5108b62
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
@@ -0,0 +1,15 @@
+Qualcomm Technologies, Inc. GENI Serial Engine Driver
+
+GENI Serial Engine Driver manages the GENI firmware based Qualcomm Universal
+Peripheral (QUP) Wrapper. GENI SE Driver also manages the common aspects of
+individual Serial Engines that composes the QUP Wrapper.
+
+Required properties:
+- compatible: Must be "qcom,geni-se-qup".
+- reg: Must contain QUP register address and length.
+
+Example:
+ qup_0: qcom,geni_se_qup_0@8c0000 {
+ compatible = "qcom,geni-se-qup";
+ reg = <0x8c0000 0x6000>;
+ }
--
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH RFC 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary
From: Karthikeyan Ramasubramanian @ 2017-12-27 16:27 UTC (permalink / raw)
To: linux-arm-msm, linux-i2c, linux-serial
Cc: Karthikeyan Ramasubramanian, linux-doc, devicetree, andy.gross,
david.brown, robh+dt, mark.rutland, corbet, wsa, gregkh, jslaby
In-Reply-To: <1514392046-30602-1-git-send-email-kramasub@codeaurora.org>
Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP)
Wrapper is a programmable module that is composed of multiple Serial
Engines (SE) and can support various Serial Interfaces like UART, SPI,
I2C, I3C, etc. This document provides a high level overview of the GENI
based QUP Wrapper.
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
---
Documentation/qcom-geni-se.txt | 56 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 Documentation/qcom-geni-se.txt
diff --git a/Documentation/qcom-geni-se.txt b/Documentation/qcom-geni-se.txt
new file mode 100644
index 0000000..dc517ef
--- /dev/null
+++ b/Documentation/qcom-geni-se.txt
@@ -0,0 +1,56 @@
+Introduction
+============
+
+Generic Interface (GENI) Serial Engine (SE) Wrapper driver is introduced
+to manage GENI firmware based Qualcomm Universal Peripheral (QUP) Wrapper
+controller. QUP Wrapper is designed to support various serial bus protocols
+like UART, SPI, I2C, I3C, etc.
+
+Hardware description
+====================
+
+GENI based QUP is a highly-flexible and programmable module for supporting
+a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. A single
+QUP module can provide upto 8 Serial Interfaces, using its internal
+Serial Engines. The actual configuration is determined by the target
+platform configuration. The protocol supported by each interface is
+determined by the firmware loaded to the Serial Engine. Each SE consists
+of a DMA Engine and GENI sub modules which enable Serial Engines to
+support FIFO and DMA modes of operation.
+
+::
+
+ +-----------------------------------------+
+ |QUP Wrapper |
+ | +----------------------------+ |
+ --QUP & SE Clocks--> | Serial Engine N | +-IO------>
+ | | ... | | Interface
+ <---Clock Perf.----+ +----+-----------------------+ | |
+ State Interface | | Serial Engine 1 | | |
+ | | | | |
+ | | | | |
+ <--------AHB-------> | | | |
+ | | +----+ |
+ | | | |
+ | | | |
+ <------SE IRQ------+ +----------------------------+ |
+ | |
+ +-----------------------------------------+
+
+ Figure 1: GENI based QUP Wrapper
+
+Software description
+====================
+
+GENI SE Wrapper driver is structured into 2 parts:
+
+geni_se_device represents QUP Wrapper controller. This part of the driver
+manages QUP Wrapper information such as hardware version, clock
+performance table that is common to all the internal Serial Engines.
+
+geni_se_rsc represents Serial Engine. This part of the driver manages
+Serial Engine information such as clocks, pinctrl states, containing QUP
+Wrapper. This part of driver also supports operations(eg. initialize the
+concerned Serial Engine, select between FIFO and DMA mode of operation etc.)
+that are common to all the Serial Engines and are independent of Serial
+Interfaces.
--
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH RFC 0/7] Introduce GENI SE Controller Driver
From: Karthikeyan Ramasubramanian @ 2017-12-27 16:27 UTC (permalink / raw)
To: linux-arm-msm, linux-i2c, linux-serial
Cc: Karthikeyan Ramasubramanian, linux-doc, devicetree, andy.gross,
david.brown, robh+dt, mark.rutland, corbet, wsa, gregkh, jslaby
Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP)
Wrapper is a next generation programmable module for supporting a wide
range of serial interfaces like UART, SPI, I2C, I3C, etc. A single QUP
module can provide upto 8 Serial Interfaces using its internal Serial
Engines (SE). The protocol supported by each interface is determined by
the firmware loaded to the Serial Engine.
This patch series introduces GENI SE Driver to manage the GENI based QUP
Wrapper and the common aspects of all SEs inside the QUP Wrapper. This
patch series also introduces the UART and I2C Controller drivers to
drive the SEs that are programmed with the respective protocols.
Karthikeyan Ramasubramanian (7):
qcom-geni-se: Add QCOM GENI SE Driver summary
soc: qcom: Add device tree binding for GENI SE
soc: qcom: Add GENI based QUP Wrapper driver
i2c: Add device tree bindings for GENI I2C Controller
i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C
controller
serial: Add device tree bindings for GENI based UART Controller
tty: serial: msm_geni_serial: Add serial driver support for GENI based
QUP
.../devicetree/bindings/i2c/i2c-qcom-geni.txt | 39 +
.../devicetree/bindings/serial/qcom,geni-uart.txt | 31 +
.../devicetree/bindings/soc/qcom/qcom,geni-se.txt | 15 +
Documentation/qcom-geni-se.txt | 56 +
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-qcom-geni.c | 685 +++++++++
drivers/soc/qcom/Kconfig | 8 +
drivers/soc/qcom/Makefile | 1 +
drivers/soc/qcom/qcom-geni-se.c | 973 +++++++++++++
drivers/tty/serial/Kconfig | 10 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/qcom_geni_serial.c | 1447 ++++++++++++++++++++
include/linux/qcom-geni-se.h | 811 +++++++++++
14 files changed, 4088 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
create mode 100644 Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
create mode 100644 Documentation/qcom-geni-se.txt
create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
create mode 100644 drivers/soc/qcom/qcom-geni-se.c
create mode 100644 drivers/tty/serial/qcom_geni_serial.c
create mode 100644 include/linux/qcom-geni-se.h
--
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* Re: [PATCH net-next] phylib: rename reset-(post-)delay-us to reset-(de)assert-us
From: David Miller @ 2017-12-27 16:07 UTC (permalink / raw)
To: dev-M/VWbR8SM2SsTnJN9+BGXg
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
andrew-g2DYL2Zd6BY, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
richard.leitner-WcANXNA0UjBBDgjK7y7TUQ,
geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ, lukma-ynQEQJNshbs,
sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8,
martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg,
david.wu-TNX95d0MmH7DzftRWevZcw, baruch-NswTu9S1W3P6gbPvEgmw2w,
devicetree-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171222100809.18826-1-dev-M/VWbR8SM2SsTnJN9+BGXg@public.gmane.org>
From: Richard Leitner <dev-M/VWbR8SM2SsTnJN9+BGXg@public.gmane.org>
Date: Fri, 22 Dec 2017 11:08:09 +0100
> From: Richard Leitner <richard.leitner-WcANXNA0UjBBDgjK7y7TUQ@public.gmane.org>
>
> As suggested by Rob Herring [1] rename the previously introduced
> reset-{,post-}delay-us bindings to the clearer reset-{,de}assert-us
>
> [1] https://patchwork.kernel.org/patch/10104905/
>
> Signed-off-by: Richard Leitner <richard.leitner-WcANXNA0UjBBDgjK7y7TUQ@public.gmane.org>
Applied, thanks.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC PATCH v12 4/5] PCI / PM: Add support for the PCIe WAKE# signal for OF
From: Rob Herring @ 2017-12-27 15:30 UTC (permalink / raw)
To: JeffyChen
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Bjorn Helgaas, linux-pm-u79uwXL29TY76Z2rM5mHXA, Tony Lindgren,
Shawn Lin, Brian Norris, Rafael J. Wysocki, Doug Anderson,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
linux-pci-u79uwXL29TY76Z2rM5mHXA, Frank Rowand, Andy Shevchenko
In-Reply-To: <5A42F847.5060305-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
On Tue, Dec 26, 2017 at 7:32 PM, JeffyChen <jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
> Hi Rob,
>
> Thanks for your reply.
>
> On 12/27/2017 07:56 AM, Rob Herring wrote:
>>>
>>> >
>>> > drivers/of/of_pci_irq.c | 49 +++++++++++++++++++++++++++++++
>>
>> Please move this to drivers/pci/of.c (or perhaps create pci/of_irq.c).
>>
>>> > drivers/pci/Makefile | 1 +
>>> > drivers/pci/pci-driver.c | 10 +++++++
>>> > drivers/pci/pci-of.c | 75
>>> > ++++++++++++++++++++++++++++++++++++++++++++++++
>>
>> We already have drivers/pci/of.c. It's not clear what the difference is
>> from the filenames. Either merge with of.c or perhaps of-pm.c.
>
>
> this new file does something similar to the pci-acpi.c and pci-mid.c..
pci-acpi.c has similar things to pci/of.c. The naming is just not consistent.
Also, I plan to move the rest of drivers/of/of_pci* to drivers/pci.
> and i am agree the naming is not clear, maybe we can rename both of those
> files to something like pci-pm-***.c?
At least pci-acpi.c is more than just PM functions, so that doesn't
make sense. Given that all the ACPI related functions are in 1 file,
we should do the same for DT.
>
> Hi Rafael, do you think this would make sense?
>
>
[...]
>>> >+static int __init of_pci_init(void)
>>> >+{
>>> >+ if (!acpi_disabled)
>>> >+ return 0;
>>> >+
>>> >+ pci_set_platform_pm(&of_pci_platform_pm);
>>
>> I guess no DT based system will override this?
>
>
> i think the !acpi_disabled means acpi been disabled or CONFIG_ACPI is
> undefined.
>
> and pci-acpi.c would only work when we have CONFIG_ACPI.
>
> but i have no idea about pci-mid.c or would it possible to have more
> platform pm ops in the future...maybe we should add some dependency in the
> Kconfig?
It's probably fine given there are only 2 other implementations so far.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox