* [PATCH v11 01/15] usb: doc: phy-mxs: Add more compatible strings
From: Peter Chen @ 2014-02-24 2:20 UTC (permalink / raw)
To: balbi, shawn.guo, robh+dt, grant.likely, pawel.moll, mark.rutland
Cc: linux-usb, linux-arm-kernel, festevam, marex, kernel, m.grzeschik,
frank.li, peter.chen, gregkh, devicetree, linux-doc
In-Reply-To: <1393208467-6355-1-git-send-email-peter.chen@freescale.com>
Add "fsl,imx6q-usbphy" for imx6dq and imx6dl, add
"fsl,imx6sl-usbphy" for imx6sl, and "fsl,imx23-usbphy"
is still a fallback for other strings.
Signed-off-by: Peter Chen <peter.chen@freescale.com>
---
Documentation/devicetree/bindings/usb/mxs-phy.txt | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/Documentation/devicetree/bindings/usb/mxs-phy.txt b/Documentation/devicetree/bindings/usb/mxs-phy.txt
index 5835b27..ea5134a 100644
--- a/Documentation/devicetree/bindings/usb/mxs-phy.txt
+++ b/Documentation/devicetree/bindings/usb/mxs-phy.txt
@@ -1,7 +1,11 @@
* Freescale MXS USB Phy Device
Required properties:
-- compatible: Should be "fsl,imx23-usbphy"
+- compatible: should contain:
+ * "fsl,imx23-usbphy" for imx23 and imx28
+ * "fsl,imx6q-usbphy" for imx6dq and imx6dl
+ * "fsl,imx6sl-usbphy" for imx6sl
+ "fsl,imx23-usbphy" is still a fallback for other strings
- reg: Should contain registers location and length
- interrupts: Should contain phy interrupt
--
1.7.8
^ permalink raw reply related
* [PATCH v11 00/15] Add power management support for mxs phy
From: Peter Chen @ 2014-02-24 2:20 UTC (permalink / raw)
To: balbi, shawn.guo, robh+dt, grant.likely, pawel.moll, mark.rutland
Cc: linux-usb, linux-arm-kernel, festevam, marex, kernel, m.grzeschik,
frank.li, peter.chen, gregkh, devicetree, linux-doc
The serial adds power management support for MXS PHY, it includes:
- Add one PHY API .set_wakeup, and related API implementation at mxs phy driver
- misc changes and bug fixes for mxs phy to support low power mode and wakeup.
It is based on the lastest Greg's usb-next, 3.14-rc3.
Changes for v11:
- Change the descrition of adding compatible string, and
add mention that "fsl,imx23-usbphy" is still a fallback
for other strings. [1/15]
Changes for v10:
- Nothing related with function change.
- It adds dt maintainers to To list, and cc devicetree@vger.kernel.org,
wait dt-maintainer's ack for 1/15, 4/15.
- Add last three dts patches for reference which have already
at Shawn's tree.
Changes for v9:
- Fix the build warning if CONFIG_SUSPEND is not set
Changes for v8:
- Shawn has already applied two dts patches, so delete them at
v8 version, http://marc.info/?l=linux-arm-kernel&m=138785188404939&w=2
- Using of_get_property to avoid imx6 fail at probe when using old DTB [5/12]
- Do not consider failure if alias id is not existed, it can avoid fail
at probe when using old DTB [8/12]
- We don't need more changes for [8/12] change due to we use
"mxs_phy->port_id ==" as condition
Changes for v7:
- Fixed indentation problem at binding doc [1/14]
- s/ganranteed/guaranteed, and add explanation for "auto setting" [3/14]
- add static before SIMPLE_DEV_PM_OPS [13/14]
- add one patch to change usb device description [7/14]
- Delete the .nofity_suspend and .notify_resume due to Felipe
does not agree to add them as PHY API, will use other ways to implement
them in future.
Changes for v6:
- Add description for IC bug fixes logic. [9/15]
- Move is_imx6q_phy and is_imx6sl_phy from [9/15] to [14/15]
- %s/mxs_phy_clock_switch/mxs_phy_clock_switch_delay to reflect
the function meaning more precise [15/15]
Changes for v5:
Add Marc and Michael Grzeschik's commnets
- typo error at [2/15]
- sqhash patches which introducing mxs_phy_disconnect_line and
fixed but at this function. [13/15]
- Introducing flag MXS_PHY_NEED_IP_FIX who stands for the SoCs
who have IC fixes. [2/15, 8/15]
- Delete one patch for low speed connection problem at every rare
situations due to the root cause has still not found.
Peter Chen (15):
usb: doc: phy-mxs: Add more compatible strings
usb: phy-mxs: Add platform judgement code
usb: phy-mxs: Add auto clock and power setting
usb: doc: phy-mxs: update binding for adding anatop phandle
usb: phy-mxs: Add anatop regmap
usb: phy-mxs: change description of usb device speed
usb: phy-mxs: Enable IC fixes for related SoCs
usb: phy-mxs: add controller id
usb: phy: Add set_wakeup API
usb: phy-mxs: Add implementation of set_wakeup
usb: phy-mxs: Add system suspend/resume API
usb: phy-mxs: Add sync time after controller clear phcd
ARM: dts: mxs: add mxs phy controller id
ARM: dts: imx6: add anatop phandle for usbphy
ARM: dts: imx6: add mxs phy controller id
Documentation/devicetree/bindings/usb/mxs-phy.txt | 8 +-
arch/arm/boot/dts/imx23.dtsi | 1 +
arch/arm/boot/dts/imx28.dtsi | 2 +
arch/arm/boot/dts/imx6qdl.dtsi | 4 +
arch/arm/boot/dts/imx6sl.dtsi | 4 +
drivers/usb/phy/phy-mxs-usb.c | 310 ++++++++++++++++++++-
include/linux/usb/phy.h | 16 +
7 files changed, 330 insertions(+), 15 deletions(-)
--
1.7.8
^ permalink raw reply
* RE: [PATCH 1/4] ASoC: simple-card: Fix device node locks
From: Li.Xiubo-KZfg59tc24xl57MIdRCFDg @ 2014-02-24 2:17 UTC (permalink / raw)
To: Jean-Francois Moine, Mark Brown
Cc: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org,
Kuninori Morimoto,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <4dca81f45b67a4dcb21271e57409ba114c3b59cb.1392995566.git.moinejf-GANU6spQydw@public.gmane.org>
> @@ -169,22 +164,26 @@ static int asoc_simple_card_parse_of(struct device_node
> *node,
> /* CPU sub-node */
> ret = -EINVAL;
> np = of_get_child_by_name(node, "simple-audio-card,cpu");
> - if (np)
> + if (np) {
> ret = asoc_simple_card_sub_parse_of(np, priv->daifmt,
> &priv->cpu_dai,
> &dai_link->cpu_of_node,
> &dai_link->cpu_dai_name);
> + of_node_put(np);
Does the of_node_put(np) is really needed here ?
> + }
> if (ret < 0)
> return ret;
>
> /* CODEC sub-node */
> ret = -EINVAL;
> np = of_get_child_by_name(node, "simple-audio-card,codec");
> - if (np)
> + if (np) {
> ret = asoc_simple_card_sub_parse_of(np, priv->daifmt,
> &priv->codec_dai,
> &dai_link->codec_of_node,
> &dai_link->codec_dai_name);
> + of_node_put(np);
The same here...
> + }
> if (ret < 0)
> return ret;
>
Thanks,
--
Best Regards,
Xiubo
--
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/3] dma: Support multiple interleaved frames with non-contiguous memory
From: Jassi Brar @ 2014-02-24 2:09 UTC (permalink / raw)
To: Srikanth Thokala
Cc: Williams, Dan J, Koul, Vinod, Michal Simek, Grant Likely,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Levente Kurusa,
Lars-Peter Clausen, lkml, dmaengine-u79uwXL29TY76Z2rM5mHXA,
Andy Shevchenko,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
In-Reply-To: <CA+mB=1KqP=KgNZ_hYGbhin5TqokA+nCRPAA4Qa2R_2OWwv4yVw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 21 February 2014 23:37, Srikanth Thokala <sthokal-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org> wrote:
> On Thu, Feb 20, 2014 at 3:23 PM, Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> wrote:
>> On 20 February 2014 14:54, Srikanth Thokala <sthokal-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org> wrote:
>>> On Wed, Feb 19, 2014 at 12:33 AM, Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>>> wrote:
>>>> On 18 February 2014 23:16, Srikanth Thokala <sthokal-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org> wrote:
>>>>> On Tue, Feb 18, 2014 at 10:20 PM, Jassi Brar
>>>>> <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>>>>>> On 18 February 2014 16:58, Srikanth Thokala <sthokal-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
>>>>>> wrote:
>>>>>>> On Mon, Feb 17, 2014 at 3:27 PM, Jassi Brar
>>>>>>> <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>>>>>>>> On 15 February 2014 17:30, Srikanth Thokala <sthokal-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
>>>>>>>> wrote:
>>>>>>>>> The current implementation of interleaved DMA API support multiple
>>>>>>>>> frames only when the memory is contiguous by incrementing
>>>>>>>>> src_start/
>>>>>>>>> dst_start members of interleaved template.
>>>>>>>>>
>>>>>>>>> But, when the memory is non-contiguous it will restrict slave
>>>>>>>>> device
>>>>>>>>> to not submit multiple frames in a batch. This patch handles this
>>>>>>>>> issue by allowing the slave device to send array of interleaved dma
>>>>>>>>> templates each having a different memory location.
>>>>>>>>>
>>>>>>>> How fragmented could be memory in your case? Is it inefficient to
>>>>>>>> submit separate transfers for each segment/frame?
>>>>>>>> It will help if you could give a typical example (chunk size and gap
>>>>>>>> in bytes) of what you worry about.
>>>>>>>
>>>>>>> With scatter-gather engine feature in the hardware, submitting
>>>>>>> separate
>>>>>>> transfers for each frame look inefficient. As an example, our DMA
>>>>>>> engine
>>>>>>> supports up to 16 video frames, with each frame (a typical video
>>>>>>> frame
>>>>>>> size) being contiguous in memory but frames are scattered into
>>>>>>> different
>>>>>>> locations. We could not definitely submit frame by frame as it would
>>>>>>> be
>>>>>>> software overhead (HW interrupting for each frame) resulting in video
>>>>>>> lags.
>>>>>>>
>>>>>> IIUIC, it is 30fps and one dma interrupt per frame ... it doesn't seem
>>>>>> inefficient at all. Even poor-latency audio would generate a higher
>>>>>> interrupt-rate. So the "inefficiency concern" doesn't seem valid to
>>>>>> me.
>>>>>>
>>>>>> Not to mean we shouldn't strive to reduce the interrupt-rate further.
>>>>>> Another option is to emulate the ring-buffer scheme of ALSA.... which
>>>>>> should be possible since for a session of video playback the frame
>>>>>> buffers' locations wouldn't change.
>>>>>>
>>>>>> Yet another option is to use the full potential of the
>>>>>> interleaved-xfer api as such. It seems you confuse a 'video frame'
>>>>>> with the interleaved-xfer api's 'frame'. They are different.
>>>>>>
>>>>>> Assuming your one video frame is F bytes long and Gk is the gap in
>>>>>> bytes between end of frame [k] and start of frame [k+1] and Gi != Gj
>>>>>> for i!=j
>>>>>> In the context of interleaved-xfer api, you have just 1 Frame of 16
>>>>>> chunks. Each chunk is Fbytes and the inter-chunk-gap(ICG) is Gk where
>>>>>> 0<=k<15
>>>>>> So for your use-case .....
>>>>>> dma_interleaved_template.numf = 1 /* just 1 frame */
>>>>>> dma_interleaved_template.frame_size = 16 /* containing 16 chunks */
>>>>>> ...... //other parameters
>>>>>>
>>>>>> You have 3 options to choose from and all should work just as fine.
>>>>>> Otherwise please state your problem in real numbers (video-frames'
>>>>>> size, count & gap in bytes).
>>>>>
>>>>> Initially I interpreted interleaved template the same. But, Lars
>>>>> corrected me
>>>>> in the subsequent discussion and let me put it here briefly,
>>>>>
>>>>> In the interleaved template, each frame represents a line of size
>>>>> denoted by
>>>>> chunk.size and the stride by icg. 'numf' represent number of frames
>>>>> i.e.
>>>>> number of lines.
>>>>>
>>>>> In video frame context,
>>>>> chunk.size -> hsize
>>>>> chunk.icg -> stride
>>>>> numf -> vsize
>>>>> and frame_size is always 1 as it will have only one chunk in a line.
>>>>>
>>>> But you said in your last post
>>>> "with each frame (a typical video frame size) being contiguous in
>>>> memory"
>>>> ... which is not true from what you write above. Anyways, my first 2
>>>> suggestions still hold.
>>>
>>> Yes, each video frame is contiguous and they can be scattered.
>>>
>> I assume by contiguous frame you mean as in framebuffer? Which is an
>> array of bytes.
>> If yes, then you should do as I suggest first, frame_size=16 and numf=1.
>
> I think am confusing you. I would like to explain with an example. Lets
> say
> each video frame is 4k size starting at address 0x10004000 (-0x10005000) and
> other frame at 0x20002000 (-0x20003000), and so on.
>
As I said plz dont confuse video frame with DMA frame.... in video
frame the stride is constant(zero or not) whereas in DMA context the
stride must be zero for the frame to be called contiguous.
> So, the frames are
> scattered in memory and as the template doesnt allow multiple src_start/
> dst_start we could not use single template to fill the HW descriptors (of
> frames). So, I feel your suggestion might not work if the frames are
> scattered.
> Also, how could we get 'vsize' value in your approach?
>
The client driver(video driver) should know the frame parameters.
Practically you'll have to populate 16 transfer templates (frame
attributes and locations won't change for a session) and submit to be
transferred _cyclically_.
> More importantly,
> we are overriding the semantics of interleaved template members.
>
Not at all. Interleaved-dma isn't meant for only constant stride/icg
transfers. Rather it's for identical frames with random strides.
>
>>
>> If no, then it seems you are already doing the right thing.... the
>> ring-buffer scheme. Please share some stats how the current api is
>> causing you overhead because that is a very common case (many
>> controllers support LLI) and you have 467ms (@30fps with 16-frames
>> ring-buffer) to queue in before you see any frame drop.
>
> As I mentioned earlier in the thread, our hardware has a SG engine where by
> we could send multiple frames in a batch. Using the original implementation
> of interleaved API, we have three options to transfer.
>
> One is to send frame by frame to the hardware. We get a async_tx desc for
> each frame and then we submit it to hardware triggering it to transfer this
> BD.
> We queue the next descriptor on the pending queue and whenever there is
> a completion interrupt we submit the next BD on this queue to hardware. In
> this implementation we are not efficiently using the SG engine in the
> hardware
> as we transferring frame by frame even HW allows us to transfer multiple
> frames.
>
Sending one frame at a time will likely cause jitters. That shouldn't be done.
> The second option is to queue all the BDs until the maximum frames that HW
> is
> capable to transfer in a batch and then submit to SG engine in the HW. With
> this approach I feel there will be additional software overhead to track the
> number
> of maximum transfers and few additional cycles to release the cookie of each
> desc.
> Here, each desc represents a frame.
>
APIs are written for the gcd of h/w. We should optimize only for
majority and here isn't even anything gained after changing the api.
What you want to 'optimize' has been there since ever. Nobody
considers that overhead. BTW you don't even want to spend a few 'extra
cycles' but what about every other platform that doesn't support this
and will have to scan for such transfer requests?
> The last option is the current implementation of the driver along with this
> change in
> API. It will allow us to send array of interleaved templates wherein we
> could allocate
> a single async desc which will handle multiple frames (or segments) and just
> submit
> this desc to HW. Then we program the current to first frame, tail to last
> frame and
> HW will complete this transfer. Here, each desc represents multiple frames.
>
> My point here is the driver should use hardware resources efficiently and I
> feel the
> driver will be inefficient if we dont use them.
>
I am afraid you are getting carried away by the 'awesomeness' of your
hardware. RingBuffers/Cyclic transfers are meant for cases just like
yours.
Consider the following ... if you queue 16 frames and don't care to
track before all are transmitted, you'll have very high latency. Video
seeks will take unacceptably long and give the impression of a slow
system. Whereas if you get callbacks for each frame rendered, you
could updates frames from the next one thereby having very quick
response time.
Anyways there are already ways to achieve what you want (however a bad
idea that might be). So I am not convinced the change to api is any
useful (your hardware won't run any more efficiently with the change).
Regards,
Jassi
--
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 2/7] IBM Akebono: Add support for a new PHY interface to the IBM emac driver
From: Alistair Popple @ 2014-02-24 2:09 UTC (permalink / raw)
To: Mark Rutland
Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org,
David S. Miller
In-Reply-To: <20140221111823.GA8783-NuALmloUBlrZROr8t4l/smS4ubULX0JqMm0uRHvK7Nw@public.gmane.org>
On Fri, 21 Feb 2014 11:18:23 Mark Rutland wrote:
> On Fri, Feb 21, 2014 at 06:31:28AM +0000, Alistair Popple wrote:
[...]
>
> > + /* Check for RGMII flags */
> > + if (of_get_property(ofdev->dev.of_node, "has-mdio", NULL))
> > + dev->flags |= EMAC_RGMII_FLAG_HAS_MDIO;
>
> You can use of_property_read_bool here.
Thanks. I will update it.
Regards,
Alistair
>
> Cheers,
> Mark.
--
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] bus: imx-weim: support CS GPR configuration
From: Shawn Guo @ 2014-02-24 2:04 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Philippe De Muyter, Alexander Shiyan, Huang Shijie,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Rob Herring, Mark Rutland,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1392705666-10995-1-git-send-email-shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
On Tue, Feb 18, 2014 at 02:41:06PM +0800, Shawn Guo wrote:
> For imx50-weim and imx6q-weim type of devices, there might a WEIM CS
> space configuration register in General Purpose Register controller,
> e.g. IOMUXC_GPR1 on i.MX6Q.
>
> Depending on which configuration of the following 4 is chosen for given
> system, IOMUXC_GPR1[11:0] should be set up as 05, 033, 0113 or 01111
> correspondingly.
>
> CS0(128M) CS1(0M) CS2(0M) CS3(0M)
> CS0(64M) CS1(64M) CS2(0M) CS3(0M)
> CS0(64M) CS1(32M) CS2(32M) CS3(0M)
> CS0(32M) CS1(32M) CS2(32M) CS3(32M)
>
> The patch creates a function for such type of devices, which scans
> 'ranges' property of WEIM node and build the GPR value incrementally.
> Thus the WEIM CS GPR can be set up automatically at boot time.
>
> Signed-off-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Philippe,
Any further comments? Or can I have your Reviewed-by or Tested-by tag?
Shawn
> ---
> Changes since v4:
> - Fix a typo in comment
> - Add fsl,weim-cs-gpr in the binding example
> - Check return of imx_weim_gpr_setup()
>
> Documentation/devicetree/bindings/bus/imx-weim.txt | 28 +++++++++-
> drivers/bus/imx-weim.c | 58 ++++++++++++++++++++
> 2 files changed, 85 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/bus/imx-weim.txt b/Documentation/devicetree/bindings/bus/imx-weim.txt
> index 0fd76c4..6630d84 100644
> --- a/Documentation/devicetree/bindings/bus/imx-weim.txt
> +++ b/Documentation/devicetree/bindings/bus/imx-weim.txt
> @@ -8,7 +8,12 @@ The actual devices are instantiated from the child nodes of a WEIM node.
>
> Required properties:
>
> - - compatible: Should be set to "fsl,<soc>-weim"
> + - compatible: Should contain one of the following:
> + "fsl,imx1-weim"
> + "fsl,imx27-weim"
> + "fsl,imx51-weim"
> + "fsl,imx50-weim"
> + "fsl,imx6q-weim"
> - reg: A resource specifier for the register space
> (see the example below)
> - clocks: the clock, see the example below.
> @@ -19,6 +24,26 @@ Required properties:
>
> <cs-number> 0 <physical address of mapping> <size>
>
> +Optional properties:
> +
> + - fsl,weim-cs-gpr: For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
> + devices, it should be the phandle to the system General
> + Purpose Register controller that contains WEIM CS GPR
> + register, e.g. IOMUXC_GPR1 on i.MX6Q. IOMUXC_GPR1[11:0]
> + should be set up as one of the following 4 possible
> + values depending on the CS space configuration.
> +
> + IOMUXC_GPR1[11:0] CS0 CS1 CS2 CS3
> + ---------------------------------------------
> + 05 128M 0M 0M 0M
> + 033 64M 64M 0M 0M
> + 0113 64M 32M 32M 0M
> + 01111 32M 32M 32M 32M
> +
> + In case that the property is absent, the reset value or
> + what bootloader sets up in IOMUXC_GPR1[11:0] will be
> + used.
> +
> Timing property for child nodes. It is mandatory, not optional.
>
> - fsl,weim-cs-timing: The timing array, contains timing values for the
> @@ -43,6 +68,7 @@ Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM:
> #address-cells = <2>;
> #size-cells = <1>;
> ranges = <0 0 0x08000000 0x08000000>;
> + fsl,weim-cs-gpr = <&gpr>;
>
> nor@0,0 {
> compatible = "cfi-flash";
> diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
> index 3ef58c8..f8ee13c 100644
> --- a/drivers/bus/imx-weim.c
> +++ b/drivers/bus/imx-weim.c
> @@ -11,6 +11,9 @@
> #include <linux/clk.h>
> #include <linux/io.h>
> #include <linux/of_device.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
> +#include <linux/regmap.h>
>
> struct imx_weim_devtype {
> unsigned int cs_count;
> @@ -56,6 +59,55 @@ static const struct of_device_id weim_id_table[] = {
> };
> MODULE_DEVICE_TABLE(of, weim_id_table);
>
> +static int __init imx_weim_gpr_setup(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct property *prop;
> + const __be32 *p;
> + struct regmap *gpr;
> + u32 gprvals[4] = {
> + 05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */
> + 033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */
> + 0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */
> + 01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */
> + };
> + u32 gprval = 0;
> + u32 val;
> + int cs = 0;
> + int i = 0;
> +
> + gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
> + if (IS_ERR(gpr)) {
> + dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
> + return 0;
> + }
> +
> + of_property_for_each_u32(np, "ranges", prop, p, val) {
> + if (i % 4 == 0) {
> + cs = val;
> + } else if (i % 4 == 3 && val) {
> + val = (val / SZ_32M) | 1;
> + gprval |= val << cs * 3;
> + }
> + i++;
> + }
> +
> + if (i == 0 || i % 4)
> + goto err;
> +
> + for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
> + if (gprval == gprvals[i]) {
> + /* Found it. Set up IOMUXC_GPR1[11:0] with it. */
> + regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
> + return 0;
> + }
> + }
> +
> +err:
> + dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
> + return -EINVAL;
> +}
> +
> /* Parse and set the timing for this device. */
> static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
> const struct imx_weim_devtype *devtype)
> @@ -92,6 +144,12 @@ static int __init weim_parse_dt(struct platform_device *pdev,
> struct device_node *child;
> int ret;
>
> + if (devtype == &imx50_weim_devtype) {
> + ret = imx_weim_gpr_setup(pdev);
> + if (ret)
> + return ret;
> + }
> +
> for_each_child_of_node(pdev->dev.of_node, child) {
> if (!child->name)
> continue;
> --
> 1.7.9.5
>
>
--
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 3/6] PM / Voltagedomain: introduce voltage domain driver support
From: Mark Brown @ 2014-02-24 1:58 UTC (permalink / raw)
To: Nishanth Menon
Cc: Rafael J. Wysocki, Viresh Kumar, MyungJoo Ham, Mike Turquette,
devicetree, linux-doc, linux-kernel, cpufreq, linux-pm,
linux-arm-kernel, linux-omap
In-Reply-To: <1392755543-28335-4-git-send-email-nm@ti.com>
[-- Attachment #1: Type: text/plain, Size: 1468 bytes --]
On Tue, Feb 18, 2014 at 02:32:20PM -0600, Nishanth Menon wrote:
> The current regulator model provides the basic building blocks for the
> transitions, however SoC drivers specific to each of these devices, be
> it cpufreq/devfreq have to replicate the logic for functionality.
> To simply the logic, we can hence introduce a layer that takes care
> of the mundane transition logic, registration mechanisms to provide
> the "user drivers" such as cpufreq/devfreq a generic interface, whose
> details are abstracted by the device tree description for the SoC on
> which the driver operates on.
This doesn't really provide a picture of what the generic interface
that's being offered is and...
> drivers/power/voltdm/Kconfig | 5 +
> drivers/power/voltdm/Makefile | 3 +
> drivers/power/voltdm/core.c | 347 +++++++++++++++++++++++--
> drivers/power/voltdm/voltage_domain_private.h | 86 ++++++
> 4 files changed, 424 insertions(+), 17 deletions(-)
...the diffstat doesn't make it obvious what the external interface is
either. It would be much easier to review this with a clearer picture
of what it's aiming to implement.
> + voltdm_np = of_parse_phandle(np, prop_name, 0);
> + if (voltdm_np) {
> + ret = of_parse_phandle_with_args(np, prop_name, "#voltdm-cells",
> + 0, args);
> + if (ret)
> + return ERR_PTR(ret);
There seems to be some DT stuff going on here, is the interface DT only?
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* [PATCH v2] dt/bindings: fsl-fec: add clock properties
From: Shawn Guo @ 2014-02-24 1:48 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Rob Herring, Mark Rutland, Gerhard Sittig, Philippe De Muyter,
Shawn Guo
Update fsl-fec.txt to add 'clocks' and 'clock-names' properties.
Signed-off-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
Changes since v1:
- Leave compatible change out, which should probably be addressed by
another patch
- Move clock properties into 'Optional properties:' section
Documentation/devicetree/bindings/net/fsl-fec.txt | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/fsl-fec.txt b/Documentation/devicetree/bindings/net/fsl-fec.txt
index 845ff84..468736d 100644
--- a/Documentation/devicetree/bindings/net/fsl-fec.txt
+++ b/Documentation/devicetree/bindings/net/fsl-fec.txt
@@ -16,6 +16,15 @@ Optional properties:
will have the duration be 1 millisecond. Numbers greater than 1000 are
invalid and 1 millisecond will be used instead.
- phy-supply: regulator that powers the Ethernet PHY.
+- clocks: the clocks feeding the FEC controller and phy.
+ - "ipg": the peripheral access clock
+ - "ahb": the bus clock for MAC
+ - "ptp": the sampling clock for PTP (IEEE 1588). On SoC like i.MX6Q,
+ the clock could come from either the internal clock control module
+ or external oscillator via pad depending on board design.
+ - "enet_out": the phy reference clock provided by SoC via pad, which
+ is available on SoC like i.MX28.
+- clock-names: Must contain the clock names described just above
Example:
--
1.7.9.5
--
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
* Re: [PATCH] clk: shmobile: Fix typoe in MSTP clock DT bindings
From: Mike Turquette @ 2014-02-24 0:54 UTC (permalink / raw)
To: Laurent Pinchart, linux-sh; +Cc: devicetree, Geert Uytterhoeven
In-Reply-To: <1392830004-5706-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Quoting Laurent Pinchart (2014-02-19 09:13:24)
> The DT bindings document a renesas,indices property, while the code, the
> DT example and the DT sources all use renesas,clock-indices. Fix the
> documentation.
>
> Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
> Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> Mike, this is a v3.14 bug fix. You can find all three Renesas clock bugfixes
> for v3.14 in
>
> git://linuxtv.org/pinchartl/fbdev.git clocks/next/drivers
>
> I'll wait a day and send a pull request just to make sure no patch gets
> forgotten.
Can you provide a description of each regression that gets fixed in the
pull request? This one is obvious: a stable api exposed from the kernel
has a bug and was merged towards 3.14, so a fix in the -rc is
appropriate. It helps to have justifications for fixes when I send my
clk-fixes PR. Also the patch $SUBJECT has a typoe ;-)
Thanks,
Mike
>
> diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
> index a6a352c..5992dce 100644
> --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
> +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
> @@ -21,9 +21,9 @@ Required Properties:
> must appear in the same order as the output clocks.
> - #clock-cells: Must be 1
> - clock-output-names: The name of the clocks as free-form strings
> - - renesas,indices: Indices of the gate clocks into the group (0 to 31)
> + - renesas,clock-indices: Indices of the gate clocks into the group (0 to 31)
>
> -The clocks, clock-output-names and renesas,indices properties contain one
> +The clocks, clock-output-names and renesas,clock-indices properties contain one
> entry per gate clock. The MSTP groups are sparsely populated. Unimplemented
> gate clocks must not be declared.
>
> --
> Regards,
>
> Laurent Pinchart
>
^ permalink raw reply
* Re: [PATCH RFC v1 3/3] clk: Add handling of clk parent and rate assigned from DT
From: Mike Turquette @ 2014-02-24 0:48 UTC (permalink / raw)
To: Sylwester Nawrocki, Grant Likely
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, linux-lFZ/pmaqli7XmaaqVzeoHQ,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
galak-sgV2jX0FEOL9JmXXK+q4OQ,
kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ,
sw0312.kim-Sze3O3UU22JBDgjK7y7TUQ,
m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
t.figa-Sze3O3UU22JBDgjK7y7TUQ
In-Reply-To: <53072C9D.5040303-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Quoting Sylwester Nawrocki (2014-02-21 02:38:21)
> On 20/02/14 15:09, Grant Likely wrote:
> [...]
> >> > diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt b/Documentation/devicetree/bindings/clock/clock-bindings.txt
> >> > index 7c52c29..d618498 100644
> >> > --- a/Documentation/devicetree/bindings/clock/clock-bindings.txt
> >> > +++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt
> >> > @@ -115,3 +115,27 @@ clock signal, and a UART.
> >> > ("pll" and "pll-switched").
> >> > * The UART has its baud clock connected the external oscillator and its
> >> > register clock connected to the PLL clock (the "pll-switched" signal)
> >> > +
> >> > +==Static initial configuration of clock parent and clock frequency==
> >> > +
> >> > +Some platforms require static configuration of (parts of) the clock controller
> >> > +often determined by the board design. Such a configuration can be specified in
> >> > +a clock consumer node through [clk-name]-clk-parent and [clk-name]-clk-rate DT
> >> > +properties. The former should contain phandle and clock specifier of the parent
> >> > +clock, the latter the required clock's frequency value (one cell). "clk-name"
> >> > +should be listed in the clock-names property and a phandle and a clock specifier
> >> > +pair corresponding to it should be present in the clocks property.
> >> > +
> >> > + uart@a000 {
> >> > + compatible = "fsl,imx-uart";
> >> > + reg = <0xa000 0x1000>;
> >> > + ...
> >> > + clocks = <&clkcon 0>, <&clkcon 3>;
> >> > + clock-names = "baud", "mux";
> >> > +
> >> > + mux-clk-parent = <&pll 1>;
> >> > + baud-clk-rate = <460800>;
> >
> > This mixes patterns for references to clocks. Plus it requires composing
> > property names which is a little painful. I'd rather see a list of
> > tuples to match the existing pattern already in use
> >
> > clocks = <&clkcon 0>, <&clkcon 3>;
> > clock-names = "baud", "mux";
> > clock-parents = <0> <&pll 1>;
> > clock-rates = <0> <460800>;
>
> Thank you for the review. This looks much better to me. My bad, I wasn't
> aware 0 can be used to denote an empty phandle like this. ePAPR seems not
> to be specifying exact meaning of the 'phandle' property values, except
> they be unique. Anyway, it seems to be clearly documented within the
> __of_parse_phandle_with_args() function.
>
> I'll try this modified binding instead, presumably it would be useful to
> have a variant of __of_parse_phandle_with_args() function which would
> accept a context data containing result of previous call within an iteration,
> similarly as of_property_next_string() is written. So we don't iterate
> from beginning of the list with each __of_parse_phandle_with_args() call.
> But it's an optimization issue that could be considered separately I guess.
I was always partial to the regulator style of blahblah-supply but
Grant's suggestion is much cleaner with respect to the rest of the clock
binding.
I guess it will be a bit ugly if a very long array is needed with a
sparse attribute. E.g. 20 clocks specified in the 'clocks' property and
only a single clock needs to have its parent or rate specified.
Is there a reason not to support both methods?
Regards,
Mike
>
> --
> Thanks,
> Sylwester
--
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 RFC v1 1/3] clk: Add function to parse an arbitrary clocks list property
From: Mike Turquette @ 2014-02-24 0:43 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: linux-lFZ/pmaqli7XmaaqVzeoHQ, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
grant.likely-QSEj5FYQhm4dnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
galak-sgV2jX0FEOL9JmXXK+q4OQ,
kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ,
sw0312.kim-Sze3O3UU22JBDgjK7y7TUQ,
m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
t.figa-Sze3O3UU22JBDgjK7y7TUQ, Sylwester Nawrocki
In-Reply-To: <1392829124-25705-2-git-send-email-s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Quoting Sylwester Nawrocki (2014-02-19 08:58:42)
> The of_clk_get_list_entry() function is like of_clk_get() except it allows
> to pass name of a DT property containing list of phandles and clock specifiers.
> For of_clk_get() it has been hard coded to "clocks".
>
> Signed-off-by: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> Acked-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
> drivers/clk/clk.h | 3 +++
> drivers/clk/clkdev.c | 25 +++++++++++++++++++++----
> 2 files changed, 24 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h
> index 795cc9f..dd9def1 100644
> --- a/drivers/clk/clk.h
> +++ b/drivers/clk/clk.h
> @@ -10,7 +10,10 @@
> */
>
> #if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
> +
> struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec);
> void of_clk_lock(void);
> void of_clk_unlock(void);
> +struct clk *of_clk_get_list_entry(struct device_node *np,
> + const char *list_name, int index);
> #endif
> diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
> index a360b2e..465662e 100644
> --- a/drivers/clk/clkdev.c
> +++ b/drivers/clk/clkdev.c
> @@ -27,17 +27,28 @@ static LIST_HEAD(clocks);
> static DEFINE_MUTEX(clocks_mutex);
>
> #if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
> -struct clk *of_clk_get(struct device_node *np, int index)
> +/**
> + * of_clk_get_list_entry() - Parse and lookup a clock referenced by a device node
> + * @np: pointer to clock consumer node
> + * @list_name: name of the clock list property
> + * @index: index to the clock list
> + *
> + * This function parses the @list_name property and together with @index
> + * value indicating an entry of the list uses it to look up the struct clk
> + * from the registered list of clock providers.
> + */
> +struct clk *of_clk_get_list_entry(struct device_node *np,
> + const char *list_name, int index)
Bikeshed alert: how about of_clk_get_by_property or of_clk_get_by_prop?
No strong opinion on the topic though.
Regards,
Mike
> {
> struct of_phandle_args clkspec;
> struct clk *clk;
> int rc;
>
> - if (index < 0)
> + if (index < 0 || !list_name)
> return ERR_PTR(-EINVAL);
>
> - rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
> - &clkspec);
> + rc = of_parse_phandle_with_args(np, list_name, "#clock-cells",
> + index, &clkspec);
> if (rc)
> return ERR_PTR(rc);
>
> @@ -51,6 +62,12 @@ struct clk *of_clk_get(struct device_node *np, int index)
> of_node_put(clkspec.np);
> return clk;
> }
> +EXPORT_SYMBOL(of_clk_get_list_entry);
> +
> +struct clk *of_clk_get(struct device_node *np, int index)
> +{
> + return of_clk_get_list_entry(np, "clocks", index);
> +}
> EXPORT_SYMBOL(of_clk_get);
>
> /**
> --
> 1.7.9.5
>
--
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/7] IBM Akebono: Add a SDHCI platform driver
From: Alistair Popple @ 2014-02-24 0:38 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: linux-mmc, cjb, devicetree, linux-kernel, linuxppc-dev
In-Reply-To: <1773685.WfxOP0FuHM@wuerfel>
On Fri, 21 Feb 2014 15:14:30 Arnd Bergmann wrote:
> On Friday 21 February 2014 17:31:27 Alistair Popple wrote:
> > +config MMC_SDHCI_OF_476GTR
> > + tristate "SDHCI OF support for the IBM PPC476GTR SoC"
> > + depends on MMC_SDHCI_PLTFM
> > + depends on PPC_OF
> > + help
> > + This selects the Secure Digital Host Controller Interface (SDHCI)
> > + found on the PPC476GTR SoC.
> > +
> > + If you have a controller with this interface, say Y or M here.
> > +
> > + If unsure, say N.
>
> Your driver doesn't actually do anything beyond what is in the common
> sdhci-pltfm.c infrastructure. IMHO you really shoulnd't need a SoC
> specific abstraction for it at all and instead add a generic
> platform driver registration into sdhci-pltfm.c. I'd suggest
> you use "generic-sdhci" (similar to what we do for usb-ohci and usb-ehci
> now) as the compatible string and change your device tree to claim
> compatibility with that and your soc-specific string.
That's a reasonable point. I guess I was just following the example set by the
other sdhci-* drivers. However on review they're not as generic as this one so
I will merge this into sdhci-pltfm.c as suggested.
- Alistair
> Arnd
^ permalink raw reply
* Re: [PATCH] clk: shmobile: Fix typoe in MSTP clock DT bindings
From: Simon Horman @ 2014-02-24 0:35 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: linux-sh, devicetree, Mike Turquette, Geert Uytterhoeven
In-Reply-To: <1392830004-5706-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
On Wed, Feb 19, 2014 at 06:13:24PM +0100, Laurent Pinchart wrote:
> The DT bindings document a renesas,indices property, while the code, the
> DT example and the DT sources all use renesas,clock-indices. Fix the
> documentation.
>
> Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Acked-by: Simon Horman <horms@verge.net.au>
BTW, there is a typo (in the word typo) in the subject :^)
> ---
> Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> Mike, this is a v3.14 bug fix. You can find all three Renesas clock bugfixes
> for v3.14 in
>
> git://linuxtv.org/pinchartl/fbdev.git clocks/next/drivers
>
> I'll wait a day and send a pull request just to make sure no patch gets
> forgotten.
>
> diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
> index a6a352c..5992dce 100644
> --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
> +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
> @@ -21,9 +21,9 @@ Required Properties:
> must appear in the same order as the output clocks.
> - #clock-cells: Must be 1
> - clock-output-names: The name of the clocks as free-form strings
> - - renesas,indices: Indices of the gate clocks into the group (0 to 31)
> + - renesas,clock-indices: Indices of the gate clocks into the group (0 to 31)
>
> -The clocks, clock-output-names and renesas,indices properties contain one
> +The clocks, clock-output-names and renesas,clock-indices properties contain one
> entry per gate clock. The MSTP groups are sparsely populated. Unimplemented
> gate clocks must not be declared.
>
> --
> Regards,
>
> Laurent Pinchart
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sh" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH v2 0/2] Add Ether DT support for R8A7791/Koelsch reference board
From: Simon Horman @ 2014-02-24 0:33 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: linux-sh, devicetree, magnus.damm, linux, linux-arm-kernel,
robh+dt, pawel.moll, mark.rutland, ijc+devicetree, galak
In-Reply-To: <201402200225.00598.sergei.shtylyov@cogentembedded.com>
On Thu, Feb 20, 2014 at 02:24:59AM +0300, Sergei Shtylyov wrote:
> Hello.
>
> Here's the set of 2 patches against Simon Horman's 'renesas.git' repo,
> 'renesas-devel-v3.14-rc3-20140218' tag. Here we add the Ether device tree
> support on the R8A7791/Koelsch reference board. The patchset requires
> the 'sh_eth' driver device tree support just merged to the 'net-next.git' repo
> in order to work.
>
> [1/2] ARM: shmobile: r8a7791: add Ether DT support
> [2/2] ARM: shmobile: koelsch: add Ether DT support
Thanks, I have queued these up.
^ permalink raw reply
* Re: [PATCH v2 0/2] Add Ether DT support for R8A7790/Lager reference board
From: Simon Horman @ 2014-02-24 0:32 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: linux-sh, devicetree, magnus.damm, linux, linux-arm-kernel,
robh+dt, pawel.moll, mark.rutland, ijc+devicetree, galak
In-Reply-To: <201402200218.23099.sergei.shtylyov@cogentembedded.com>
On Thu, Feb 20, 2014 at 02:18:22AM +0300, Sergei Shtylyov wrote:
> Hello.
>
> Here's the set of 2 patches against Simon Horman's 'renesas.git' repo,
> 'renesas-devel-v3.14-rc3-20140218' tag. Here we add the Ether device tree
> support on the R8A7790/Lager reference board. The patchset requires the
> 'sh_eth' driver device tree support just merged to the 'net-next.git' repo in
> order to work.
>
> [1/2] ARM: shmobile: r8a7790: add Ether DT support
> [2/2] ARM: shmobile: lager: add Ether DT support
Thanks, I have queued these up.
^ permalink raw reply
* Re: [PATCH v2 2/2] ARM: shmobile: bockw reference dts: Add SPI FLASH
From: Simon Horman @ 2014-02-24 0:28 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: linux-spi, devicetree, linux-arm-kernel, linux-sh, linux-kernel,
Geert Uytterhoeven
In-Reply-To: <1392908239-22645-2-git-send-email-geert@linux-m68k.org>
On Thu, Feb 20, 2014 at 03:57:19PM +0100, Geert Uytterhoeven wrote:
> From: Geert Uytterhoeven <geert+renesas@linux-m68k.org>
>
> Add Spansion s25fl008k SPI FLASH and MTD partition, based on bockw legacy
> board code.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@linux-m68k.org>
> Tested-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> ---
> v2:
> - Add Tested-by
Thanks, I have queued this up.
^ permalink raw reply
* Re: [PATCH v2 1/2] ARM: shmobile: r8a7778/r8a7779 dtsi: Improve and correct HSPI bindings
From: Simon Horman @ 2014-02-24 0:28 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: linux-spi, devicetree, linux-arm-kernel, linux-sh, linux-kernel,
Geert Uytterhoeven, Mark Brown
In-Reply-To: <1392908239-22645-1-git-send-email-geert@linux-m68k.org>
On Thu, Feb 20, 2014 at 03:57:18PM +0100, Geert Uytterhoeven wrote:
> From: Geert Uytterhoeven <geert+renesas@linux-m68k.org>
>
> Binding documentation:
> - Add future-proof "renesas,hspi-<soctype>" compatible values,
> - Add "interrupt-parent", "#address-cells" and "#size-cells" properties,
> - Add reference to pinctrl documentation,
> - Add example bindings.
>
> r8a7778 and r8a7779 dtsi:
> - Add "renesas,hspi-r8a7778" resp. "renesas,hspi-r8a7779" compatible
> values,
> - Correct reference to parent interrupt controller
> (use "interrupt-parent" instead of "interrupt-controller"),
> - Add missing "#address-cells" and "#size-cells" properties, which are
> needed when populating the SPI buses.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@linux-m68k.org>
> Tested-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> [HSPI/BockW]
> Cc: Mark Brown <broonie@linaro.org>
> ---
> v2:
> - Add Tested-by
> - List full example compatible properties with soctypes instead of just
> the soctypes, so checkpatch can validate DTSes.
Thanks, I have queued this up.
^ permalink raw reply
* Re: [PATCH 4/7] ECHI Platform: Merge ppc-of EHCI driver into the ehci-platform driver
From: Alistair Popple @ 2014-02-24 0:28 UTC (permalink / raw)
To: Alan Stern
Cc: gregkh, linux-usb, devicetree, linux-kernel, linuxppc-dev,
Tony Prisk, Mark Rutland
In-Reply-To: <Pine.LNX.4.44L0.1402211037300.1273-100000@iolanthe.rowland.org>
On Fri, 21 Feb 2014 10:41:50 Alan Stern wrote:
> On Fri, 21 Feb 2014, Alistair Popple wrote:
> > Currently the ppc-of driver uses the compatibility string
> > "usb-ehci". This means platforms that use device-tree and implement an
> > EHCI compatible interface have to either use the ppc-of driver or add
> > a compatible line to the ehci-platform driver. It would be more
> > appropriate for the platform driver to be compatible with "usb-ehci"
> > as non-powerpc platforms are also beginning to utilise device-tree.
> >
> > This patch merges the device tree property parsing from ehci-ppc-of
> > into the platform driver and adds a "usb-ehci" compatibility
> > string. The existing ehci-ppc-of driver is removed and the 440EPX
> > specific quirks are added to the ehci-platform driver.
> >
> > Signed-off-by: Alistair Popple <alistair@popple.id.au>
>
> This patch is also out of date. The compatibility string used by the
> ehci-platform driver is "generic-ehci".
Ok. I will switch to using "generic-ehci" in the Akebono device tree.
> There remains the question of whether to merge ehci-ppc-of into
> ehci-platform. This would be a rather invasive change, but I suppose
> we could do it. With adjustments along the lines suggested by Mark
> Rutland.
As I can use "generic-ehci" to support Akebono I will drop this patch. I can
look at merging ehci-ppc-of at some later date if desired (thanks Mark & Tony
for reviewing though).
- Alistair
> Alan Stern
^ permalink raw reply
* Re: [PATCH 3/7] IBM Akebono: Add support to the OHCI platform driver for PPC476GTR
From: Alistair Popple @ 2014-02-24 0:20 UTC (permalink / raw)
To: Arnd Bergmann
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <16991392.luyzl1NYP3@wuerfel>
On Fri, 21 Feb 2014 15:16:52 Arnd Bergmann wrote:
> On Friday 21 February 2014 17:31:29 Alistair Popple wrote:
> > +static const struct of_device_id ohci_of_match[] = {
> > + { .compatible = "usb-ohci", },
> > + {},
> > +};
> > +
> >
> > static const struct platform_device_id ohci_platform_table[] = {
> >
> > { "ohci-platform", 0 },
> > { }
> >
> > @@ -198,6 +209,7 @@ static struct platform_driver ohci_platform_driver = {
> >
> > .owner = THIS_MODULE,
> > .name = "ohci-platform",
> > .pm = &ohci_platform_pm_ops,
> >
> > + .of_match_table = ohci_of_match,
> >
> > }
> >
> > };
>
> Linux-next already has a patch to add an of_match_table in this driver,
> using
>
> static const struct of_device_id ohci_platform_ids[] = {
> { .compatible = "generic-ohci", },
> { }
> };
>
> I think you should just use that string on your platform.
Excellent! I will drop this patch and use "generic-ohci" instead. Thanks for
pointing this out.
- Alistair
> Arnd
--
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
* [PATCHv1 6/6] Documentation: DT: omap-ssi binding documentation
From: Sebastian Reichel @ 2014-02-23 23:50 UTC (permalink / raw)
To: Sebastian Reichel, Linus Walleij, Shubhrajyoti Datta,
Carlos Chinea
Cc: Tony Lindgren, Grant Likely, Rob Herring, Pawel Moll,
Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley,
devicetree, linux-kernel, linux-omap, Pali Rohár,
Ивайло Димитров,
Joni Lapilainen, Aaro Koskinen, Sebastian Reichel
In-Reply-To: <1393199401-27197-1-git-send-email-sre@debian.org>
Create device tree binding documentation for
OMAP Synchronous Serial Interface (SSI) device.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
Documentation/devicetree/bindings/hsi/omap_ssi.txt | 82 ++++++++++++++++++++++
1 file changed, 82 insertions(+)
create mode 100644 Documentation/devicetree/bindings/hsi/omap_ssi.txt
diff --git a/Documentation/devicetree/bindings/hsi/omap_ssi.txt b/Documentation/devicetree/bindings/hsi/omap_ssi.txt
new file mode 100644
index 0000000..cfd729b
--- /dev/null
+++ b/Documentation/devicetree/bindings/hsi/omap_ssi.txt
@@ -0,0 +1,82 @@
+OMAP SSI controller bindings
+
+Required properties:
+- compatible: Should include "ti,omap3-ssi".
+- reg-names: Contains the values "sys" and "gdd".
+- reg: Contains a register specifier for each entry in
+ reg-names.
+- interrupt-names: Contains the value "gdd_mpu".
+- interrupts: Contains interrupt information for each entry in
+ interrupt-names.
+- ranges: Represents the bus address mapping between the main
+ controller node and the child nodes below.
+- clocks: Contains clock specifiers for each entry in
+ clock-names.
+- clock-names: Must include the following entries:
+ "ssi_ssr_fck": The OMAP clock of that name
+ "ssi_sst_fck": The OMAP clock of that name
+ "ssi_ick": The OMAP clock of that name
+- #address-cells: Should be set to <1>
+- #size-cells: Should be set to <1>
+
+Each port is represented as a sub-node of the ti,omap3-ssi device.
+
+Required Port sub-node properties:
+- compatible: Should be set to the following value
+ ti,omap3-ssi-port (applicable to OMAP34xx devices)
+- reg-names: Contains the values "rx" and "tx".
+- reg: Contains a register specifier for each entry in
+ reg-names.
+- interrupt-parent Should be a phandle for the interrupt controller
+- interrupt-names: Contains the values "mpu_irq0" and "mpu_irq1".
+- interrupts: Contains interrupt information for each entry in
+ interrupt-names.
+- ti,ssi-cawake-gpio: Defines which GPIO pin is used to signify CAWAKE
+ events for the port. This is an optional board-specific
+ property. If it's missing the port will not be
+ enabled.
+
+Example for Nokia N900:
+
+ssi-controller@48058000 {
+ compatible = "ti,omap3-ssi";
+
+ /* needed until hwmod is updated to use the compatible string */
+ ti,hwmods = "ssi";
+
+ reg = <0x48058000 0x1000>,
+ <0x48059000 0x1000>;
+ reg-names = "sys",
+ "gdd";
+
+ interrupts = <55>;
+ interrupt-names = "gdd_mpu";
+
+ clocks = <&ssi_ssr_fck_3430es1>,
+ <&ssi_sst_fck_3430es1>,
+ <&ssi_ick_3430es1>;
+ clock-names = "ssi_ssr_fck",
+ "ssi_sst_fck",
+ "ssi_ick";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ ssi-port@0 {
+ compatible = "ti,omap3-ssi-port";
+
+ reg = <0x4805a000 0x800>,
+ <0x4805a800 0x800>;
+ reg-names = "tx",
+ "rx";
+
+ interrupt-parent = <&intc>;
+ interrupts = <51>,
+ <52>;
+ interrupt-names = "mpu_irq0",
+ "mpu_irq1";
+
+ ti,ssi-cawake-gpio = <&gpio5 23 GPIO_ACTIVE_HIGH>; /* 151 */
+ }
+}
--
1.8.5.3
^ permalink raw reply related
* [PATCHv1 5/6] HSI: Introduce OMAP SSI driver
From: Sebastian Reichel @ 2014-02-23 23:50 UTC (permalink / raw)
To: Sebastian Reichel, Linus Walleij, Shubhrajyoti Datta,
Carlos Chinea
Cc: Tony Lindgren, Grant Likely, Rob Herring, Pawel Moll,
Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley,
devicetree, linux-kernel, linux-omap, Pali Rohár,
Ивайло Димитров,
Joni Lapilainen, Aaro Koskinen, Sebastian Reichel
In-Reply-To: <1393199401-27197-1-git-send-email-sre@debian.org>
Add OMAP SSI driver to the HSI subsystem.
The Synchronous Serial Interface (SSI) is a legacy version
of HSI. As in the case of HSI, it is mainly used to connect
Application engines (APE) with cellular modem engines (CMT)
in cellular handsets.
It provides a multichannel, full-duplex, multi-core communication
with no reference clock. The OMAP SSI block is capable of reaching
speeds of 110 Mbit/s.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
drivers/hsi/Kconfig | 1 +
drivers/hsi/Makefile | 1 +
drivers/hsi/controllers/Kconfig | 19 +
drivers/hsi/controllers/Makefile | 6 +
drivers/hsi/controllers/omap_ssi.c | 618 ++++++++++++++
drivers/hsi/controllers/omap_ssi.h | 166 ++++
drivers/hsi/controllers/omap_ssi_port.c | 1401 +++++++++++++++++++++++++++++++
drivers/hsi/controllers/omap_ssi_regs.h | 171 ++++
8 files changed, 2383 insertions(+)
create mode 100644 drivers/hsi/controllers/Kconfig
create mode 100644 drivers/hsi/controllers/Makefile
create mode 100644 drivers/hsi/controllers/omap_ssi.c
create mode 100644 drivers/hsi/controllers/omap_ssi.h
create mode 100644 drivers/hsi/controllers/omap_ssi_port.c
create mode 100644 drivers/hsi/controllers/omap_ssi_regs.h
diff --git a/drivers/hsi/Kconfig b/drivers/hsi/Kconfig
index d94e38d..2c76de4 100644
--- a/drivers/hsi/Kconfig
+++ b/drivers/hsi/Kconfig
@@ -14,6 +14,7 @@ config HSI_BOARDINFO
bool
default y
+source "drivers/hsi/controllers/Kconfig"
source "drivers/hsi/clients/Kconfig"
endif # HSI
diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile
index 9d5d33f..360371e 100644
--- a/drivers/hsi/Makefile
+++ b/drivers/hsi/Makefile
@@ -3,4 +3,5 @@
#
obj-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o
obj-$(CONFIG_HSI) += hsi.o
+obj-y += controllers/
obj-y += clients/
diff --git a/drivers/hsi/controllers/Kconfig b/drivers/hsi/controllers/Kconfig
new file mode 100644
index 0000000..037a344
--- /dev/null
+++ b/drivers/hsi/controllers/Kconfig
@@ -0,0 +1,19 @@
+#
+# HSI controllers configuration
+#
+comment "HSI controllers"
+
+config OMAP_SSI
+ tristate "OMAP SSI hardware driver"
+ depends on ARCH_OMAP && HSI
+ ---help---
+ SSI is a legacy version of HSI. It is usually used to connect
+ an application engine with a cellular modem.
+ If you say Y here, you will enable the OMAP SSI hardware driver.
+
+ If unsure, say N.
+
+config OMAP_SSI_PORT
+ tristate
+ default m if OMAP_SSI=m
+ default y if OMAP_SSI=y
diff --git a/drivers/hsi/controllers/Makefile b/drivers/hsi/controllers/Makefile
new file mode 100644
index 0000000..d2665cf
--- /dev/null
+++ b/drivers/hsi/controllers/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for HSI controllers drivers
+#
+
+obj-$(CONFIG_OMAP_SSI) += omap_ssi.o
+obj-$(CONFIG_OMAP_SSI_PORT) += omap_ssi_port.o
diff --git a/drivers/hsi/controllers/omap_ssi.c b/drivers/hsi/controllers/omap_ssi.c
new file mode 100644
index 0000000..55ef01b
--- /dev/null
+++ b/drivers/hsi/controllers/omap_ssi.c
@@ -0,0 +1,618 @@
+/* OMAP SSI driver.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2013 Sebastian Reichel <sre@kernel.org>
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/compiler.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/scatterlist.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_platform.h>
+#include <linux/hsi/hsi.h>
+#include <linux/idr.h>
+
+#include "omap_ssi_regs.h"
+#include "omap_ssi.h"
+
+/* For automatically allocated device IDs */
+static DEFINE_IDA(platform_omap_ssi_ida);
+
+#ifdef CONFIG_DEBUG_FS
+static int ssi_debug_show(struct seq_file *m, void *p __maybe_unused)
+{
+ struct hsi_controller *ssi = m->private;
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *sys = omap_ssi->sys;
+
+ pm_runtime_get_sync(ssi->device.parent);
+ seq_printf(m, "REVISION\t: 0x%08x\n", readl(sys + SSI_REVISION_REG));
+ seq_printf(m, "SYSCONFIG\t: 0x%08x\n", readl(sys + SSI_SYSCONFIG_REG));
+ seq_printf(m, "SYSSTATUS\t: 0x%08x\n", readl(sys + SSI_SYSSTATUS_REG));
+ pm_runtime_put_sync(ssi->device.parent);
+
+ return 0;
+}
+
+static int ssi_debug_gdd_show(struct seq_file *m, void *p __maybe_unused)
+{
+ struct hsi_controller *ssi = m->private;
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *gdd = omap_ssi->gdd;
+ void __iomem *sys = omap_ssi->sys;
+ int lch;
+
+ pm_runtime_get_sync(ssi->device.parent);
+
+ seq_printf(m, "GDD_MPU_STATUS\t: 0x%08x\n",
+ readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG));
+ seq_printf(m, "GDD_MPU_ENABLE\t: 0x%08x\n\n",
+ readl(sys + SSI_GDD_MPU_IRQ_ENABLE_REG));
+ seq_printf(m, "HW_ID\t\t: 0x%08x\n",
+ readl(gdd + SSI_GDD_HW_ID_REG));
+ seq_printf(m, "PPORT_ID\t: 0x%08x\n",
+ readl(gdd + SSI_GDD_PPORT_ID_REG));
+ seq_printf(m, "MPORT_ID\t: 0x%08x\n",
+ readl(gdd + SSI_GDD_MPORT_ID_REG));
+ seq_printf(m, "TEST\t\t: 0x%08x\n",
+ readl(gdd + SSI_GDD_TEST_REG));
+ seq_printf(m, "GCR\t\t: 0x%08x\n",
+ readl(gdd + SSI_GDD_GCR_REG));
+
+ for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) {
+ seq_printf(m, "\nGDD LCH %d\n=========\n", lch);
+ seq_printf(m, "CSDP\t\t: 0x%04x\n",
+ readw(gdd + SSI_GDD_CSDP_REG(lch)));
+ seq_printf(m, "CCR\t\t: 0x%04x\n",
+ readw(gdd + SSI_GDD_CCR_REG(lch)));
+ seq_printf(m, "CICR\t\t: 0x%04x\n",
+ readw(gdd + SSI_GDD_CICR_REG(lch)));
+ seq_printf(m, "CSR\t\t: 0x%04x\n",
+ readw(gdd + SSI_GDD_CSR_REG(lch)));
+ seq_printf(m, "CSSA\t\t: 0x%08x\n",
+ readl(gdd + SSI_GDD_CSSA_REG(lch)));
+ seq_printf(m, "CDSA\t\t: 0x%08x\n",
+ readl(gdd + SSI_GDD_CDSA_REG(lch)));
+ seq_printf(m, "CEN\t\t: 0x%04x\n",
+ readw(gdd + SSI_GDD_CEN_REG(lch)));
+ seq_printf(m, "CSAC\t\t: 0x%04x\n",
+ readw(gdd + SSI_GDD_CSAC_REG(lch)));
+ seq_printf(m, "CDAC\t\t: 0x%04x\n",
+ readw(gdd + SSI_GDD_CDAC_REG(lch)));
+ seq_printf(m, "CLNK_CTRL\t: 0x%04x\n",
+ readw(gdd + SSI_GDD_CLNK_CTRL_REG(lch)));
+ }
+
+ pm_runtime_put_sync(ssi->device.parent);
+
+ return 0;
+}
+
+static int ssi_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ssi_debug_show, inode->i_private);
+}
+
+static int ssi_gdd_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ssi_debug_gdd_show, inode->i_private);
+}
+
+static const struct file_operations ssi_regs_fops = {
+ .open = ssi_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations ssi_gdd_regs_fops = {
+ .open = ssi_gdd_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init ssi_debug_add_ctrl(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct dentry *dir;
+
+ /* SSI controller */
+ omap_ssi->dir = debugfs_create_dir(dev_name(&ssi->device), NULL);
+ if (IS_ERR(omap_ssi->dir))
+ return PTR_ERR(omap_ssi->dir);
+
+ debugfs_create_file("regs", S_IRUGO, omap_ssi->dir, ssi,
+ &ssi_regs_fops);
+ /* SSI GDD (DMA) */
+ dir = debugfs_create_dir("gdd", omap_ssi->dir);
+ if (IS_ERR(dir))
+ goto rback;
+ debugfs_create_file("regs", S_IRUGO, dir, ssi, &ssi_gdd_regs_fops);
+
+ return 0;
+rback:
+ debugfs_remove_recursive(omap_ssi->dir);
+
+ return PTR_ERR(dir);
+}
+
+static void ssi_debug_remove_ctrl(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ debugfs_remove_recursive(omap_ssi->dir);
+}
+#endif /* CONFIG_DEBUG_FS */
+
+/*
+ * FIXME: Horrible HACK needed until we remove the useless wakeline test
+ * in the CMT. To be removed !!!!
+ */
+void ssi_waketest(struct hsi_client *cl, unsigned int enable)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ omap_port->wktest = !!enable;
+ if (omap_port->wktest) {
+ pm_runtime_get_sync(ssi->device.parent);
+ writel_relaxed(SSI_WAKE(0),
+ omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
+ } else {
+ writel_relaxed(SSI_WAKE(0),
+ omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
+ pm_runtime_put_sync(ssi->device.parent);
+ }
+}
+EXPORT_SYMBOL_GPL(ssi_waketest);
+
+static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg = omap_ssi->gdd_trn[lch].msg;
+ struct hsi_port *port = to_hsi_port(msg->cl->device.parent);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ unsigned int dir;
+ u32 csr;
+ u32 val;
+
+ spin_lock(&omap_ssi->lock);
+
+ val = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ val &= ~SSI_GDD_LCH(lch);
+ writel_relaxed(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+
+ if (msg->ttype == HSI_MSG_READ) {
+ dir = DMA_FROM_DEVICE;
+ val = SSI_DATAAVAILABLE(msg->channel);
+ pm_runtime_put_sync(ssi->device.parent);
+ } else {
+ dir = DMA_TO_DEVICE;
+ val = SSI_DATAACCEPT(msg->channel);
+ /* Keep clocks reference for write pio event */
+ }
+ dma_unmap_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, dir);
+ csr = readw(omap_ssi->gdd + SSI_GDD_CSR_REG(lch));
+ omap_ssi->gdd_trn[lch].msg = NULL; /* release GDD lch */
+ dev_dbg(&port->device, "DMA completed ch %d ttype %d\n",
+ msg->channel, msg->ttype);
+ spin_unlock(&omap_ssi->lock);
+ if (csr & SSI_CSR_TOUR) { /* Timeout error */
+ msg->status = HSI_STATUS_ERROR;
+ msg->actual_len = 0;
+ spin_lock(&omap_port->lock);
+ list_del(&msg->link); /* Dequeue msg */
+ spin_unlock(&omap_port->lock);
+ msg->complete(msg);
+ return;
+ }
+ spin_lock(&omap_port->lock);
+ val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ writel_relaxed(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ spin_unlock(&omap_port->lock);
+
+ msg->status = HSI_STATUS_COMPLETED;
+ msg->actual_len = sg_dma_len(msg->sgt.sgl);
+}
+
+static void ssi_gdd_tasklet(unsigned long dev)
+{
+ struct hsi_controller *ssi = (struct hsi_controller *)dev;
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *sys = omap_ssi->sys;
+ unsigned int lch;
+ u32 status_reg;
+
+ pm_runtime_get_sync(ssi->device.parent);
+
+ status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+ for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) {
+ if (status_reg & SSI_GDD_LCH(lch))
+ ssi_gdd_complete(ssi, lch);
+ }
+ writel_relaxed(status_reg, sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+ status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+
+ pm_runtime_put_sync(ssi->device.parent);
+
+ if (status_reg)
+ tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
+ else
+ enable_irq(omap_ssi->gdd_irq);
+
+}
+
+static irqreturn_t ssi_gdd_isr(int irq, void *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
+ disable_irq_nosync(irq);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ unsigned long rate = clk_get_rate(omap_ssi->fck);
+ return rate;
+}
+
+static int __init ssi_get_iomem(struct platform_device *pd,
+ const char *name, void __iomem **pbase, dma_addr_t *phy)
+{
+ struct resource *mem;
+ struct resource *ioarea;
+ void __iomem *base;
+ struct hsi_controller *ssi = platform_get_drvdata(pd);
+
+ mem = platform_get_resource_byname(pd, IORESOURCE_MEM, name);
+ if (!mem) {
+ dev_err(&pd->dev, "IO memory region missing (%s)\n", name);
+ return -ENXIO;
+ }
+ ioarea = devm_request_mem_region(&ssi->device, mem->start,
+ resource_size(mem), dev_name(&pd->dev));
+ if (!ioarea) {
+ dev_err(&pd->dev, "%s IO memory region request failed\n",
+ mem->name);
+ return -ENXIO;
+ }
+ base = devm_ioremap(&ssi->device, mem->start, resource_size(mem));
+ if (!base) {
+ dev_err(&pd->dev, "%s IO remap failed\n", mem->name);
+ return -ENXIO;
+ }
+ *pbase = base;
+
+ if (phy)
+ *phy = mem->start;
+
+ return 0;
+}
+
+static int __init ssi_add_controller(struct hsi_controller *ssi,
+ struct platform_device *pd)
+{
+ struct omap_ssi_controller *omap_ssi;
+ struct resource *irq;
+ int err;
+
+ omap_ssi = devm_kzalloc(&ssi->device, sizeof(*omap_ssi), GFP_KERNEL);
+ if (!omap_ssi) {
+ dev_err(&pd->dev, "not enough memory for omap ssi\n");
+ return -ENOMEM;
+ }
+
+ ssi->id = ida_simple_get(&platform_omap_ssi_ida, 0, 0, GFP_KERNEL);
+ if (ssi->id < 0) {
+ err = ssi->id;
+ goto out_err;
+ }
+
+ ssi->owner = THIS_MODULE;
+ ssi->device.parent = &pd->dev;
+ dev_set_name(&ssi->device, "ssi%d", ssi->id);
+ hsi_controller_set_drvdata(ssi, omap_ssi);
+ omap_ssi->dev = &ssi->device;
+ err = ssi_get_iomem(pd, "sys", &omap_ssi->sys, NULL);
+ if (err < 0)
+ goto out_err;
+ err = ssi_get_iomem(pd, "gdd", &omap_ssi->gdd, NULL);
+ if (err < 0)
+ goto out_err;
+ irq = platform_get_resource_byname(pd, IORESOURCE_IRQ, "gdd_mpu");
+ if (!irq) {
+ dev_err(&pd->dev, "GDD IRQ resource missing\n");
+ err = -ENXIO;
+ goto out_err;
+ }
+ omap_ssi->gdd_irq = irq->start;
+ tasklet_init(&omap_ssi->gdd_tasklet, ssi_gdd_tasklet,
+ (unsigned long)ssi);
+ err = devm_request_irq(&ssi->device, omap_ssi->gdd_irq, ssi_gdd_isr,
+ 0, irq->name, ssi);
+ if (err < 0) {
+ dev_err(&ssi->device, "Request GDD IRQ %d failed (%d)",
+ omap_ssi->gdd_irq, err);
+ goto out_err;
+ }
+
+ omap_ssi->port = devm_kzalloc(&ssi->device,
+ sizeof(struct omap_ssi_port *) * ssi->num_ports, GFP_KERNEL);
+ if (!omap_ssi->port) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ omap_ssi->fck = devm_clk_get(&ssi->device, "ssi_ssr_fck");
+ if (IS_ERR(omap_ssi->fck)) {
+ dev_err(&pd->dev, "Could not acquire clock \"ssi_ssr_fck\": %li\n",
+ PTR_ERR(omap_ssi->fck));
+ err = -ENODEV;
+ goto out_err;
+ }
+
+ /* TODO: find register, which can be used to detect context loss */
+ omap_ssi->get_loss = NULL;
+
+ omap_ssi->max_speed = UINT_MAX;
+ spin_lock_init(&omap_ssi->lock);
+ err = hsi_register_controller(ssi);
+
+ if (err < 0)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ ida_simple_remove(&platform_omap_ssi_ida, ssi->id);
+ return err;
+}
+
+static int __init ssi_hw_init(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ unsigned int i;
+ u32 val;
+ int err;
+
+ err = pm_runtime_get_sync(ssi->device.parent);
+ if (err < 0) {
+ dev_err(&ssi->device, "runtime PM failed %d\n", err);
+ return err;
+ }
+ /* Reseting SSI controller */
+ writel_relaxed(SSI_SOFTRESET, omap_ssi->sys + SSI_SYSCONFIG_REG);
+ val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
+ for (i = 0; ((i < 20) && !(val & SSI_RESETDONE)); i++) {
+ msleep(20);
+ val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
+ }
+ if (!(val & SSI_RESETDONE)) {
+ dev_err(&ssi->device, "SSI HW reset failed\n");
+ pm_runtime_put_sync(ssi->device.parent);
+ return -EIO;
+ }
+ /* Reseting GDD */
+ writel_relaxed(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG);
+ /* Get FCK rate in KHz */
+ omap_ssi->fck_rate = DIV_ROUND_CLOSEST(ssi_get_clk_rate(ssi), 1000);
+ dev_dbg(&ssi->device, "SSI fck rate %lu KHz\n", omap_ssi->fck_rate);
+ /* Set default PM settings */
+ val = SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART;
+ writel_relaxed(val, omap_ssi->sys + SSI_SYSCONFIG_REG);
+ omap_ssi->sysconfig = val;
+ writel_relaxed(SSI_CLK_AUTOGATING_ON, omap_ssi->sys + SSI_GDD_GCR_REG);
+ omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON;
+ pm_runtime_put_sync(ssi->device.parent);
+
+ return 0;
+}
+
+static void ssi_remove_controller(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ int id = ssi->id;
+ tasklet_kill(&omap_ssi->gdd_tasklet);
+ hsi_unregister_controller(ssi);
+ ida_simple_remove(&platform_omap_ssi_ida, id);
+}
+
+static inline int ssi_of_get_available_child_count(const struct device_node *np)
+{
+ struct device_node *child;
+ int num = 0;
+
+ for_each_child_of_node(np, child)
+ if (of_device_is_available(child))
+ num++;
+
+ return num;
+}
+
+static int __init ssi_probe(struct platform_device *pd)
+{
+ struct device_node *np = pd->dev.of_node;
+ struct hsi_controller *ssi;
+ int err;
+ int num_ports;
+
+ if (!np) {
+ dev_err(&pd->dev, "missing device tree data\n");
+ return -EINVAL;
+ }
+
+ num_ports = ssi_of_get_available_child_count(np);
+
+ ssi = hsi_alloc_controller(num_ports, GFP_KERNEL);
+ if (!ssi) {
+ dev_err(&pd->dev, "No memory for controller\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pd, ssi);
+
+ err = ssi_add_controller(ssi, pd);
+ if (err < 0)
+ goto out1;
+
+ pm_runtime_irq_safe(&pd->dev);
+ pm_runtime_enable(&pd->dev);
+
+ err = ssi_hw_init(ssi);
+ if (err < 0)
+ goto out2;
+#ifdef CONFIG_DEBUG_FS
+ err = ssi_debug_add_ctrl(ssi);
+ if (err < 0)
+ goto out2;
+#endif
+
+ err = of_platform_populate(pd->dev.of_node, NULL, NULL, &pd->dev);
+ if (err) {
+ dev_err(&pd->dev, "failed to create ssi controller ports (err=%d)\n",
+ err);
+ return -ENODEV;
+ }
+
+ dev_info(&pd->dev, "ssi controller %d initialized (%d ports)!\n",
+ ssi->id, num_ports);
+ return err;
+out2:
+ ssi_remove_controller(ssi);
+out1:
+ platform_set_drvdata(pd, NULL);
+ pm_runtime_disable(&pd->dev);
+
+ return err;
+}
+
+static int __exit ssi_remove_ports(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ of_device_unregister(pdev);
+
+ return 0;
+}
+
+static int __exit ssi_remove(struct platform_device *pd)
+{
+ struct hsi_controller *ssi = platform_get_drvdata(pd);
+
+#ifdef CONFIG_DEBUG_FS
+ ssi_debug_remove_ctrl(ssi);
+#endif
+ ssi_remove_controller(ssi);
+ platform_set_drvdata(pd, NULL);
+
+ pm_runtime_disable(&pd->dev);
+
+ /* cleanup of of_platform_populate() call */
+ device_for_each_child(&pd->dev, NULL, ssi_remove_ports);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int omap_ssi_runtime_suspend(struct device *dev)
+{
+ struct hsi_controller *ssi = dev_get_drvdata(dev);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ dev_dbg(dev, "runtime suspend!\n");
+
+ if (omap_ssi->get_loss)
+ omap_ssi->loss_count =
+ (*omap_ssi->get_loss)(ssi->device.parent);
+
+ return 0;
+}
+
+static int omap_ssi_runtime_resume(struct device *dev)
+{
+ struct hsi_controller *ssi = dev_get_drvdata(dev);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ dev_dbg(dev, "runtime resume!\n");
+
+ if ((omap_ssi->get_loss) && (omap_ssi->loss_count ==
+ (*omap_ssi->get_loss)(ssi->device.parent)))
+ return 0;
+
+ writel_relaxed(omap_ssi->gdd_gcr, omap_ssi->gdd + SSI_GDD_GCR_REG);
+
+ return 0;
+}
+
+static const struct dev_pm_ops omap_ssi_pm_ops = {
+ SET_RUNTIME_PM_OPS(omap_ssi_runtime_suspend, omap_ssi_runtime_resume,
+ NULL)
+};
+
+#define DEV_PM_OPS (&omap_ssi_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_ssi_of_match[] = {
+ { .compatible = "ti,omap3-ssi", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_ssi_of_match);
+#else
+#define omap_ssi_of_match NULL
+#endif
+
+static struct platform_driver ssi_pdriver = {
+ .remove = __exit_p(ssi_remove),
+ .driver = {
+ .name = "omap_ssi",
+ .owner = THIS_MODULE,
+ .pm = DEV_PM_OPS,
+ .of_match_table = omap_ssi_of_match,
+ },
+};
+
+module_platform_driver_probe(ssi_pdriver, ssi_probe);
+
+MODULE_ALIAS("platform:omap_ssi");
+MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
+MODULE_AUTHOR("Sebastian Reichel <sre@debian.org>");
+MODULE_DESCRIPTION("Synchronous Serial Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h
new file mode 100644
index 0000000..9d05641
--- /dev/null
+++ b/drivers/hsi/controllers/omap_ssi.h
@@ -0,0 +1,166 @@
+/* OMAP SSI internal interface.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2013 Sebastian Reichel
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __LINUX_HSI_OMAP_SSI_H__
+#define __LINUX_HSI_OMAP_SSI_H__
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/hsi/hsi.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#define SSI_MAX_CHANNELS 8
+#define SSI_MAX_GDD_LCH 8
+#define SSI_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1)
+
+/**
+ * struct omap_ssm_ctx - OMAP synchronous serial module (TX/RX) context
+ * @mode: Bit transmission mode
+ * @channels: Number of channels
+ * @framesize: Frame size in bits
+ * @timeout: RX frame timeout
+ * @divisor: TX divider
+ * @arb_mode: Arbitration mode for TX frame (Round robin, priority)
+ */
+struct omap_ssm_ctx {
+ u32 mode;
+ u32 channels;
+ u32 frame_size;
+ union {
+ u32 timeout; /* Rx Only */
+ struct {
+ u32 arb_mode;
+ u32 divisor;
+ }; /* Tx only */
+ };
+};
+
+/**
+ * struct omap_ssi_port - OMAP SSI port data
+ * @dev: device associated to the port (HSI port)
+ * @pdev: platform device associated to the port
+ * @sst_dma: SSI transmitter physical base address
+ * @ssr_dma: SSI receiver physical base address
+ * @sst_base: SSI transmitter base address
+ * @ssr_base: SSI receiver base address
+ * @wk_lock: spin lock to serialize access to the wake lines
+ * @lock: Spin lock to serialize access to the SSI port
+ * @channels: Current number of channels configured (1,2,4 or 8)
+ * @txqueue: TX message queues
+ * @rxqueue: RX message queues
+ * @brkqueue: Queue of incoming HWBREAK requests (FRAME mode)
+ * @irq: IRQ number
+ * @wake_irq: IRQ number for incoming wake line (-1 if none)
+ * @wake_gpio: GPIO number for incoming wake line (-1 if none)
+ * @pio_tasklet: Bottom half for PIO transfers and events
+ * @wake_tasklet: Bottom half for incoming wake events
+ * @wkin_cken: Keep track of clock references due to the incoming wake line
+ * @wk_refcount: Reference count for output wake line
+ * @sys_mpu_enable: Context for the interrupt enable register for irq 0
+ * @sst: Context for the synchronous serial transmitter
+ * @ssr: Context for the synchronous serial receiver
+ */
+struct omap_ssi_port {
+ struct device *dev;
+ struct device *pdev;
+ dma_addr_t sst_dma;
+ dma_addr_t ssr_dma;
+ void __iomem *sst_base;
+ void __iomem *ssr_base;
+ spinlock_t wk_lock;
+ spinlock_t lock;
+ unsigned int channels;
+ struct list_head txqueue[SSI_MAX_CHANNELS];
+ struct list_head rxqueue[SSI_MAX_CHANNELS];
+ struct list_head brkqueue;
+ unsigned int irq;
+ int wake_irq;
+ int wake_gpio;
+ struct tasklet_struct pio_tasklet;
+ struct tasklet_struct wake_tasklet;
+ bool wktest:1; /* FIXME: HACK to be removed */
+ bool wkin_cken:1; /* Workaround */
+ unsigned int wk_refcount;
+ /* OMAP SSI port context */
+ u32 sys_mpu_enable; /* We use only one irq */
+ struct omap_ssm_ctx sst;
+ struct omap_ssm_ctx ssr;
+ u32 loss_count;
+ u32 port_id;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dir;
+#endif
+};
+
+/**
+ * struct gdd_trn - GDD transaction data
+ * @msg: Pointer to the HSI message being served
+ * @sg: Pointer to the current sg entry being served
+ */
+struct gdd_trn {
+ struct hsi_msg *msg;
+ struct scatterlist *sg;
+};
+
+/**
+ * struct omap_ssi_controller - OMAP SSI controller data
+ * @dev: device associated to the controller (HSI controller)
+ * @sys: SSI I/O base address
+ * @gdd: GDD I/O base address
+ * @fck: SSI functional clock
+ * @gdd_irq: IRQ line for GDD
+ * @gdd_tasklet: bottom half for DMA transfers
+ * @gdd_trn: Array of GDD transaction data for ongoing GDD transfers
+ * @lock: lock to serialize access to GDD
+ * @loss_count: To follow if we need to restore context or not
+ * @max_speed: Maximum TX speed (Kb/s) set by the clients.
+ * @sysconfig: SSI controller saved context
+ * @gdd_gcr: SSI GDD saved context
+ * @get_loss: Pointer to omap_pm_get_dev_context_loss_count, if any
+ * @port: Array of pointers of the ports of the controller
+ * @dir: Debugfs SSI root directory
+ */
+struct omap_ssi_controller {
+ struct device *dev;
+ void __iomem *sys;
+ void __iomem *gdd;
+ struct clk *fck;
+ unsigned int gdd_irq;
+ struct tasklet_struct gdd_tasklet;
+ struct gdd_trn gdd_trn[SSI_MAX_GDD_LCH];
+ spinlock_t lock;
+ unsigned long fck_rate;
+ u32 loss_count;
+ u32 max_speed;
+ /* OMAP SSI Controller context */
+ u32 sysconfig;
+ u32 gdd_gcr;
+ int (*get_loss)(struct device *dev);
+ struct omap_ssi_port **port;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dir;
+#endif
+};
+
+#endif /* __LINUX_HSI_OMAP_SSI_H__ */
diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c
new file mode 100644
index 0000000..1ecae14
--- /dev/null
+++ b/drivers/hsi/controllers/omap_ssi_port.c
@@ -0,0 +1,1401 @@
+/* OMAP SSI port driver.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2013 Sebastian Reichel <sre@kernel.org>
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/of_gpio.h>
+#include <linux/debugfs.h>
+
+#include "omap_ssi_regs.h"
+#include "omap_ssi.h"
+
+static inline int hsi_dummy_msg(struct hsi_msg *msg __maybe_unused)
+{
+ return 0;
+}
+
+static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused)
+{
+ return 0;
+}
+
+static inline unsigned int ssi_wakein(struct hsi_port *port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ return gpio_get_value(omap_port->wake_gpio);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void ssi_debug_remove_port(struct hsi_port *port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+
+ debugfs_remove_recursive(omap_port->dir);
+}
+
+static int ssi_debug_port_show(struct seq_file *m, void *p __maybe_unused)
+{
+ struct hsi_port *port = m->private;
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *base = omap_ssi->sys;
+ unsigned int ch;
+
+ pm_runtime_get_sync(omap_port->pdev);
+ if (omap_port->wake_irq > 0)
+ seq_printf(m, "CAWAKE\t\t: %d\n", ssi_wakein(port));
+ seq_printf(m, "WAKE\t\t: 0x%08x\n",
+ readl(base + SSI_WAKE_REG(port->num)));
+ seq_printf(m, "MPU_ENABLE_IRQ%d\t: 0x%08x\n", 0,
+ readl(base + SSI_MPU_ENABLE_REG(port->num, 0)));
+ seq_printf(m, "MPU_STATUS_IRQ%d\t: 0x%08x\n", 0,
+ readl(base + SSI_MPU_STATUS_REG(port->num, 0)));
+ /* SST */
+ base = omap_port->sst_base;
+ seq_puts(m, "\nSST\n===\n");
+ seq_printf(m, "ID SST\t\t: 0x%08x\n",
+ readl(base + SSI_SST_ID_REG));
+ seq_printf(m, "MODE\t\t: 0x%08x\n",
+ readl(base + SSI_SST_MODE_REG));
+ seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+ readl(base + SSI_SST_FRAMESIZE_REG));
+ seq_printf(m, "DIVISOR\t\t: 0x%08x\n",
+ readl(base + SSI_SST_DIVISOR_REG));
+ seq_printf(m, "CHANNELS\t: 0x%08x\n",
+ readl(base + SSI_SST_CHANNELS_REG));
+ seq_printf(m, "ARBMODE\t\t: 0x%08x\n",
+ readl(base + SSI_SST_ARBMODE_REG));
+ seq_printf(m, "TXSTATE\t\t: 0x%08x\n",
+ readl(base + SSI_SST_TXSTATE_REG));
+ seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+ readl(base + SSI_SST_BUFSTATE_REG));
+ seq_printf(m, "BREAK\t\t: 0x%08x\n",
+ readl(base + SSI_SST_BREAK_REG));
+ for (ch = 0; ch < omap_port->channels; ch++) {
+ seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+ readl(base + SSI_SST_BUFFER_CH_REG(ch)));
+ }
+ /* SSR */
+ base = omap_port->ssr_base;
+ seq_puts(m, "\nSSR\n===\n");
+ seq_printf(m, "ID SSR\t\t: 0x%08x\n",
+ readl(base + SSI_SSR_ID_REG));
+ seq_printf(m, "MODE\t\t: 0x%08x\n",
+ readl(base + SSI_SSR_MODE_REG));
+ seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+ readl(base + SSI_SSR_FRAMESIZE_REG));
+ seq_printf(m, "CHANNELS\t: 0x%08x\n",
+ readl(base + SSI_SSR_CHANNELS_REG));
+ seq_printf(m, "TIMEOUT\t\t: 0x%08x\n",
+ readl(base + SSI_SSR_TIMEOUT_REG));
+ seq_printf(m, "RXSTATE\t\t: 0x%08x\n",
+ readl(base + SSI_SSR_RXSTATE_REG));
+ seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+ readl(base + SSI_SSR_BUFSTATE_REG));
+ seq_printf(m, "BREAK\t\t: 0x%08x\n",
+ readl(base + SSI_SSR_BREAK_REG));
+ seq_printf(m, "ERROR\t\t: 0x%08x\n",
+ readl(base + SSI_SSR_ERROR_REG));
+ seq_printf(m, "ERRORACK\t: 0x%08x\n",
+ readl(base + SSI_SSR_ERRORACK_REG));
+ for (ch = 0; ch < omap_port->channels; ch++) {
+ seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+ readl(base + SSI_SSR_BUFFER_CH_REG(ch)));
+ }
+ pm_runtime_put_sync(omap_port->pdev);
+
+ return 0;
+}
+
+static int ssi_port_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ssi_debug_port_show, inode->i_private);
+}
+
+static const struct file_operations ssi_port_regs_fops = {
+ .open = ssi_port_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ssi_div_get(void *data, u64 *val)
+{
+ struct hsi_port *port = data;
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+
+ pm_runtime_get_sync(omap_port->pdev);
+ *val = readl(omap_port->sst_base + SSI_SST_DIVISOR_REG);
+ pm_runtime_put_sync(omap_port->pdev);
+
+ return 0;
+}
+
+static int ssi_div_set(void *data, u64 val)
+{
+ struct hsi_port *port = data;
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+
+ if (val > 127)
+ return -EINVAL;
+
+ pm_runtime_get_sync(omap_port->pdev);
+ writel(val, omap_port->sst_base + SSI_SST_DIVISOR_REG);
+ omap_port->sst.divisor = val;
+ pm_runtime_put_sync(omap_port->pdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n");
+
+static int __init ssi_debug_add_port(struct omap_ssi_port *omap_port,
+ struct dentry *dir)
+{
+ struct hsi_port *port = to_hsi_port(omap_port->dev);
+
+ dir = debugfs_create_dir(dev_name(omap_port->dev), dir);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+ omap_port->dir = dir;
+ debugfs_create_file("regs", S_IRUGO, dir, port, &ssi_port_regs_fops);
+ dir = debugfs_create_dir("sst", dir);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+ debugfs_create_file("divisor", S_IRUGO | S_IWUSR, dir, port,
+ &ssi_sst_div_fops);
+
+ return 0;
+}
+#endif
+
+static int ssi_claim_lch(struct hsi_msg *msg)
+{
+
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ int lch;
+
+ for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++)
+ if (!omap_ssi->gdd_trn[lch].msg) {
+ omap_ssi->gdd_trn[lch].msg = msg;
+ omap_ssi->gdd_trn[lch].sg = msg->sgt.sgl;
+ return lch;
+ }
+
+ return -EBUSY;
+}
+
+static int ssi_start_dma(struct hsi_msg *msg, int lch)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *gdd = omap_ssi->gdd;
+ int err;
+ u16 csdp;
+ u16 ccr;
+ u32 s_addr;
+ u32 d_addr;
+ u32 tmp;
+
+ if (msg->ttype == HSI_MSG_READ) {
+ err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents,
+ DMA_FROM_DEVICE);
+ if (err < 0) {
+ dev_dbg(&ssi->device, "DMA map SG failed !\n");
+ return err;
+ }
+ csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT |
+ SSI_SRC_SINGLE_ACCESS0 | SSI_SRC_PERIPHERAL_PORT |
+ SSI_DATA_TYPE_S32;
+ ccr = msg->channel + 0x10 + (port->num * 8); /* Sync */
+ ccr |= SSI_DST_AMODE_POSTINC | SSI_SRC_AMODE_CONST |
+ SSI_CCR_ENABLE;
+ s_addr = omap_port->ssr_dma +
+ SSI_SSR_BUFFER_CH_REG(msg->channel);
+ d_addr = sg_dma_address(msg->sgt.sgl);
+ } else {
+ err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents,
+ DMA_TO_DEVICE);
+ if (err < 0) {
+ dev_dbg(&ssi->device, "DMA map SG failed !\n");
+ return err;
+ }
+ csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT |
+ SSI_DST_SINGLE_ACCESS0 | SSI_DST_PERIPHERAL_PORT |
+ SSI_DATA_TYPE_S32;
+ ccr = (msg->channel + 1 + (port->num * 8)) & 0xf; /* Sync */
+ ccr |= SSI_SRC_AMODE_POSTINC | SSI_DST_AMODE_CONST |
+ SSI_CCR_ENABLE;
+ s_addr = sg_dma_address(msg->sgt.sgl);
+ d_addr = omap_port->sst_dma +
+ SSI_SST_BUFFER_CH_REG(msg->channel);
+ }
+ dev_dbg(&ssi->device, "lch %d cdsp %08x ccr %04x s_addr %08x d_addr %08x\n",
+ lch, csdp, ccr, s_addr, d_addr);
+
+ /* Hold clocks during the transfer */
+ pm_runtime_get_sync(omap_port->pdev);
+
+ writew_relaxed(csdp, gdd + SSI_GDD_CSDP_REG(lch));
+ writew_relaxed(SSI_BLOCK_IE | SSI_TOUT_IE, gdd + SSI_GDD_CICR_REG(lch));
+ writel_relaxed(d_addr, gdd + SSI_GDD_CDSA_REG(lch));
+ writel_relaxed(s_addr, gdd + SSI_GDD_CSSA_REG(lch));
+ writew_relaxed(SSI_BYTES_TO_FRAMES(msg->sgt.sgl->length),
+ gdd + SSI_GDD_CEN_REG(lch));
+
+ spin_lock_bh(&omap_ssi->lock);
+ tmp = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ tmp |= SSI_GDD_LCH(lch);
+ writel_relaxed(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ spin_unlock_bh(&omap_ssi->lock);
+ writew(ccr, gdd + SSI_GDD_CCR_REG(lch));
+ msg->status = HSI_STATUS_PROCEEDING;
+
+ return 0;
+}
+
+static int ssi_start_pio(struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ u32 val;
+
+ pm_runtime_get_sync(omap_port->pdev);
+ if (msg->ttype == HSI_MSG_WRITE) {
+ val = SSI_DATAACCEPT(msg->channel);
+ /* Hold clocks for pio writes */
+ pm_runtime_get_sync(omap_port->pdev);
+ } else {
+ val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED;
+ }
+ dev_dbg(&port->device, "Single %s transfer\n",
+ msg->ttype ? "write" : "read");
+ val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ pm_runtime_put_sync(omap_port->pdev);
+ msg->actual_len = 0;
+ msg->status = HSI_STATUS_PROCEEDING;
+
+ return 0;
+}
+
+static int ssi_start_transfer(struct list_head *queue)
+{
+ struct hsi_msg *msg;
+ int lch = -1;
+
+ if (list_empty(queue))
+ return 0;
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ if (msg->status != HSI_STATUS_QUEUED)
+ return 0;
+ if ((msg->sgt.nents) && (msg->sgt.sgl->length > sizeof(u32)))
+ lch = ssi_claim_lch(msg);
+ if (lch >= 0)
+ return ssi_start_dma(msg, lch);
+ else
+ return ssi_start_pio(msg);
+}
+
+static int ssi_async_break(struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ int err = 0;
+ u32 tmp;
+
+ pm_runtime_get_sync(omap_port->pdev);
+ if (msg->ttype == HSI_MSG_WRITE) {
+ if (omap_port->sst.mode != SSI_MODE_FRAME) {
+ err = -EINVAL;
+ goto out;
+ }
+ writel(1, omap_port->sst_base + SSI_SST_BREAK_REG);
+ msg->status = HSI_STATUS_COMPLETED;
+ msg->complete(msg);
+ } else {
+ if (omap_port->ssr.mode != SSI_MODE_FRAME) {
+ err = -EINVAL;
+ goto out;
+ }
+ spin_lock_bh(&omap_port->lock);
+ tmp = readl(omap_ssi->sys +
+ SSI_MPU_ENABLE_REG(port->num, 0));
+ writel(tmp | SSI_BREAKDETECTED,
+ omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ msg->status = HSI_STATUS_PROCEEDING;
+ list_add_tail(&msg->link, &omap_port->brkqueue);
+ spin_unlock_bh(&omap_port->lock);
+ }
+out:
+ pm_runtime_put_sync(omap_port->pdev);
+
+ return err;
+}
+
+static int ssi_async(struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct list_head *queue;
+ int err = 0;
+
+ BUG_ON(!msg);
+
+ if (msg->sgt.nents > 1)
+ return -ENOSYS; /* TODO: Add sg support */
+
+ if (msg->break_frame)
+ return ssi_async_break(msg);
+
+ if (msg->ttype) {
+ BUG_ON(msg->channel >= omap_port->sst.channels);
+ queue = &omap_port->txqueue[msg->channel];
+ } else {
+ BUG_ON(msg->channel >= omap_port->ssr.channels);
+ queue = &omap_port->rxqueue[msg->channel];
+ }
+ msg->status = HSI_STATUS_QUEUED;
+ spin_lock_bh(&omap_port->lock);
+ list_add_tail(&msg->link, queue);
+ err = ssi_start_transfer(queue);
+ if (err < 0) {
+ list_del(&msg->link);
+ msg->status = HSI_STATUS_ERROR;
+ }
+ spin_unlock_bh(&omap_port->lock);
+ dev_dbg(&port->device, "msg status %d ttype %d ch %d\n",
+ msg->status, msg->ttype, msg->channel);
+
+ return err;
+}
+
+static u32 ssi_calculate_div(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ u32 tx_fckrate = (u32) omap_ssi->fck_rate;
+
+ /* / 2 : SSI TX clock is always half of the SSI functional clock */
+ tx_fckrate >>= 1;
+ /* Round down when tx_fckrate % omap_ssi->max_speed == 0 */
+ tx_fckrate--;
+ dev_dbg(&ssi->device, "TX div %d for fck_rate %lu Khz speed %d Kb/s\n",
+ tx_fckrate / omap_ssi->max_speed, omap_ssi->fck_rate,
+ omap_ssi->max_speed);
+
+ return tx_fckrate / omap_ssi->max_speed;
+}
+
+static void ssi_flush_queue(struct list_head *queue, struct hsi_client *cl)
+{
+ struct list_head *node, *tmp;
+ struct hsi_msg *msg;
+
+ list_for_each_safe(node, tmp, queue) {
+ msg = list_entry(node, struct hsi_msg, link);
+ if ((cl) && (cl != msg->cl))
+ continue;
+ list_del(node);
+ pr_debug("flush queue: ch %d, msg %p len %d type %d ctxt %p\n",
+ msg->channel, msg, msg->sgt.sgl->length,
+ msg->ttype, msg->context);
+ if (msg->destructor)
+ msg->destructor(msg);
+ else
+ hsi_free_msg(msg);
+ }
+}
+
+static int ssi_setup(struct hsi_client *cl)
+{
+ struct hsi_port *port = to_hsi_port(cl->device.parent);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *sst = omap_port->sst_base;
+ void __iomem *ssr = omap_port->ssr_base;
+ u32 div;
+ u32 val;
+ int err = 0;
+
+ pm_runtime_get_sync(omap_port->pdev);
+ spin_lock_bh(&omap_port->lock);
+ if (cl->tx_cfg.speed)
+ omap_ssi->max_speed = cl->tx_cfg.speed;
+ div = ssi_calculate_div(ssi);
+ if (div > SSI_MAX_DIVISOR) {
+ dev_err(&cl->device, "Invalid TX speed %d Mb/s (div %d)\n",
+ cl->tx_cfg.speed, div);
+ err = -EINVAL;
+ goto out;
+ }
+ /* Set TX/RX module to sleep to stop TX/RX during cfg update */
+ writel_relaxed(SSI_MODE_SLEEP, sst + SSI_SST_MODE_REG);
+ writel_relaxed(SSI_MODE_SLEEP, ssr + SSI_SSR_MODE_REG);
+ /* Flush posted write */
+ val = readl(ssr + SSI_SSR_MODE_REG);
+ /* TX */
+ writel_relaxed(31, sst + SSI_SST_FRAMESIZE_REG);
+ writel_relaxed(div, sst + SSI_SST_DIVISOR_REG);
+ writel_relaxed(cl->tx_cfg.channels, sst + SSI_SST_CHANNELS_REG);
+ writel_relaxed(cl->tx_cfg.arb_mode, sst + SSI_SST_ARBMODE_REG);
+ writel_relaxed(cl->tx_cfg.mode, sst + SSI_SST_MODE_REG);
+ /* RX */
+ writel_relaxed(31, ssr + SSI_SSR_FRAMESIZE_REG);
+ writel_relaxed(cl->rx_cfg.channels, ssr + SSI_SSR_CHANNELS_REG);
+ writel_relaxed(0, ssr + SSI_SSR_TIMEOUT_REG);
+ /* Cleanup the break queue if we leave FRAME mode */
+ if ((omap_port->ssr.mode == SSI_MODE_FRAME) &&
+ (cl->rx_cfg.mode != SSI_MODE_FRAME))
+ ssi_flush_queue(&omap_port->brkqueue, cl);
+ writel_relaxed(cl->rx_cfg.mode, ssr + SSI_SSR_MODE_REG);
+ omap_port->channels = max(cl->rx_cfg.channels, cl->tx_cfg.channels);
+ /* Shadow registering for OFF mode */
+ /* SST */
+ omap_port->sst.divisor = div;
+ omap_port->sst.frame_size = 31;
+ omap_port->sst.channels = cl->tx_cfg.channels;
+ omap_port->sst.arb_mode = cl->tx_cfg.arb_mode;
+ omap_port->sst.mode = cl->tx_cfg.mode;
+ /* SSR */
+ omap_port->ssr.frame_size = 31;
+ omap_port->ssr.timeout = 0;
+ omap_port->ssr.channels = cl->rx_cfg.channels;
+ omap_port->ssr.mode = cl->rx_cfg.mode;
+out:
+ spin_unlock_bh(&omap_port->lock);
+ pm_runtime_put_sync(omap_port->pdev);
+
+ return err;
+}
+
+static int ssi_flush(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg;
+ void __iomem *sst = omap_port->sst_base;
+ void __iomem *ssr = omap_port->ssr_base;
+ unsigned int i;
+ u32 err;
+
+ pm_runtime_get_sync(omap_port->pdev);
+ spin_lock_bh(&omap_port->lock);
+ /* Stop all DMA transfers */
+ for (i = 0; i < SSI_MAX_GDD_LCH; i++) {
+ msg = omap_ssi->gdd_trn[i].msg;
+ if (!msg || (port != hsi_get_port(msg->cl)))
+ continue;
+ writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
+ if (msg->ttype == HSI_MSG_READ)
+ pm_runtime_put_sync(omap_port->pdev);
+ omap_ssi->gdd_trn[i].msg = NULL;
+ }
+ /* Flush all SST buffers */
+ writel_relaxed(0, sst + SSI_SST_BUFSTATE_REG);
+ writel_relaxed(0, sst + SSI_SST_TXSTATE_REG);
+ /* Flush all SSR buffers */
+ writel_relaxed(0, ssr + SSI_SSR_RXSTATE_REG);
+ writel_relaxed(0, ssr + SSI_SSR_BUFSTATE_REG);
+ /* Flush all errors */
+ err = readl(ssr + SSI_SSR_ERROR_REG);
+ writel_relaxed(err, ssr + SSI_SSR_ERRORACK_REG);
+ /* Flush break */
+ writel_relaxed(0, ssr + SSI_SSR_BREAK_REG);
+ /* Clear interrupts */
+ writel_relaxed(0, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ writel_relaxed(0xffffff00,
+ omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+ writel_relaxed(0, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ writel(0xff, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+ /* Dequeue all pending requests */
+ for (i = 0; i < omap_port->channels; i++) {
+ /* Release write clocks */
+ if (!list_empty(&omap_port->txqueue[i]))
+ pm_runtime_put_sync(omap_port->pdev);
+ ssi_flush_queue(&omap_port->txqueue[i], NULL);
+ ssi_flush_queue(&omap_port->rxqueue[i], NULL);
+ }
+ ssi_flush_queue(&omap_port->brkqueue, NULL);
+ spin_unlock_bh(&omap_port->lock);
+ pm_runtime_put_sync(omap_port->pdev);
+
+ return 0;
+}
+
+static int ssi_start_tx(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ dev_dbg(&port->device, "Wake out high %d\n", omap_port->wk_refcount);
+
+ spin_lock_bh(&omap_port->wk_lock);
+ if (omap_port->wk_refcount++) {
+ spin_unlock_bh(&omap_port->wk_lock);
+ return 0;
+ }
+ pm_runtime_get_sync(omap_port->pdev); /* Grab clocks */
+ writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
+ spin_unlock_bh(&omap_port->wk_lock);
+
+ return 0;
+}
+
+static int ssi_stop_tx(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ dev_dbg(&port->device, "Wake out low %d\n", omap_port->wk_refcount);
+
+ spin_lock_bh(&omap_port->wk_lock);
+ BUG_ON(!omap_port->wk_refcount);
+ if (--omap_port->wk_refcount) {
+ spin_unlock_bh(&omap_port->wk_lock);
+ return 0;
+ }
+ writel(SSI_WAKE(0),
+ omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
+ pm_runtime_put_sync(omap_port->pdev); /* Release clocks */
+ spin_unlock_bh(&omap_port->wk_lock);
+
+ return 0;
+}
+
+static void ssi_transfer(struct omap_ssi_port *omap_port,
+ struct list_head *queue)
+{
+ struct hsi_msg *msg;
+ int err = -1;
+
+ spin_lock_bh(&omap_port->lock);
+ while (err < 0) {
+ err = ssi_start_transfer(queue);
+ if (err < 0) {
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ msg->status = HSI_STATUS_ERROR;
+ msg->actual_len = 0;
+ list_del(&msg->link);
+ spin_unlock_bh(&omap_port->lock);
+ msg->complete(msg);
+ spin_lock_bh(&omap_port->lock);
+ }
+ }
+ spin_unlock_bh(&omap_port->lock);
+}
+
+static void ssi_cleanup_queues(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg;
+ unsigned int i;
+ u32 rxbufstate = 0;
+ u32 txbufstate = 0;
+ u32 status = SSI_ERROROCCURED;
+ u32 tmp;
+
+ ssi_flush_queue(&omap_port->brkqueue, cl);
+ if (list_empty(&omap_port->brkqueue))
+ status |= SSI_BREAKDETECTED;
+
+ for (i = 0; i < omap_port->channels; i++) {
+ if (list_empty(&omap_port->txqueue[i]))
+ continue;
+ msg = list_first_entry(&omap_port->txqueue[i], struct hsi_msg,
+ link);
+ if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) {
+ txbufstate |= (1 << i);
+ status |= SSI_DATAACCEPT(i);
+ /* Release the clocks writes, also GDD ones */
+ pm_runtime_put_sync(omap_port->pdev);
+ }
+ ssi_flush_queue(&omap_port->txqueue[i], cl);
+ }
+ for (i = 0; i < omap_port->channels; i++) {
+ if (list_empty(&omap_port->rxqueue[i]))
+ continue;
+ msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg,
+ link);
+ if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) {
+ rxbufstate |= (1 << i);
+ status |= SSI_DATAAVAILABLE(i);
+ }
+ ssi_flush_queue(&omap_port->rxqueue[i], cl);
+ /* Check if we keep the error detection interrupt armed */
+ if (!list_empty(&omap_port->rxqueue[i]))
+ status &= ~SSI_ERROROCCURED;
+ }
+ /* Cleanup write buffers */
+ tmp = readl(omap_port->sst_base + SSI_SST_BUFSTATE_REG);
+ tmp &= ~txbufstate;
+ writel_relaxed(tmp, omap_port->sst_base + SSI_SST_BUFSTATE_REG);
+ /* Cleanup read buffers */
+ tmp = readl(omap_port->ssr_base + SSI_SSR_BUFSTATE_REG);
+ tmp &= ~rxbufstate;
+ writel_relaxed(tmp, omap_port->ssr_base + SSI_SSR_BUFSTATE_REG);
+ /* Disarm and ack pending interrupts */
+ tmp = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ tmp &= ~status;
+ writel_relaxed(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ writel_relaxed(status, omap_ssi->sys +
+ SSI_MPU_STATUS_REG(port->num, 0));
+}
+
+static void ssi_cleanup_gdd(struct hsi_controller *ssi, struct hsi_client *cl)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_msg *msg;
+ unsigned int i;
+ u32 val = 0;
+ u32 tmp;
+
+ for (i = 0; i < SSI_MAX_GDD_LCH; i++) {
+ msg = omap_ssi->gdd_trn[i].msg;
+ if ((!msg) || (msg->cl != cl))
+ continue;
+ writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
+ val |= (1 << i);
+ /*
+ * Clock references for write will be handled in
+ * ssi_cleanup_queues
+ */
+ if (msg->ttype == HSI_MSG_READ)
+ pm_runtime_put_sync(omap_port->pdev);
+ omap_ssi->gdd_trn[i].msg = NULL;
+ }
+ tmp = readl_relaxed(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ tmp &= ~val;
+ writel_relaxed(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ writel(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+}
+
+static int ssi_set_port_mode(struct omap_ssi_port *omap_port, u32 mode)
+{
+ writel(mode, omap_port->sst_base + SSI_SST_MODE_REG);
+ writel(mode, omap_port->ssr_base + SSI_SSR_MODE_REG);
+ /* OCP barrier */
+ mode = readl(omap_port->ssr_base + SSI_SSR_MODE_REG);
+
+ return 0;
+}
+
+static int ssi_release(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+
+ spin_lock_bh(&omap_port->lock);
+ pm_runtime_get_sync(omap_port->pdev);
+ /* Stop all the pending DMA requests for that client */
+ ssi_cleanup_gdd(ssi, cl);
+ /* Now cleanup all the queues */
+ ssi_cleanup_queues(cl);
+ pm_runtime_put_sync(omap_port->pdev);
+ /* If it is the last client of the port, do extra checks and cleanup */
+ if (port->claimed <= 1) {
+ /*
+ * Drop the clock reference for the incoming wake line
+ * if it is still kept high by the other side.
+ */
+ if (omap_port->wkin_cken) {
+ pm_runtime_put_sync(omap_port->pdev);
+ omap_port->wkin_cken = 0;
+ }
+ pm_runtime_get_sync(omap_port->pdev);
+ /* Stop any SSI TX/RX without a client */
+ ssi_set_port_mode(omap_port, SSI_MODE_SLEEP);
+ omap_port->sst.mode = SSI_MODE_SLEEP;
+ omap_port->ssr.mode = SSI_MODE_SLEEP;
+ pm_runtime_put_sync(omap_port->pdev);
+ WARN_ON(omap_port->wk_refcount != 0);
+ }
+ spin_unlock_bh(&omap_port->lock);
+
+ return 0;
+}
+
+
+
+static void ssi_error(struct hsi_port *port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg;
+ unsigned int i;
+ u32 err;
+ u32 val;
+ u32 tmp;
+
+ /* ACK error */
+ err = readl(omap_port->ssr_base + SSI_SSR_ERROR_REG);
+ dev_err(&port->device, "SSI error: 0x%02x\n", err);
+ if (!err) {
+ dev_dbg(&port->device, "spurious SSI error ignored!\n");
+ return;
+ }
+ spin_lock(&omap_ssi->lock);
+ /* Cancel all GDD read transfers */
+ for (i = 0, val = 0; i < SSI_MAX_GDD_LCH; i++) {
+ msg = omap_ssi->gdd_trn[i].msg;
+ if ((msg) && (msg->ttype == HSI_MSG_READ)) {
+ writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
+ val |= (1 << i);
+ omap_ssi->gdd_trn[i].msg = NULL;
+ }
+ }
+ tmp = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ tmp &= ~val;
+ writel_relaxed(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ spin_unlock(&omap_ssi->lock);
+ /* Cancel all PIO read transfers */
+ spin_lock(&omap_port->lock);
+ tmp = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ tmp &= 0xfeff00ff; /* Disable error & all dataavailable interrupts */
+ writel_relaxed(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ /* ACK error */
+ writel_relaxed(err, omap_port->ssr_base + SSI_SSR_ERRORACK_REG);
+ writel_relaxed(SSI_ERROROCCURED,
+ omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+ /* Signal the error all current pending read requests */
+ for (i = 0; i < omap_port->channels; i++) {
+ if (list_empty(&omap_port->rxqueue[i]))
+ continue;
+ msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg,
+ link);
+ list_del(&msg->link);
+ msg->status = HSI_STATUS_ERROR;
+ spin_unlock(&omap_port->lock);
+ msg->complete(msg);
+ /* Now restart queued reads if any */
+ ssi_transfer(omap_port, &omap_port->rxqueue[i]);
+ spin_lock(&omap_port->lock);
+ }
+ spin_unlock(&omap_port->lock);
+}
+
+static void ssi_break_complete(struct hsi_port *port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg;
+ struct hsi_msg *tmp;
+ u32 val;
+
+ dev_dbg(&port->device, "HWBREAK received\n");
+
+ spin_lock(&omap_port->lock);
+ val = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ val &= ~SSI_BREAKDETECTED;
+ writel_relaxed(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ writel_relaxed(0, omap_port->ssr_base + SSI_SSR_BREAK_REG);
+ writel(SSI_BREAKDETECTED,
+ omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+ spin_unlock(&omap_port->lock);
+
+ list_for_each_entry_safe(msg, tmp, &omap_port->brkqueue, link) {
+ msg->status = HSI_STATUS_COMPLETED;
+ spin_lock(&omap_port->lock);
+ list_del(&msg->link);
+ spin_unlock(&omap_port->lock);
+ msg->complete(msg);
+ }
+
+}
+
+static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
+{
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_msg *msg;
+ u32 *buf;
+ u32 reg;
+ u32 val;
+
+ spin_lock(&omap_port->lock);
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
+ msg->actual_len = 0;
+ msg->status = HSI_STATUS_PENDING;
+ }
+ if (msg->ttype == HSI_MSG_WRITE)
+ val = SSI_DATAACCEPT(msg->channel);
+ else
+ val = SSI_DATAAVAILABLE(msg->channel);
+ if (msg->status == HSI_STATUS_PROCEEDING) {
+ buf = sg_virt(msg->sgt.sgl) + msg->actual_len;
+ if (msg->ttype == HSI_MSG_WRITE)
+ writel(*buf, omap_port->sst_base +
+ SSI_SST_BUFFER_CH_REG(msg->channel));
+ else
+ *buf = readl(omap_port->ssr_base +
+ SSI_SSR_BUFFER_CH_REG(msg->channel));
+ dev_dbg(&port->device, "ch %d ttype %d 0x%08x\n", msg->channel,
+ msg->ttype, *buf);
+ msg->actual_len += sizeof(*buf);
+ if (msg->actual_len >= msg->sgt.sgl->length)
+ msg->status = HSI_STATUS_COMPLETED;
+ /*
+ * Wait for the last written frame to be really sent before
+ * we call the complete callback
+ */
+ if ((msg->status == HSI_STATUS_PROCEEDING) ||
+ ((msg->status == HSI_STATUS_COMPLETED) &&
+ (msg->ttype == HSI_MSG_WRITE))) {
+ writel(val, omap_ssi->sys +
+ SSI_MPU_STATUS_REG(port->num, 0));
+ spin_unlock(&omap_port->lock);
+
+ return;
+ }
+
+ }
+ /* Transfer completed at this point */
+ reg = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ if (msg->ttype == HSI_MSG_WRITE) {
+ /* Release clocks for write transfer */
+ pm_runtime_put_sync(omap_port->pdev);
+ }
+ reg &= ~val;
+ writel_relaxed(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ writel_relaxed(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+ list_del(&msg->link);
+ spin_unlock(&omap_port->lock);
+ msg->complete(msg);
+ ssi_transfer(omap_port, queue);
+}
+
+static void ssi_pio_tasklet(unsigned long ssi_port)
+{
+ struct hsi_port *port = (struct hsi_port *)ssi_port;
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *sys = omap_ssi->sys;
+ unsigned int ch;
+ u32 status_reg;
+
+ pm_runtime_get_sync(omap_port->pdev);
+ status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+ status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
+
+ for (ch = 0; ch < omap_port->channels; ch++) {
+ if (status_reg & SSI_DATAACCEPT(ch))
+ ssi_pio_complete(port, &omap_port->txqueue[ch]);
+ if (status_reg & SSI_DATAAVAILABLE(ch))
+ ssi_pio_complete(port, &omap_port->rxqueue[ch]);
+ }
+ if (status_reg & SSI_BREAKDETECTED)
+ ssi_break_complete(port);
+ if (status_reg & SSI_ERROROCCURED)
+ ssi_error(port);
+
+ status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+ status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ pm_runtime_put_sync(omap_port->pdev);
+
+ if (status_reg)
+ tasklet_hi_schedule(&omap_port->pio_tasklet);
+ else
+ enable_irq(omap_port->irq);
+}
+
+static irqreturn_t ssi_pio_isr(int irq, void *port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+
+ tasklet_hi_schedule(&omap_port->pio_tasklet);
+ disable_irq_nosync(irq);
+
+ return IRQ_HANDLED;
+}
+
+static void ssi_wake_tasklet(unsigned long ssi_port)
+{
+ struct hsi_port *port = (struct hsi_port *)ssi_port;
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ if (ssi_wakein(port)) {
+ /**
+ * We can have a quick High-Low-High transition in the line.
+ * In such a case if we have long interrupt latencies,
+ * we can miss the low event or get twice a high event.
+ * This workaround will avoid breaking the clock reference
+ * count when such a situation ocurrs.
+ */
+ spin_lock(&omap_port->lock);
+ if (!omap_port->wkin_cken) {
+ omap_port->wkin_cken = 1;
+ pm_runtime_get_sync(omap_port->pdev);
+ }
+ spin_unlock(&omap_port->lock);
+ dev_dbg(&ssi->device, "Wake in high\n");
+ if (omap_port->wktest) { /* FIXME: HACK ! To be removed */
+ writel(SSI_WAKE(0),
+ omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
+ }
+ hsi_event(port, HSI_EVENT_START_RX);
+ } else {
+ dev_dbg(&ssi->device, "Wake in low\n");
+ if (omap_port->wktest) { /* FIXME: HACK ! To be removed */
+ writel(SSI_WAKE(0),
+ omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
+ }
+ hsi_event(port, HSI_EVENT_STOP_RX);
+ spin_lock(&omap_port->lock);
+ if (omap_port->wkin_cken) {
+ pm_runtime_put_sync(omap_port->pdev);
+ omap_port->wkin_cken = 0;
+ }
+ spin_unlock(&omap_port->lock);
+ }
+}
+
+static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(ssi_port);
+
+ tasklet_hi_schedule(&omap_port->wake_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static int __init ssi_port_irq(struct hsi_port *port,
+ struct platform_device *pd)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct resource *irq;
+ int err;
+
+ irq = platform_get_resource_byname(pd, IORESOURCE_IRQ, "mpu_irq0");
+ if (!irq) {
+ dev_err(&port->device, "Port IRQ resource missing\n");
+ return -ENXIO;
+ }
+ omap_port->irq = irq->start;
+ tasklet_init(&omap_port->pio_tasklet, ssi_pio_tasklet,
+ (unsigned long)port);
+ err = devm_request_irq(&port->device, omap_port->irq, ssi_pio_isr,
+ 0, irq->name, port);
+ if (err < 0)
+ dev_err(&port->device, "Request IRQ %d failed (%d)\n",
+ omap_port->irq, err);
+ return err;
+}
+
+static int __init ssi_wake_irq(struct hsi_port *port,
+ struct platform_device *pd)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ int cawake_irq;
+ int err;
+
+ if (omap_port->wake_gpio == -1) {
+ omap_port->wake_irq = -1;
+ return 0;
+ }
+
+ cawake_irq = gpio_to_irq(omap_port->wake_gpio);
+
+ omap_port->wake_irq = cawake_irq;
+ tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet,
+ (unsigned long)port);
+ err = devm_request_irq(&port->device, cawake_irq, ssi_wake_isr,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "cawake", port);
+ if (err < 0)
+ dev_err(&port->device, "Request Wake in IRQ %d failed %d\n",
+ cawake_irq, err);
+ err = enable_irq_wake(cawake_irq);
+ if (err < 0)
+ dev_err(&port->device, "Enable wake on the wakeline in irq %d failed %d\n",
+ cawake_irq, err);
+
+ return err;
+}
+
+static void __init ssi_queues_init(struct omap_ssi_port *omap_port)
+{
+ unsigned int ch;
+
+ for (ch = 0; ch < SSI_MAX_CHANNELS; ch++) {
+ INIT_LIST_HEAD(&omap_port->txqueue[ch]);
+ INIT_LIST_HEAD(&omap_port->rxqueue[ch]);
+ }
+ INIT_LIST_HEAD(&omap_port->brkqueue);
+}
+
+static int __init ssi_port_get_iomem(struct platform_device *pd,
+ const char *name, void __iomem **pbase, dma_addr_t *phy)
+{
+ struct hsi_port *port = platform_get_drvdata(pd);
+ struct resource *mem;
+ struct resource *ioarea;
+ void __iomem *base;
+
+ mem = platform_get_resource_byname(pd, IORESOURCE_MEM, name);
+ if (!mem) {
+ dev_err(&pd->dev, "IO memory region missing (%s)\n", name);
+ return -ENXIO;
+ }
+ ioarea = devm_request_mem_region(&port->device, mem->start,
+ resource_size(mem), dev_name(&pd->dev));
+ if (!ioarea) {
+ dev_err(&pd->dev, "%s IO memory region request failed\n",
+ mem->name);
+ return -ENXIO;
+ }
+ base = devm_ioremap(&port->device, mem->start, resource_size(mem));
+ if (!base) {
+ dev_err(&pd->dev, "%s IO remap failed\n", mem->name);
+ return -ENXIO;
+ }
+ *pbase = base;
+
+ if (phy)
+ *phy = mem->start;
+
+ return 0;
+}
+
+static int __init ssi_port_probe(struct platform_device *pd)
+{
+ struct device_node *np = pd->dev.of_node;
+ struct hsi_port *port;
+ struct omap_ssi_port *omap_port;
+ struct hsi_controller *ssi = dev_get_drvdata(pd->dev.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ u32 cawake_gpio = 0;
+ u32 port_id;
+ int err;
+
+ dev_dbg(&pd->dev, "init ssi port...\n");
+
+ err = ref_module(THIS_MODULE, ssi->owner);
+ if (err) {
+ dev_err(&pd->dev, "could not increment parent module refcount (err=%d)\n",
+ err);
+ return -ENODEV;
+ }
+
+ if (!ssi->port || !omap_ssi->port) {
+ dev_err(&pd->dev, "ssi controller not initialized!\n");
+ err = -ENODEV;
+ goto error;
+ }
+
+ /* get id of first uninitialized port in controller */
+ for (port_id = 0; port_id < ssi->num_ports && omap_ssi->port[port_id];
+ port_id++)
+ ;
+
+ if (port_id >= ssi->num_ports) {
+ dev_err(&pd->dev, "port id out of range!\n");
+ err = -ENODEV;
+ goto error;
+ }
+
+ port = ssi->port[port_id];
+
+ if (!np) {
+ dev_err(&pd->dev, "missing device tree data\n");
+ err = -EINVAL;
+ goto error;
+ }
+
+ cawake_gpio = of_get_named_gpio(np, "ti,ssi-cawake-gpio", 0);
+ if (cawake_gpio < 0) {
+ dev_err(&pd->dev, "DT data is missing cawake gpio (err=%d)\n",
+ cawake_gpio);
+ err = -ENODEV;
+ goto error;
+ }
+
+ err = devm_gpio_request_one(&port->device, cawake_gpio, GPIOF_DIR_IN,
+ "cawake");
+ if (err) {
+ dev_err(&pd->dev, "could not request cawake gpio (err=%d)!\n",
+ err);
+ err = -ENXIO;
+ goto error;
+ }
+
+ omap_port = devm_kzalloc(&port->device, sizeof(*omap_port), GFP_KERNEL);
+ if (!omap_port) {
+ err = -ENOMEM;
+ goto error;
+ }
+ omap_port->wake_gpio = cawake_gpio;
+ omap_port->pdev = &pd->dev;
+ omap_port->port_id = port_id;
+
+ /* initialize HSI port */
+ port->async = ssi_async;
+ port->setup = ssi_setup;
+ port->flush = ssi_flush;
+ port->start_tx = ssi_start_tx;
+ port->stop_tx = ssi_stop_tx;
+ port->release = ssi_release;
+ hsi_port_set_drvdata(port, omap_port);
+ omap_ssi->port[port_id] = omap_port;
+
+ platform_set_drvdata(pd, port);
+
+ err = ssi_port_get_iomem(pd, "tx", &omap_port->sst_base,
+ &omap_port->sst_dma);
+ if (err < 0)
+ goto error;
+ err = ssi_port_get_iomem(pd, "rx", &omap_port->ssr_base,
+ &omap_port->ssr_dma);
+ if (err < 0)
+ goto error;
+
+ err = ssi_port_irq(port, pd);
+ if (err < 0)
+ goto error;
+ err = ssi_wake_irq(port, pd);
+ if (err < 0)
+ goto error;
+
+ ssi_queues_init(omap_port);
+ spin_lock_init(&omap_port->lock);
+ spin_lock_init(&omap_port->wk_lock);
+ omap_port->dev = &port->device;
+
+ pm_runtime_irq_safe(omap_port->pdev);
+ pm_runtime_enable(omap_port->pdev);
+
+#ifdef CONFIG_DEBUG_FS
+ err = ssi_debug_add_port(omap_port, omap_ssi->dir);
+ if (err < 0) {
+ pm_runtime_disable(omap_port->pdev);
+ goto error;
+ }
+#endif
+
+ hsi_add_clients_from_dt(port, np);
+
+ dev_info(&pd->dev, "ssi port %u successfully initialized (cawake=%d)\n",
+ port_id, cawake_gpio);
+
+ return 0;
+
+error:
+ return err;
+}
+
+static int __exit ssi_port_remove(struct platform_device *pd)
+{
+ struct hsi_port *port = platform_get_drvdata(pd);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+#ifdef CONFIG_DEBUG_FS
+ ssi_debug_remove_port(port);
+#endif
+
+ hsi_port_unregister_clients(port);
+
+ tasklet_kill(&omap_port->wake_tasklet);
+ tasklet_kill(&omap_port->pio_tasklet);
+
+ port->async = hsi_dummy_msg;
+ port->setup = hsi_dummy_cl;
+ port->flush = hsi_dummy_cl;
+ port->start_tx = hsi_dummy_cl;
+ port->stop_tx = hsi_dummy_cl;
+ port->release = hsi_dummy_cl;
+
+ omap_ssi->port[omap_port->port_id] = NULL;
+ platform_set_drvdata(pd, NULL);
+ pm_runtime_disable(&pd->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int ssi_save_port_ctx(struct omap_ssi_port *omap_port)
+{
+ struct hsi_port *port = to_hsi_port(omap_port->dev);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ omap_port->sys_mpu_enable = readl(omap_ssi->sys +
+ SSI_MPU_ENABLE_REG(port->num, 0));
+
+ return 0;
+}
+
+static int ssi_restore_port_ctx(struct omap_ssi_port *omap_port)
+{
+ struct hsi_port *port = to_hsi_port(omap_port->dev);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *base;
+
+ writel_relaxed(omap_port->sys_mpu_enable,
+ omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+
+ /* SST context */
+ base = omap_port->sst_base;
+ writel_relaxed(omap_port->sst.frame_size, base + SSI_SST_FRAMESIZE_REG);
+ writel_relaxed(omap_port->sst.channels, base + SSI_SST_CHANNELS_REG);
+ writel_relaxed(omap_port->sst.arb_mode, base + SSI_SST_ARBMODE_REG);
+
+ /* SSR context */
+ base = omap_port->ssr_base;
+ writel_relaxed(omap_port->ssr.frame_size, base + SSI_SSR_FRAMESIZE_REG);
+ writel_relaxed(omap_port->ssr.channels, base + SSI_SSR_CHANNELS_REG);
+ writel_relaxed(omap_port->ssr.timeout, base + SSI_SSR_TIMEOUT_REG);
+
+ return 0;
+}
+
+static int ssi_restore_port_mode(struct omap_ssi_port *omap_port)
+{
+ u32 mode;
+
+ writel_relaxed(omap_port->sst.mode,
+ omap_port->sst_base + SSI_SST_MODE_REG);
+ writel_relaxed(omap_port->ssr.mode,
+ omap_port->ssr_base + SSI_SSR_MODE_REG);
+ /* OCP barrier */
+ mode = readl(omap_port->ssr_base + SSI_SSR_MODE_REG);
+
+ return 0;
+}
+
+static int ssi_restore_divisor(struct omap_ssi_port *omap_port)
+{
+ writel_relaxed(omap_port->sst.divisor,
+ omap_port->sst_base + SSI_SST_DIVISOR_REG);
+
+ return 0;
+}
+
+static int omap_ssi_port_runtime_suspend(struct device *dev)
+{
+ struct hsi_port *port = dev_get_drvdata(dev);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ dev_dbg(dev, "port runtime suspend!\n");
+
+ ssi_set_port_mode(omap_port, SSI_MODE_SLEEP);
+ if (omap_ssi->get_loss)
+ omap_port->loss_count =
+ (*omap_ssi->get_loss)(ssi->device.parent);
+ ssi_save_port_ctx(omap_port);
+
+ return 0;
+}
+
+static int omap_ssi_port_runtime_resume(struct device *dev)
+{
+ struct hsi_port *port = dev_get_drvdata(dev);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ dev_dbg(dev, "port runtime resume!\n");
+
+ if ((omap_ssi->get_loss) && (omap_port->loss_count ==
+ (*omap_ssi->get_loss)(ssi->device.parent)))
+ goto mode; /* We always need to restore the mode & TX divisor */
+
+ ssi_restore_port_ctx(omap_port);
+
+mode:
+ ssi_restore_divisor(omap_port);
+ ssi_restore_port_mode(omap_port);
+
+ return 0;
+}
+
+static const struct dev_pm_ops omap_ssi_port_pm_ops = {
+ SET_RUNTIME_PM_OPS(omap_ssi_port_runtime_suspend,
+ omap_ssi_port_runtime_resume, NULL)
+};
+
+#define DEV_PM_OPS (&omap_ssi_port_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif
+
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_ssi_port_of_match[] = {
+ { .compatible = "ti,omap3-ssi-port", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_ssi_port_of_match);
+#else
+#define omap_ssi_port_of_match NULL
+#endif
+
+static struct platform_driver ssi_port_pdriver = {
+ .remove = __exit_p(ssi_port_remove),
+ .driver = {
+ .name = "omap_ssi_port",
+ .owner = THIS_MODULE,
+ .of_match_table = omap_ssi_port_of_match,
+ .pm = DEV_PM_OPS,
+ },
+};
+
+module_platform_driver_probe(ssi_port_pdriver, ssi_port_probe);
+
+MODULE_ALIAS("platform:omap_ssi_port");
+MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
+MODULE_AUTHOR("Sebastian Reichel <sre@debian.org>");
+MODULE_DESCRIPTION("Synchronous Serial Interface Port Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hsi/controllers/omap_ssi_regs.h b/drivers/hsi/controllers/omap_ssi_regs.h
new file mode 100644
index 0000000..08f98dd
--- /dev/null
+++ b/drivers/hsi/controllers/omap_ssi_regs.h
@@ -0,0 +1,171 @@
+/* Hardware definitions for SSI.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __OMAP_SSI_REGS_H__
+#define __OMAP_SSI_REGS_H__
+
+/*
+ * SSI SYS registers
+ */
+#define SSI_REVISION_REG 0
+# define SSI_REV_MAJOR 0xf0
+# define SSI_REV_MINOR 0xf
+#define SSI_SYSCONFIG_REG 0x10
+# define SSI_AUTOIDLE (1 << 0)
+# define SSI_SOFTRESET (1 << 1)
+# define SSI_SIDLEMODE_FORCE 0
+# define SSI_SIDLEMODE_NO (1 << 3)
+# define SSI_SIDLEMODE_SMART (1 << 4)
+# define SSI_SIDLEMODE_MASK 0x18
+# define SSI_MIDLEMODE_FORCE 0
+# define SSI_MIDLEMODE_NO (1 << 12)
+# define SSI_MIDLEMODE_SMART (1 << 13)
+# define SSI_MIDLEMODE_MASK 0x3000
+#define SSI_SYSSTATUS_REG 0x14
+# define SSI_RESETDONE 1
+#define SSI_MPU_STATUS_REG(port, irq) (0x808 + ((port) * 0x10) + ((irq) * 2))
+#define SSI_MPU_ENABLE_REG(port, irq) (0x80c + ((port) * 0x10) + ((irq) * 8))
+# define SSI_DATAACCEPT(channel) (1 << (channel))
+# define SSI_DATAAVAILABLE(channel) (1 << ((channel) + 8))
+# define SSI_DATAOVERRUN(channel) (1 << ((channel) + 16))
+# define SSI_ERROROCCURED (1 << 24)
+# define SSI_BREAKDETECTED (1 << 25)
+#define SSI_GDD_MPU_IRQ_STATUS_REG 0x0800
+#define SSI_GDD_MPU_IRQ_ENABLE_REG 0x0804
+# define SSI_GDD_LCH(channel) (1 << (channel))
+#define SSI_WAKE_REG(port) (0xc00 + ((port) * 0x10))
+#define SSI_CLEAR_WAKE_REG(port) (0xc04 + ((port) * 0x10))
+#define SSI_SET_WAKE_REG(port) (0xc08 + ((port) * 0x10))
+# define SSI_WAKE(channel) (1 << (channel))
+# define SSI_WAKE_MASK 0xff
+
+/*
+ * SSI SST registers
+ */
+#define SSI_SST_ID_REG 0
+#define SSI_SST_MODE_REG 4
+# define SSI_MODE_VAL_MASK 3
+# define SSI_MODE_SLEEP 0
+# define SSI_MODE_STREAM 1
+# define SSI_MODE_FRAME 2
+# define SSI_MODE_MULTIPOINTS 3
+#define SSI_SST_FRAMESIZE_REG 8
+# define SSI_FRAMESIZE_DEFAULT 31
+#define SSI_SST_TXSTATE_REG 0xc
+# define SSI_TXSTATE_IDLE 0
+#define SSI_SST_BUFSTATE_REG 0x10
+# define SSI_FULL(channel) (1 << (channel))
+#define SSI_SST_DIVISOR_REG 0x18
+# define SSI_MAX_DIVISOR 127
+#define SSI_SST_BREAK_REG 0x20
+#define SSI_SST_CHANNELS_REG 0x24
+# define SSI_CHANNELS_DEFAULT 4
+#define SSI_SST_ARBMODE_REG 0x28
+# define SSI_ARBMODE_ROUNDROBIN 0
+# define SSI_ARBMODE_PRIORITY 1
+#define SSI_SST_BUFFER_CH_REG(channel) (0x80 + ((channel) * 4))
+#define SSI_SST_SWAPBUF_CH_REG(channel) (0xc0 + ((channel) * 4))
+
+/*
+ * SSI SSR registers
+ */
+#define SSI_SSR_ID_REG 0
+#define SSI_SSR_MODE_REG 4
+#define SSI_SSR_FRAMESIZE_REG 8
+#define SSI_SSR_RXSTATE_REG 0xc
+#define SSI_SSR_BUFSTATE_REG 0x10
+# define SSI_NOTEMPTY(channel) (1 << (channel))
+#define SSI_SSR_BREAK_REG 0x1c
+#define SSI_SSR_ERROR_REG 0x20
+#define SSI_SSR_ERRORACK_REG 0x24
+#define SSI_SSR_OVERRUN_REG 0x2c
+#define SSI_SSR_OVERRUNACK_REG 0x30
+#define SSI_SSR_TIMEOUT_REG 0x34
+# define SSI_TIMEOUT_DEFAULT 0
+#define SSI_SSR_CHANNELS_REG 0x28
+#define SSI_SSR_BUFFER_CH_REG(channel) (0x80 + ((channel) * 4))
+#define SSI_SSR_SWAPBUF_CH_REG(channel) (0xc0 + ((channel) * 4))
+
+/*
+ * SSI GDD registers
+ */
+#define SSI_GDD_HW_ID_REG 0
+#define SSI_GDD_PPORT_ID_REG 0x10
+#define SSI_GDD_MPORT_ID_REG 0x14
+#define SSI_GDD_PPORT_SR_REG 0x20
+#define SSI_GDD_MPORT_SR_REG 0x24
+# define SSI_ACTIVE_LCH_NUM_MASK 0xff
+#define SSI_GDD_TEST_REG 0x40
+# define SSI_TEST 1
+#define SSI_GDD_GCR_REG 0x100
+# define SSI_CLK_AUTOGATING_ON (1 << 3)
+# define SSI_FREE (1 << 2)
+# define SSI_SWITCH_OFF (1 << 0)
+#define SSI_GDD_GRST_REG 0x200
+# define SSI_SWRESET 1
+#define SSI_GDD_CSDP_REG(channel) (0x800 + ((channel) * 0x40))
+# define SSI_DST_BURST_EN_MASK 0xc000
+# define SSI_DST_SINGLE_ACCESS0 0
+# define SSI_DST_SINGLE_ACCESS (1 << 14)
+# define SSI_DST_BURST_4x32_BIT (2 << 14)
+# define SSI_DST_BURST_8x32_BIT (3 << 14)
+# define SSI_DST_MASK 0x1e00
+# define SSI_DST_MEMORY_PORT (8 << 9)
+# define SSI_DST_PERIPHERAL_PORT (9 << 9)
+# define SSI_SRC_BURST_EN_MASK 0x180
+# define SSI_SRC_SINGLE_ACCESS0 0
+# define SSI_SRC_SINGLE_ACCESS (1 << 7)
+# define SSI_SRC_BURST_4x32_BIT (2 << 7)
+# define SSI_SRC_BURST_8x32_BIT (3 << 7)
+# define SSI_SRC_MASK 0x3c
+# define SSI_SRC_MEMORY_PORT (8 << 2)
+# define SSI_SRC_PERIPHERAL_PORT (9 << 2)
+# define SSI_DATA_TYPE_MASK 3
+# define SSI_DATA_TYPE_S32 2
+#define SSI_GDD_CCR_REG(channel) (0x802 + ((channel) * 0x40))
+# define SSI_DST_AMODE_MASK (3 << 14)
+# define SSI_DST_AMODE_CONST 0
+# define SSI_DST_AMODE_POSTINC (1 << 12)
+# define SSI_SRC_AMODE_MASK (3 << 12)
+# define SSI_SRC_AMODE_CONST 0
+# define SSI_SRC_AMODE_POSTINC (1 << 12)
+# define SSI_CCR_ENABLE (1 << 7)
+# define SSI_CCR_SYNC_MASK 0x1f
+#define SSI_GDD_CICR_REG(channel) (0x804 + ((channel) * 0x40))
+# define SSI_BLOCK_IE (1 << 5)
+# define SSI_HALF_IE (1 << 2)
+# define SSI_TOUT_IE (1 << 0)
+#define SSI_GDD_CSR_REG(channel) (0x806 + ((channel) * 0x40))
+# define SSI_CSR_SYNC (1 << 6)
+# define SSI_CSR_BLOCK (1 << 5)
+# define SSI_CSR_HALF (1 << 2)
+# define SSI_CSR_TOUR (1 << 0)
+#define SSI_GDD_CSSA_REG(channel) (0x808 + ((channel) * 0x40))
+#define SSI_GDD_CDSA_REG(channel) (0x80c + ((channel) * 0x40))
+#define SSI_GDD_CEN_REG(channel) (0x810 + ((channel) * 0x40))
+#define SSI_GDD_CSAC_REG(channel) (0x818 + ((channel) * 0x40))
+#define SSI_GDD_CDAC_REG(channel) (0x81a + ((channel) * 0x40))
+#define SSI_GDD_CLNK_CTRL_REG(channel) (0x828 + ((channel) * 0x40))
+# define SSI_ENABLE_LNK (1 << 15)
+# define SSI_STOP_LNK (1 << 14)
+# define SSI_NEXT_CH_ID_MASK 0xf
+
+#endif /* __OMAP_SSI_REGS_H__ */
--
1.8.5.3
^ permalink raw reply related
* [PATCHv1 4/6] HSI: hsi-char: fix driver for multiport scenarios
From: Sebastian Reichel @ 2014-02-23 23:49 UTC (permalink / raw)
To: Sebastian Reichel, Linus Walleij, Shubhrajyoti Datta,
Carlos Chinea
Cc: Tony Lindgren, Grant Likely, Rob Herring, Pawel Moll,
Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley,
devicetree, linux-kernel, linux-omap, Pali Rohár,
Ивайло Димитров,
Joni Lapilainen, Aaro Koskinen, Sebastian Reichel
In-Reply-To: <1393199401-27197-1-git-send-email-sre@debian.org>
Fix return code check of alloc_chrdev_region, which
returns 0 on success.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
drivers/hsi/clients/hsi_char.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hsi/clients/hsi_char.c b/drivers/hsi/clients/hsi_char.c
index 7f64bed..f51cf45 100644
--- a/drivers/hsi/clients/hsi_char.c
+++ b/drivers/hsi/clients/hsi_char.c
@@ -706,7 +706,7 @@ static int hsc_probe(struct device *dev)
if (!hsc_major) {
ret = alloc_chrdev_region(&hsc_dev, hsc_baseminor,
HSC_DEVS, devname);
- if (ret > 0)
+ if (ret == 0)
hsc_major = MAJOR(hsc_dev);
} else {
hsc_dev = MKDEV(hsc_major, hsc_baseminor);
--
1.8.5.3
^ permalink raw reply related
* [PATCHv1 3/6] HSI: hsi-char: add Device Tree support
From: Sebastian Reichel @ 2014-02-23 23:49 UTC (permalink / raw)
To: Sebastian Reichel, Linus Walleij, Shubhrajyoti Datta,
Carlos Chinea
Cc: Tony Lindgren, Grant Likely, Rob Herring, Pawel Moll,
Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley,
devicetree, linux-kernel, linux-omap, Pali Rohár,
Ивайло Димитров,
Joni Lapilainen, Aaro Koskinen, Sebastian Reichel
In-Reply-To: <1393199401-27197-1-git-send-email-sre@debian.org>
Add of_match_table to hsi_char driver, so that it can
be referenced from Device Tree.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
drivers/hsi/clients/hsi_char.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/hsi/clients/hsi_char.c b/drivers/hsi/clients/hsi_char.c
index e61e5f9..7f64bed 100644
--- a/drivers/hsi/clients/hsi_char.c
+++ b/drivers/hsi/clients/hsi_char.c
@@ -42,6 +42,7 @@
#include <linux/stat.h>
#include <linux/hsi/hsi.h>
#include <linux/hsi/hsi_char.h>
+#include <linux/of_device.h>
#define HSC_DEVS 16 /* Num of channels */
#define HSC_MSGS 4
@@ -758,12 +759,22 @@ static int hsc_remove(struct device *dev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id hsi_char_of_match[] = {
+ { .compatible = "ssi-char", },
+ { .compatible = "hsi-char", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hsi_char_of_match);
+#endif
+
static struct hsi_client_driver hsc_driver = {
.driver = {
.name = "hsi_char",
.owner = THIS_MODULE,
.probe = hsc_probe,
.remove = hsc_remove,
+ .of_match_table = of_match_ptr(hsi_char_of_match),
},
};
--
1.8.5.3
^ permalink raw reply related
* [PATCHv1 2/6] HSI: method to unregister clients from an hsi port
From: Sebastian Reichel @ 2014-02-23 23:49 UTC (permalink / raw)
To: Sebastian Reichel, Linus Walleij, Shubhrajyoti Datta,
Carlos Chinea
Cc: Tony Lindgren, Grant Likely, Rob Herring, Pawel Moll,
Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley,
devicetree, linux-kernel, linux-omap, Pali Rohár,
Ивайло Димитров,
Joni Lapilainen, Aaro Koskinen, Sebastian Reichel
In-Reply-To: <1393199401-27197-1-git-send-email-sre@debian.org>
This exports a method to unregister all clients from
an hsi port.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
drivers/hsi/hsi.c | 10 ++++++++++
include/linux/hsi/hsi.h | 1 +
2 files changed, 11 insertions(+)
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c
index 8bbc0f1..c7b842b 100644
--- a/drivers/hsi/hsi.c
+++ b/drivers/hsi/hsi.c
@@ -198,6 +198,16 @@ static void hsi_port_release(struct device *dev)
}
/**
+ * hsi_unregister_port - Unregister an HSI port
+ * @port: The HSI port to unregister
+ */
+void hsi_port_unregister_clients(struct hsi_port *port)
+{
+ device_for_each_child(&port->device, NULL, hsi_remove_client);
+}
+EXPORT_SYMBOL_GPL(hsi_port_unregister_clients);
+
+/**
* hsi_unregister_controller - Unregister an HSI controller
* @hsi: The HSI controller to register
*/
diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h
index fb07339..89cc6f2 100644
--- a/include/linux/hsi/hsi.h
+++ b/include/linux/hsi/hsi.h
@@ -284,6 +284,7 @@ int hsi_register_controller(struct hsi_controller *hsi);
void hsi_unregister_controller(struct hsi_controller *hsi);
void hsi_add_clients_from_dt(struct hsi_port *port,
struct device_node *clients);
+void hsi_port_unregister_clients(struct hsi_port *port);
static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi,
void *data)
--
1.8.5.3
^ permalink raw reply related
* [PATCHv1 1/6] HSI: add Device Tree support for HSI clients
From: Sebastian Reichel @ 2014-02-23 23:49 UTC (permalink / raw)
To: Sebastian Reichel, Linus Walleij, Shubhrajyoti Datta,
Carlos Chinea
Cc: Tony Lindgren, Grant Likely, Rob Herring, Pawel Moll,
Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley,
devicetree, linux-kernel, linux-omap, Pali Rohár,
Ивайло Димитров,
Joni Lapilainen, Aaro Koskinen, Sebastian Reichel
In-Reply-To: <1393199401-27197-1-git-send-email-sre@debian.org>
Add new method hsi_add_clients_from_dt, which can be used
to initialize HSI clients from a device tree node.
The patch also documents the DT binding for trivial HSI
clients.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
.../devicetree/bindings/hsi/trivial-devices.txt | 36 +++++++++++
drivers/hsi/hsi.c | 70 +++++++++++++++++++++-
include/dt-bindings/hsi/hsi.h | 17 ++++++
include/linux/hsi/hsi.h | 2 +
4 files changed, 124 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/hsi/trivial-devices.txt
create mode 100644 include/dt-bindings/hsi/hsi.h
diff --git a/Documentation/devicetree/bindings/hsi/trivial-devices.txt b/Documentation/devicetree/bindings/hsi/trivial-devices.txt
new file mode 100644
index 0000000..1ace14a
--- /dev/null
+++ b/Documentation/devicetree/bindings/hsi/trivial-devices.txt
@@ -0,0 +1,36 @@
+This is a list of trivial hsi client devices that have simple
+device tree bindings, consisting only of a compatible field
+and the optional hsi configuration.
+
+If a device needs more specific bindings, such as properties to
+describe some aspect of it, there needs to be a specific binding
+document for it just like any other devices.
+
+Optional HSI configuration properties:
+
+- hsi,mode Bit transmission mode (STREAM or FRAME)
+ The first value is used for RX and the second one for
+ TX configuration. If only one value is provided it will
+ be used for RX and TX.
+ The assignments may be found in header file
+ <dt-bindings/hsi/hsi.h>.
+- hsi,channels Number of channels to use [1..16]
+ The first value is used for RX and the second one for
+ TX configuration. If only one value is provided it will
+ be used for RX and TX.
+- hsi,speed Max bit transmission speed (Kbit/s)
+ The first value is used for RX and the second one for
+ TX configuration. If only one value is provided it will
+ be used for RX and TX.
+- hsi,flow RX flow type (SYNCHRONIZED or PIPELINE)
+ The assignments may be found in header file
+ <dt-bindings/hsi/hsi.h>.
+- hsi,arb_mode Arbitration mode for TX frame (Round robin, priority)
+ The assignments may be found in header file
+ <dt-bindings/hsi/hsi.h>.
+
+This is the list of trivial client devices:
+
+Compatible Description
+========== =============
+hsi-char HSI character device
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c
index 749f7b5..8bbc0f1 100644
--- a/drivers/hsi/hsi.c
+++ b/drivers/hsi/hsi.c
@@ -26,6 +26,8 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include "hsi_core.h"
static ssize_t modalias_show(struct device *dev,
@@ -50,7 +52,10 @@ static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
static int hsi_bus_match(struct device *dev, struct device_driver *driver)
{
- return strcmp(dev_name(dev), driver->name) == 0;
+ if (dev->of_node != NULL)
+ return of_driver_match_device(dev, driver);
+ else
+ return strcmp(dev_name(dev), driver->name) == 0;
}
static struct bus_type hsi_bus_type = {
@@ -75,6 +80,7 @@ static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info)
cl->tx_cfg = info->tx_cfg;
cl->rx_cfg = info->rx_cfg;
cl->device.bus = &hsi_bus_type;
+
cl->device.parent = &port->device;
cl->device.release = hsi_client_release;
dev_set_name(&cl->device, "%s", info->name);
@@ -101,6 +107,68 @@ static void hsi_scan_board_info(struct hsi_controller *hsi)
}
}
+static void hsi_of_get_client_cfg_property(struct device_node *client,
+ char *name, unsigned int *rx, unsigned int *tx)
+{
+ int err;
+
+ err = of_property_read_u32_index(client, name, 0, rx);
+ if (err)
+ *rx = 0;
+
+ err = of_property_read_u32_index(client, name, 1, tx);
+ if (err)
+ *tx = *rx;
+}
+
+static void hsi_add_client_from_dt(struct hsi_port *port,
+ struct device_node *client)
+{
+ struct hsi_client *cl;
+ const char *name;
+ int err;
+
+ cl = kzalloc(sizeof(*cl), GFP_KERNEL);
+ if (!cl)
+ return;
+
+ err = of_property_read_string(client, "compatible", &name);
+ if (!err) {
+ dev_set_name(&cl->device, "%s", name);
+ } else {
+ kfree(cl);
+ return;
+ }
+
+ hsi_of_get_client_cfg_property(client, "hsi,mode", &cl->rx_cfg.mode,
+ &cl->tx_cfg.mode);
+ hsi_of_get_client_cfg_property(client, "hsi,speed", &cl->rx_cfg.speed,
+ &cl->tx_cfg.speed);
+ hsi_of_get_client_cfg_property(client, "hsi,channels",
+ &cl->rx_cfg.channels, &cl->tx_cfg.channels);
+ of_property_read_u32(client, "hsi,flow", &cl->rx_cfg.flow);
+ of_property_read_u32(client, "hsi,arb_mode", &cl->tx_cfg.arb_mode);
+
+ cl->device.bus = &hsi_bus_type;
+ cl->device.parent = &port->device;
+ cl->device.release = hsi_client_release;
+ cl->device.of_node = client;
+
+ if (device_register(&cl->device) < 0) {
+ pr_err("hsi: failed to register client: %s\n", name);
+ put_device(&cl->device);
+ }
+}
+
+void hsi_add_clients_from_dt(struct hsi_port *port, struct device_node *clients)
+{
+ struct device_node *child;
+
+ for_each_available_child_of_node(clients, child)
+ hsi_add_client_from_dt(port, child);
+}
+EXPORT_SYMBOL_GPL(hsi_add_clients_from_dt);
+
static int hsi_remove_client(struct device *dev, void *data __maybe_unused)
{
device_unregister(dev);
diff --git a/include/dt-bindings/hsi/hsi.h b/include/dt-bindings/hsi/hsi.h
new file mode 100644
index 0000000..2d6b181
--- /dev/null
+++ b/include/dt-bindings/hsi/hsi.h
@@ -0,0 +1,17 @@
+/*
+ * This header provides constants for hsi client bindings.
+ */
+
+#ifndef _DT_BINDINGS_HSI_H
+#define _DT_BINDINGS_HSI_H
+
+#define HSI_MODE_STREAM 1
+#define HSI_MODE_FRAME 2
+
+#define HSI_FLOW_SYNC 0
+#define HSI_FLOW_PIPE 1
+
+#define HSI_ARB_RR 0
+#define HSI_ARB_PRIO 1
+
+#endif /* _DT_BINDINGS_HSI_H */
diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h
index 0dca785..fb07339 100644
--- a/include/linux/hsi/hsi.h
+++ b/include/linux/hsi/hsi.h
@@ -282,6 +282,8 @@ struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags);
void hsi_put_controller(struct hsi_controller *hsi);
int hsi_register_controller(struct hsi_controller *hsi);
void hsi_unregister_controller(struct hsi_controller *hsi);
+void hsi_add_clients_from_dt(struct hsi_port *port,
+ struct device_node *clients);
static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi,
void *data)
--
1.8.5.3
^ permalink raw reply related
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