* Re: [PATCH] PM / Domains: Fix compatible for domain idle state
From: Rob Herring @ 2016-12-08 16:07 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Ulf Hansson, Lina Iyer, Kevin Hilman, linux-pm@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, Andy Gross, Stephen Boyd,
linux-arm-msm@vger.kernel.org, Brendan Jackman, Lorenzo Pieralisi,
Sudeep Holla, Juri Lelli, devicetree@vger.kernel.org
In-Reply-To: <1526421.aCsca4sLnR@aspire.rjw.lan>
On Wed, Dec 7, 2016 at 6:13 PM, Rafael J. Wysocki <rjw@rjwysocki.net> wrote:
> On Thursday, December 01, 2016 10:21:59 PM Rafael J. Wysocki wrote:
>> On Tuesday, November 29, 2016 09:47:03 AM Ulf Hansson wrote:
>> > On 10 November 2016 at 20:58, Rob Herring <robh@kernel.org> wrote:
>> > > On Mon, Nov 07, 2016 at 12:14:28PM +0100, Ulf Hansson wrote:
>> > >> On 3 November 2016 at 22:54, Lina Iyer <lina.iyer@linaro.org> wrote:
>> > >> > Re-using idle state definition provided by arm,idle-state for domain
>> > >> > idle states creates a lot of confusion and limits further evolution of
>> > >> > the domain idle definition. To keep things clear and simple, define a
>> > >> > idle states for domain using a new compatible "domain-idle-state".
>> > >> >
>> > >> > Fix existing PM domains code to look for the newly defined compatible.
>> > >> >
>> > >> > Cc: <devicetree@vger.kernel.org>
>> > >> > Cc: Rob Herring <robh@kernel.org>
>> > >> > Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> > >> > ---
>> > >> > .../bindings/power/domain-idle-state.txt | 33 ++++++++++++++++++++++
>> > >> > .../devicetree/bindings/power/power_domain.txt | 8 +++---
>> > >> > drivers/base/power/domain.c | 2 +-
>> > >> > 3 files changed, 38 insertions(+), 5 deletions(-)
>> > >> > create mode 100644 Documentation/devicetree/bindings/power/domain-idle-state.txt
>> > >> >
>> > >> > diff --git a/Documentation/devicetree/bindings/power/domain-idle-state.txt b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> > >> > new file mode 100644
>> > >> > index 0000000..eefc7ed
>> > >> > --- /dev/null
>> > >> > +++ b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> > >> > @@ -0,0 +1,33 @@
>> > >> > +PM Domain Idle State Node:
>> > >> > +
>> > >> > +A domain idle state node represents the state parameters that will be used to
>> > >> > +select the state when there are no active components in the domain.
>> > >> > +
>> > >> > +The state node has the following parameters -
>> > >> > +
>> > >> > +- compatible:
>> > >> > + Usage: Required
>> > >> > + Value type: <string>
>> > >> > + Definition: Must be "domain-idle-state".
>> > >> > +
>> > >> > +- entry-latency-us
>> > >> > + Usage: Required
>> > >> > + Value type: <prop-encoded-array>
>> > >> > + Definition: u32 value representing worst case latency in
>> > >> > + microseconds required to enter the idle state.
>> > >> > + The exit-latency-us duration may be guaranteed
>> > >> > + only after entry-latency-us has passed.
>> > >>
>> > >> As we anyway are going to change this, why not use an u64 and have the
>> > >> value in ns instead of us?
>> > >
>> > > I can't imagine that you would need more resolution or range. For times
>> > > less than 1us, s/w and register access times are going to dominate the
>> > > time.
>> > >
>> > > Unless there is a real need, I'd keep alignment with the existing
>> > > binding.
>> >
>> > Rob, are you fine with this? I thought it would be great to get this
>> > in for 4.10 rc1.
>>
>> Rob, any objections here?
>
> Well, no objections, so applied.
Sorry, just found my ack sitting in my drafts. Thought I had sent it.
Rob
^ permalink raw reply
* Re: [PATCH v2 1/2] devicetree: i2c-hid: Add Wacom digitizer + regulator support
From: Rob Herring @ 2016-12-08 16:03 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Brian Norris, Jiri Kosina, Doug Anderson,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
open list:ARM/Rockchip SoC...,
linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Dmitry Torokhov, Caesar Wang
In-Reply-To: <20161208154145.GA30888-/m+UfqrgI5QNLKR9yMNcA1aTQe2KTcn/@public.gmane.org>
On Thu, Dec 8, 2016 at 9:41 AM, Benjamin Tissoires
<benjamin.tissoires-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
> On Dec 06 2016 or thereabouts, Doug Anderson wrote:
>> Hi,
>>
>> On Tue, Dec 6, 2016 at 6:56 AM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>> > On Tue, Dec 6, 2016 at 2:48 AM, Benjamin Tissoires
>> > <benjamin.tissoires-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>> >> On Dec 05 2016 or thereabouts, Rob Herring wrote:
>> >>> On Thu, Dec 01, 2016 at 09:24:50AM -0800, Brian Norris wrote:
>> >>> > Hi Benjamin and Rob,
>> >>> >
>> >>> > On Thu, Dec 01, 2016 at 03:34:34PM +0100, Benjamin Tissoires wrote:
>> >>> > > On Nov 30 2016 or thereabouts, Brian Norris wrote:
>> >>> > > > From: Caesar Wang <wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> >>> > > >
>> >>> > > > Add a compatible string and regulator property for Wacom W9103
>> >>> > > > digitizer. Its VDD supply may need to be enabled before using it.
>> >>> > > >
>> >>> > > > Signed-off-by: Caesar Wang <wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> >>> > > > Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> >>> > > > Cc: Jiri Kosina <jikos-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> >>> > > > Cc: linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> >>> > > > Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
>> >>> > > > ---
>> >>> > > > v1 was a few months back. I finally got around to rewriting it based on
>> >>> > > > DT binding feedback.
>> >>> > > >
>> >>> > > > v2:
>> >>> > > > * add compatible property for wacom
>> >>> > > > * name the regulator property specifically (VDD)
>> >>> > > >
>> >>> > > > Documentation/devicetree/bindings/input/hid-over-i2c.txt | 6 +++++-
>> >>> > > > 1 file changed, 5 insertions(+), 1 deletion(-)
>> >>> > > >
>> >>> > > > diff --git a/Documentation/devicetree/bindings/input/hid-over-i2c.txt b/Documentation/devicetree/bindings/input/hid-over-i2c.txt
>> >>> > > > index 488edcb264c4..eb98054e60c9 100644
>> >>> > > > --- a/Documentation/devicetree/bindings/input/hid-over-i2c.txt
>> >>> > > > +++ b/Documentation/devicetree/bindings/input/hid-over-i2c.txt
>> >>> > > > @@ -11,12 +11,16 @@ If this binding is used, the kernel module i2c-hid will handle the communication
>> >>> > > > with the device and the generic hid core layer will handle the protocol.
>> >>> > > >
>> >>> > > > Required properties:
>> >>> > > > -- compatible: must be "hid-over-i2c"
>> >>> > > > +- compatible: must be "hid-over-i2c", or a device-specific string like:
>> >>> > > > + * "wacom,w9013"
>> >>> > >
>> >>> > > NACK on this one.
>> >>> > >
>> >>> > > After re-reading the v1 submission I realized Rob asked for this change,
>> >>> > > but I strongly disagree.
>> >>> > >
>> >>> > > HID over I2C is a generic protocol, in the same way HID over USB is. We
>> >>> > > can not start adding device specifics here, this is opening the can of
>> >>> > > worms. If the device is a HID one, nothing else should matter. The rest
>> >>> > > (description of the device, name, etc...) is all provided by the
>> >>> > > protocol.
>> >>> >
>> >>> > I should have spoken up when Rob made the suggestion, because I more or
>> >>> > less agree with Benjamin here. I don't really see why this needs to have
>> >>> > a specialized compatible string, as the property is still fairly
>> >>> > generic, and the entire device handling is via a generic protocol. The
>> >>> > fact that we manage its power via a regulator is not very
>> >>> > device-specific.
>> >>>
>> >>> It doesn't matter that the protocol is generic. The device attached and
>> >>> the implementation is not. Implementations have been known to have
>> >>> bugs/quirks (generally speaking, not HID over I2C in particular). There
>> >>> are also things outside the scope of what is 'hid-over-i2c' like what's
>> >>> needed to power-on the device which this patch clearly show.
>> >>
>> >> Yes, there are bugs, quirks, even with HID. But the HID declares within
>> >> the protocol the Vendor ID and the Product ID, which means once we pass
>> >> the initial "device is ready" step and can do a single i2c write/read,
>> >> we don't give a crap about device tree anymore.
>> >>
>> >> This is just about setting the device in shape so that it can answer a
>> >> single write/read.
>> >>
>> >>>
>> >>> This is no different than a panel attached via LVDS, eDP, etc., or
>> >>> USB/PCIe device hard-wired on a board. They all use standard protocols
>> >>> and all need additional data to describe them. Of course, adding a
>> >>> single property for a delay would not be a big deal, but it's never
>> >>> ending. Next you need multiple supplies, GPIO controls, mutiple
>> >>> delays... This has been discussed to death already. As Thierry Reding
>> >>> said, you're not special[1].
>> >>
>> >> I can somewhat understand what you mean. The official specification is
>> >> for ACPI. And ACPI allows to calls various settings while querying the
>> >> _STA method for instance. So in the ACPI world, we don't need to care
>> >> about regulators or GPIOs because the OEM deals with this in its own
>> >> blob.
>> >>
>> >> Now, coming back to our issue. We are not special, maybe, if he says so.
>> >> But this really feels like a design choice between putting the burden on
>> >> device tree and OEMs or in the module maintainers. And I'd rather have
>> >> the OEM deal with their device than me having to update the module for
>> >> each generations of hardware. Indeed, this looks like an "endless"
>> >> amount of quirks, but I'd rather have this endless amount of quirks than
>> >> having to maintain an endless amount of list of new devices that behaves
>> >> the same way. We are talking here about "wacom,w9013", but then comes
>> >> "wacom,w9014" and we need to upgrade the kernel.
>> >
>> > No. If the w9014 can claim compatibility with then w9013, then you
>> > don't need a kernel change. The DT should list the w9014 AND w9013,
>> > but the kernel only needs to know about the w9013. That is until there
>> > is some difference which is why the DT should list w9014 to start
>> > with.
>> >
>> > If you don't have any power control to deal with, then the kernel can
>> > always just match on "hid-over-i2c" compatible.
>>
>> Just my $0.02. Feel free to ignore.
>>
>> One thought is that I would say that the need to power on the device
>> explicitly seems more like a board level difference and less like a
>> difference associated with a particular digitizer. Said another way,
>> it seems likely there will be boards with a w9013 without explicit
>> control of the regulator in software and it seems like there will be
>> boards with other digitizers where suddenly a new board will come out
>> that needs explicit control of the regulator.
>>
>> In this particular case I feel like we can draw a lot of parallels to
>> an SDIO bus.
>>
>> When you specify an SDIO bus you don't specify what kind of card will
>> be present, you just say "I've got an SDIO bus" and then the specific
>> device underneath is probed. Here we've say "I've got an i2c
>> connection intended for HID" and then you probe for the HID device
>> that's on the connection.
>>
>> Also for an SDIO bus, you've possibly got a regulators / GPIOs /
>> resets that need to be controlled, but the specific details of these
>> regulator / GPIOs / resets are specific to a given board and not
>> necessarily a given SDIO device.
>>
>
> Thanks Doug for this. I had the feeling this wasn't right, but you
> actually managed to put the words on it. If it's a board problem (if
> you switch the wacom device with an other HID over I2C device and you
> still need the same regulator/timing parameters), then this should
> simply be mentioned on the patch.
>
> So Brian, could you please respin the series and remve the Wacom
> mentions and explain that it is required for the board itself?
In advance, NAK.
This is not how DT works. Either this binding needs a Wacom compatible
or don't use DT.
Rob
^ permalink raw reply
* Re: [PATCH v2 1/2] devicetree: i2c-hid: Add Wacom digitizer + regulator support
From: Rob Herring @ 2016-12-08 16:01 UTC (permalink / raw)
To: Doug Anderson
Cc: Benjamin Tissoires, Brian Norris, Jiri Kosina, Caesar Wang,
open list:ARM/Rockchip SoC...,
linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Dmitry Torokhov, Mark Rutland
In-Reply-To: <CAD=FV=UMonRe8WJ1feDv3Nh9g8hEGAwwFT5NYLbzXADBqhTf-A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Tue, Dec 6, 2016 at 10:18 AM, Doug Anderson <dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote:
> Hi,
>
> On Tue, Dec 6, 2016 at 6:56 AM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>> On Tue, Dec 6, 2016 at 2:48 AM, Benjamin Tissoires
>> <benjamin.tissoires-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>>> On Dec 05 2016 or thereabouts, Rob Herring wrote:
>>>> On Thu, Dec 01, 2016 at 09:24:50AM -0800, Brian Norris wrote:
>>>> > Hi Benjamin and Rob,
>>>> >
>>>> > On Thu, Dec 01, 2016 at 03:34:34PM +0100, Benjamin Tissoires wrote:
>>>> > > On Nov 30 2016 or thereabouts, Brian Norris wrote:
>>>> > > > From: Caesar Wang <wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>>> > > >
>>>> > > > Add a compatible string and regulator property for Wacom W9103
>>>> > > > digitizer. Its VDD supply may need to be enabled before using it.
>>>> > > >
>>>> > > > Signed-off-by: Caesar Wang <wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>>> > > > Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>>>> > > > Cc: Jiri Kosina <jikos-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>>>> > > > Cc: linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>>> > > > Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
>>>> > > > ---
>>>> > > > v1 was a few months back. I finally got around to rewriting it based on
>>>> > > > DT binding feedback.
>>>> > > >
>>>> > > > v2:
>>>> > > > * add compatible property for wacom
>>>> > > > * name the regulator property specifically (VDD)
>>>> > > >
>>>> > > > Documentation/devicetree/bindings/input/hid-over-i2c.txt | 6 +++++-
>>>> > > > 1 file changed, 5 insertions(+), 1 deletion(-)
>>>> > > >
>>>> > > > diff --git a/Documentation/devicetree/bindings/input/hid-over-i2c.txt b/Documentation/devicetree/bindings/input/hid-over-i2c.txt
>>>> > > > index 488edcb264c4..eb98054e60c9 100644
>>>> > > > --- a/Documentation/devicetree/bindings/input/hid-over-i2c.txt
>>>> > > > +++ b/Documentation/devicetree/bindings/input/hid-over-i2c.txt
>>>> > > > @@ -11,12 +11,16 @@ If this binding is used, the kernel module i2c-hid will handle the communication
>>>> > > > with the device and the generic hid core layer will handle the protocol.
>>>> > > >
>>>> > > > Required properties:
>>>> > > > -- compatible: must be "hid-over-i2c"
>>>> > > > +- compatible: must be "hid-over-i2c", or a device-specific string like:
>>>> > > > + * "wacom,w9013"
>>>> > >
>>>> > > NACK on this one.
>>>> > >
>>>> > > After re-reading the v1 submission I realized Rob asked for this change,
>>>> > > but I strongly disagree.
>>>> > >
>>>> > > HID over I2C is a generic protocol, in the same way HID over USB is. We
>>>> > > can not start adding device specifics here, this is opening the can of
>>>> > > worms. If the device is a HID one, nothing else should matter. The rest
>>>> > > (description of the device, name, etc...) is all provided by the
>>>> > > protocol.
>>>> >
>>>> > I should have spoken up when Rob made the suggestion, because I more or
>>>> > less agree with Benjamin here. I don't really see why this needs to have
>>>> > a specialized compatible string, as the property is still fairly
>>>> > generic, and the entire device handling is via a generic protocol. The
>>>> > fact that we manage its power via a regulator is not very
>>>> > device-specific.
>>>>
>>>> It doesn't matter that the protocol is generic. The device attached and
>>>> the implementation is not. Implementations have been known to have
>>>> bugs/quirks (generally speaking, not HID over I2C in particular). There
>>>> are also things outside the scope of what is 'hid-over-i2c' like what's
>>>> needed to power-on the device which this patch clearly show.
>>>
>>> Yes, there are bugs, quirks, even with HID. But the HID declares within
>>> the protocol the Vendor ID and the Product ID, which means once we pass
>>> the initial "device is ready" step and can do a single i2c write/read,
>>> we don't give a crap about device tree anymore.
>>>
>>> This is just about setting the device in shape so that it can answer a
>>> single write/read.
>>>
>>>>
>>>> This is no different than a panel attached via LVDS, eDP, etc., or
>>>> USB/PCIe device hard-wired on a board. They all use standard protocols
>>>> and all need additional data to describe them. Of course, adding a
>>>> single property for a delay would not be a big deal, but it's never
>>>> ending. Next you need multiple supplies, GPIO controls, mutiple
>>>> delays... This has been discussed to death already. As Thierry Reding
>>>> said, you're not special[1].
>>>
>>> I can somewhat understand what you mean. The official specification is
>>> for ACPI. And ACPI allows to calls various settings while querying the
>>> _STA method for instance. So in the ACPI world, we don't need to care
>>> about regulators or GPIOs because the OEM deals with this in its own
>>> blob.
>>>
>>> Now, coming back to our issue. We are not special, maybe, if he says so.
>>> But this really feels like a design choice between putting the burden on
>>> device tree and OEMs or in the module maintainers. And I'd rather have
>>> the OEM deal with their device than me having to update the module for
>>> each generations of hardware. Indeed, this looks like an "endless"
>>> amount of quirks, but I'd rather have this endless amount of quirks than
>>> having to maintain an endless amount of list of new devices that behaves
>>> the same way. We are talking here about "wacom,w9013", but then comes
>>> "wacom,w9014" and we need to upgrade the kernel.
>>
>> No. If the w9014 can claim compatibility with then w9013, then you
>> don't need a kernel change. The DT should list the w9014 AND w9013,
>> but the kernel only needs to know about the w9013. That is until there
>> is some difference which is why the DT should list w9014 to start
>> with.
>>
>> If you don't have any power control to deal with, then the kernel can
>> always just match on "hid-over-i2c" compatible.
>
> Just my $0.02. Feel free to ignore.
>
> One thought is that I would say that the need to power on the device
> explicitly seems more like a board level difference and less like a
> difference associated with a particular digitizer. Said another way,
> it seems likely there will be boards with a w9013 without explicit
> control of the regulator in software and it seems like there will be
> boards with other digitizers where suddenly a new board will come out
> that needs explicit control of the regulator.
Then either the regulator is optional or you don't say it is a w9013
for that board. But if you do need to initialize the device and
therefore know what type of device it is, then you need a compatible
for the device. It's when things really vary by board that we add DT
properties.
> In this particular case I feel like we can draw a lot of parallels to
> an SDIO bus.
>
> When you specify an SDIO bus you don't specify what kind of card will
> be present, you just say "I've got an SDIO bus" and then the specific
> device underneath is probed. Here we've say "I've got an i2c
> connection intended for HID" and then you probe for the HID device
> that's on the connection.
No, the soldered down devices require all sorts of extra non-SDIO
connections and we do specify the device in those cases.
> Also for an SDIO bus, you've possibly got a regulators / GPIOs /
> resets that need to be controlled, but the specific details of these
> regulator / GPIOs / resets are specific to a given board and not
> necessarily a given SDIO device.
It's both. The device defines what is needed and the specs to control
them (active states of GPIOs, de/assertion times of resets, supply
voltages, etc.). The board only determines what the connections are
and if you can control them.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] i2c: rk3x: keep i2c irq ON in suspend
From: David.Wu @ 2016-12-08 15:50 UTC (permalink / raw)
To: Grygorii Strashko, Doug Anderson
Cc: Heiko Stuebner, Wolfram Sang,
linux-arm-kernel@lists.infradead.org,
open list:ARM/Rockchip SoC..., linux-i2c@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <e34bab0d-b4b1-ff0d-3a27-c3701b8206c1@ti.com>
Hi Grygorii, Doug and Heiko,
Thanks for your replies.
I will do 2 steps:
1. Add "suspended" flag in suspend_noirq()/resume_noirq() callback to
prevent new i2c started, and use i2c_lock_adapter() to wait for current
i2c transfer finished.
2. IRQF_NO_SUSPEND added could make i2c work well during the time
between suspend_device_irqs() and i2c_suspend_noirq() callback. In the
other side, it is the the time between resume_device_irqs() and
i2c_resume_noirq() callback.
If any i2c client try to access I2C after suspend_noirq() or before
resume_noirq() callback, print the warning, and they should fix it, not
to start i2c access and the moment.
在 2016/12/8 0:27, Grygorii Strashko 写道:
>
>
> On 12/06/2016 09:37 PM, David.Wu wrote:
>> Hi Doug,
>>
>> 在 2016/12/7 0:31, Doug Anderson 写道:
>>> Hi,
>>>
>>> On Tue, Dec 6, 2016 at 12:12 AM, David.Wu <david.wu@rock-chips.com>
>>> wrote:
>>>> Hi Heiko,
>>>>
>>>> 在 2016/12/5 18:54, Heiko Stuebner 写道:
>>>>>
>>>>> Hi David,
>>>>>
>>>>> Am Montag, 5. Dezember 2016, 16:02:59 CET schrieb David Wu:
>>>>>>
>>>>>> During suspend there may still be some i2c access happening.
>>>>>> And if we don't keep i2c irq ON, there may be i2c access timeout if
>>>>>> i2c is in irq mode of operation.
>>>>>
>>>>>
>>>>> can you describe the issue you're trying to fix a bit more please?
>>>>
>>>>
>>>> Sometimes we could see the i2c timeout errors during suspend/resume,
>>>> which
>>>> makes the duration of suspend/resume too longer.
>>>>
>>>> [ 484.171541] CPU4: Booted secondary processor [410fd082]
>>>> [ 485.172777] rk3x-i2c ff3c0000.i2c: timeout, ipd: 0x10, state: 1
>>>> [ 486.172760] rk3x-i2c ff3c0000.i2c: timeout, ipd: 0x10, state: 1
>>>> [ 487.172759] rk3x-i2c ff3c0000.i2c: timeout, ipd: 0x10, state: 1
>>>> [ 487.172840] cpu cpu4: _set_opp_voltage: failed to set voltage (800000
>>>> 800000 800000 mV): -110
>>>> [ 487.172874] cpu cpu4: failed to set volt 800000
>>>>
>>>>>
>>>>> I.e. I'd think the i2c-core does suspend i2c-client devices first,
>>>>> so that
>>>>> these should be able to finish up their ongoing transfers and not start
>>>>> any
>>>>> new ones instead?
>>>>>
>>>>> Your irq can still happen slightly after the system started going to
>>>>> actually
>>>>> sleep, so to me it looks like you just widened the window where irqs
>>>>> can
>>>>> be
>>>>> handled. Especially as your irq could also just simply stem from the
>>>>> start
>>>>> state, so you cannot even be sure if your transaction actually is
>>>>> finished.
>>>>
>>>>
>>>> Okay, you are right. I want to give it a double insurance at first,
>>>> but it
>>>> may hide the unhappend issue.
>>>>
>>>>>
>>>>> So to me it looks like the i2c-connected device driver should be fixed
>>>>> instead?
>>>>
>>>>
>>>> I tell them to fix it in rk808 driver.
>>>
>>> To me it seems like perhaps cpufreq should not be changing frequencies
>>> until it is resumed properly. Presumably if all the ordering is done
>>> right then cpufreq should be resumed _after_ the i2c regulator so you
>>> should be OK. ...or am I somehow confused about that?
>>
>> yes,the cpufreq and regulator should start i2c job after they resume
>> properly.
>>
>>>
>>> Also note that previous i2c busses I worked with simply returned -EIO
>>> in the case where they were called when suspended. See
>>> "i2c-exynos5.c" and "i2c-s3c2410.c".
>>
>> In "i2c-exynos5.c", it seems that using the "i2c->suspended" to protect
>> i2c transfer works most of the time. Of course it could prevent the next
>> new i2c transfer to start. But in one case, if the current i2c job was
>> not finished until the i2c irq was disabled by system suspend, the i2c
>> timeout error would also happen, as the current i2c job may have a large
>> data to transfer and it lasts from a long time.
>
> And this means you have bug in some of I2C client drivers which do not stop
> their activities during suspend properly (most usual case - driver uses work
> and this work still active during suspend and can run on one CPU while suspend
> runs on another).
>
> At the moment .suspend_noirq() callback is called there should be no active
> I2C transactions in general.
>
>>
>> So is it necessary to add a mutex lock to wait the current job to be
>> finished before the "i2c->suspended" is changed in i2c_suspend_noirq()?
>>
>
> You need to catch and fix all driver who will try to access I2C after your
> I2C bus driver passes suspend_noirq stage. Smth, like [1], uses i2c_lock_adapter().
>
>
> [1] https://git.ti.com/android-sdk/kernel-omap/commit/125ef8f7016e7b205886f93862288a45a312b1d8
>
^ permalink raw reply
* Re: [PATCH 1/2] Documentation: sample averaging for imx6ul_tsc
From: Rob Herring @ 2016-12-08 15:45 UTC (permalink / raw)
To: Guy Shapiro
Cc: Fabio Estevam, Mark Rutland, devicetree@vger.kernel.org,
Haibo Chen, Dmitry Torokhov, linux-input@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <82b04890-692d-4e22-36c1-a21affad5126@mobi-wize.com>
On Thu, Dec 8, 2016 at 9:15 AM, Guy Shapiro <guy.shapiro@mobi-wize.com> wrote:
> On 01/12/2016 18:13, Rob Herring wrote:
>>
>> On Sun, Nov 27, 2016 at 09:44:57AM +0200, Guy Shapiro wrote:
>>>
>>> The i.MX6UL internal touchscreen controller contains an option to
>>> average upon samples. This feature reduces noise from the produced
>>> touch locations.
>>>
>>> This patch introduces a new device tree optional property for this
>>> feature. It provides control over the amount of averaged samples per
>>> touch event.
>>>
>>> The property was inspired by a similar property on the
>>> "brcm,iproc-touchscreen" binding.
>>>
>>> Signed-off-by: Guy Shapiro <guy.shapiro@mobi-wize.com>
>>> ---
>>> .../devicetree/bindings/input/touchscreen/imx6ul_tsc.txt | 8
>>
>> ++++++++
>>>
>>> 1 file changed, 8 insertions(+)
>>>
>>> diff --git
>>
>> a/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt
>> b/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt
>>>
>>> index 853dff9..a66069f 100644
>>> --- a/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt
>>> +++ b/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt
>>> @@ -17,6 +17,13 @@ Optional properties:
>>> This value depends on the touch screen.
>>> - pre-charge-time: the touch screen need some time to precharge.
>>> This value depends on the touch screen.
>>> +- average-samples: Number of data samples which are averaged for each
>>
>> read.
>>>
>>> + Valid values 0-4
>>> + 0 = 1 sample
>>> + 1 = 4 samples
>>> + 2 = 8 samples
>>> + 3 = 16 samples
>>> + 4 = 32 samples
>>
>> Either this needs a vendor prefix or should be documented as a generic
>> property. In the latter case, you should use actual number of samples
>> (1-32) for the values.
>
> In the term "generic property", do you mean to document it on
> bindings/input/touchscreen/touchscreen.txt ?
Yes.
> If so, should I add the "touchscreen-" prefix like all the other properties
> in that file?
Yes.
> Grepping bindings/input/touchscreen/, I found two other device drivers that
> implement a
> similar property - "ti-tsc-adc" and "brcm,iproc-touchscreen" (The latter,
> BTW, uses a non
> vendor prefixed property name).
Unfortunately, the brcm one doesn't look directly usable.
> Do we want to move from per-vendor properties to a generic one?
No. Those are set already. I just don't want to get more vendor properties.
Rob
^ permalink raw reply
* Re: [PATCH v2 1/2] devicetree: i2c-hid: Add Wacom digitizer + regulator support
From: Benjamin Tissoires @ 2016-12-08 15:41 UTC (permalink / raw)
To: Doug Anderson
Cc: Rob Herring, Brian Norris, Jiri Kosina, Caesar Wang,
open list:ARM/Rockchip SoC...,
linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Dmitry Torokhov, Mark Rutland
In-Reply-To: <CAD=FV=UMonRe8WJ1feDv3Nh9g8hEGAwwFT5NYLbzXADBqhTf-A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Dec 06 2016 or thereabouts, Doug Anderson wrote:
> Hi,
>
> On Tue, Dec 6, 2016 at 6:56 AM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> > On Tue, Dec 6, 2016 at 2:48 AM, Benjamin Tissoires
> > <benjamin.tissoires-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
> >> On Dec 05 2016 or thereabouts, Rob Herring wrote:
> >>> On Thu, Dec 01, 2016 at 09:24:50AM -0800, Brian Norris wrote:
> >>> > Hi Benjamin and Rob,
> >>> >
> >>> > On Thu, Dec 01, 2016 at 03:34:34PM +0100, Benjamin Tissoires wrote:
> >>> > > On Nov 30 2016 or thereabouts, Brian Norris wrote:
> >>> > > > From: Caesar Wang <wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> >>> > > >
> >>> > > > Add a compatible string and regulator property for Wacom W9103
> >>> > > > digitizer. Its VDD supply may need to be enabled before using it.
> >>> > > >
> >>> > > > Signed-off-by: Caesar Wang <wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> >>> > > > Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> >>> > > > Cc: Jiri Kosina <jikos-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> >>> > > > Cc: linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> >>> > > > Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> >>> > > > ---
> >>> > > > v1 was a few months back. I finally got around to rewriting it based on
> >>> > > > DT binding feedback.
> >>> > > >
> >>> > > > v2:
> >>> > > > * add compatible property for wacom
> >>> > > > * name the regulator property specifically (VDD)
> >>> > > >
> >>> > > > Documentation/devicetree/bindings/input/hid-over-i2c.txt | 6 +++++-
> >>> > > > 1 file changed, 5 insertions(+), 1 deletion(-)
> >>> > > >
> >>> > > > diff --git a/Documentation/devicetree/bindings/input/hid-over-i2c.txt b/Documentation/devicetree/bindings/input/hid-over-i2c.txt
> >>> > > > index 488edcb264c4..eb98054e60c9 100644
> >>> > > > --- a/Documentation/devicetree/bindings/input/hid-over-i2c.txt
> >>> > > > +++ b/Documentation/devicetree/bindings/input/hid-over-i2c.txt
> >>> > > > @@ -11,12 +11,16 @@ If this binding is used, the kernel module i2c-hid will handle the communication
> >>> > > > with the device and the generic hid core layer will handle the protocol.
> >>> > > >
> >>> > > > Required properties:
> >>> > > > -- compatible: must be "hid-over-i2c"
> >>> > > > +- compatible: must be "hid-over-i2c", or a device-specific string like:
> >>> > > > + * "wacom,w9013"
> >>> > >
> >>> > > NACK on this one.
> >>> > >
> >>> > > After re-reading the v1 submission I realized Rob asked for this change,
> >>> > > but I strongly disagree.
> >>> > >
> >>> > > HID over I2C is a generic protocol, in the same way HID over USB is. We
> >>> > > can not start adding device specifics here, this is opening the can of
> >>> > > worms. If the device is a HID one, nothing else should matter. The rest
> >>> > > (description of the device, name, etc...) is all provided by the
> >>> > > protocol.
> >>> >
> >>> > I should have spoken up when Rob made the suggestion, because I more or
> >>> > less agree with Benjamin here. I don't really see why this needs to have
> >>> > a specialized compatible string, as the property is still fairly
> >>> > generic, and the entire device handling is via a generic protocol. The
> >>> > fact that we manage its power via a regulator is not very
> >>> > device-specific.
> >>>
> >>> It doesn't matter that the protocol is generic. The device attached and
> >>> the implementation is not. Implementations have been known to have
> >>> bugs/quirks (generally speaking, not HID over I2C in particular). There
> >>> are also things outside the scope of what is 'hid-over-i2c' like what's
> >>> needed to power-on the device which this patch clearly show.
> >>
> >> Yes, there are bugs, quirks, even with HID. But the HID declares within
> >> the protocol the Vendor ID and the Product ID, which means once we pass
> >> the initial "device is ready" step and can do a single i2c write/read,
> >> we don't give a crap about device tree anymore.
> >>
> >> This is just about setting the device in shape so that it can answer a
> >> single write/read.
> >>
> >>>
> >>> This is no different than a panel attached via LVDS, eDP, etc., or
> >>> USB/PCIe device hard-wired on a board. They all use standard protocols
> >>> and all need additional data to describe them. Of course, adding a
> >>> single property for a delay would not be a big deal, but it's never
> >>> ending. Next you need multiple supplies, GPIO controls, mutiple
> >>> delays... This has been discussed to death already. As Thierry Reding
> >>> said, you're not special[1].
> >>
> >> I can somewhat understand what you mean. The official specification is
> >> for ACPI. And ACPI allows to calls various settings while querying the
> >> _STA method for instance. So in the ACPI world, we don't need to care
> >> about regulators or GPIOs because the OEM deals with this in its own
> >> blob.
> >>
> >> Now, coming back to our issue. We are not special, maybe, if he says so.
> >> But this really feels like a design choice between putting the burden on
> >> device tree and OEMs or in the module maintainers. And I'd rather have
> >> the OEM deal with their device than me having to update the module for
> >> each generations of hardware. Indeed, this looks like an "endless"
> >> amount of quirks, but I'd rather have this endless amount of quirks than
> >> having to maintain an endless amount of list of new devices that behaves
> >> the same way. We are talking here about "wacom,w9013", but then comes
> >> "wacom,w9014" and we need to upgrade the kernel.
> >
> > No. If the w9014 can claim compatibility with then w9013, then you
> > don't need a kernel change. The DT should list the w9014 AND w9013,
> > but the kernel only needs to know about the w9013. That is until there
> > is some difference which is why the DT should list w9014 to start
> > with.
> >
> > If you don't have any power control to deal with, then the kernel can
> > always just match on "hid-over-i2c" compatible.
>
> Just my $0.02. Feel free to ignore.
>
> One thought is that I would say that the need to power on the device
> explicitly seems more like a board level difference and less like a
> difference associated with a particular digitizer. Said another way,
> it seems likely there will be boards with a w9013 without explicit
> control of the regulator in software and it seems like there will be
> boards with other digitizers where suddenly a new board will come out
> that needs explicit control of the regulator.
>
> In this particular case I feel like we can draw a lot of parallels to
> an SDIO bus.
>
> When you specify an SDIO bus you don't specify what kind of card will
> be present, you just say "I've got an SDIO bus" and then the specific
> device underneath is probed. Here we've say "I've got an i2c
> connection intended for HID" and then you probe for the HID device
> that's on the connection.
>
> Also for an SDIO bus, you've possibly got a regulators / GPIOs /
> resets that need to be controlled, but the specific details of these
> regulator / GPIOs / resets are specific to a given board and not
> necessarily a given SDIO device.
>
Thanks Doug for this. I had the feeling this wasn't right, but you
actually managed to put the words on it. If it's a board problem (if
you switch the wacom device with an other HID over I2C device and you
still need the same regulator/timing parameters), then this should
simply be mentioned on the patch.
So Brian, could you please respin the series and remve the Wacom
mentions and explain that it is required for the board itself?
Cheers,
Benjamin
--
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 6/6] net: smmac: allow configuring lower pbl values
From: David Miller @ 2016-12-08 15:18 UTC (permalink / raw)
To: alexandre.torgue
Cc: niklas.cassel, robh+dt, mark.rutland, corbet, peppe.cavallaro,
preid, eric, pavel, afaerber, manabian, vpalatin,
gabriel.fernandez, niklass, netdev, devicetree, linux-kernel,
linux-doc
In-Reply-To: <4d43479f-8373-308b-8c7d-cfed5346dc53@st.com>
From: Alexandre Torgue <alexandre.torgue@st.com>
Date: Thu, 8 Dec 2016 11:42:56 +0100
> Thanks for this patch, you can add my Acked-by.
Please simply state:
Acked-by: Alexandre Torgue <alexandre.torgue@st.com>
in your reply and it will automatically appear when the patch is
applied. You don't have to ask the patch submitter or the person who
applies it to do it as you are doing here.
^ permalink raw reply
* Re: [PATCH 1/2] Documentation: sample averaging for imx6ul_tsc
From: Guy Shapiro @ 2016-12-08 15:15 UTC (permalink / raw)
To: Rob Herring
Cc: fabio.estevam, mark.rutland, devicetree, haibo.chen,
dmitry.torokhov, linux-input, linux-arm-kernel
In-Reply-To: <20161201161343.g7p2maxiatp4m5di@rob-hp-laptop>
On 01/12/2016 18:13, Rob Herring wrote:
> On Sun, Nov 27, 2016 at 09:44:57AM +0200, Guy Shapiro wrote:
>> The i.MX6UL internal touchscreen controller contains an option to
>> average upon samples. This feature reduces noise from the produced
>> touch locations.
>>
>> This patch introduces a new device tree optional property for this
>> feature. It provides control over the amount of averaged samples per
>> touch event.
>>
>> The property was inspired by a similar property on the
>> "brcm,iproc-touchscreen" binding.
>>
>> Signed-off-by: Guy Shapiro <guy.shapiro@mobi-wize.com>
>> ---
>> .../devicetree/bindings/input/touchscreen/imx6ul_tsc.txt | 8
> ++++++++
>> 1 file changed, 8 insertions(+)
>>
>> diff --git
> a/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt
> b/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt
>> index 853dff9..a66069f 100644
>> --- a/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt
>> +++ b/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt
>> @@ -17,6 +17,13 @@ Optional properties:
>> This value depends on the touch screen.
>> - pre-charge-time: the touch screen need some time to precharge.
>> This value depends on the touch screen.
>> +- average-samples: Number of data samples which are averaged for each
> read.
>> + Valid values 0-4
>> + 0 = 1 sample
>> + 1 = 4 samples
>> + 2 = 8 samples
>> + 3 = 16 samples
>> + 4 = 32 samples
> Either this needs a vendor prefix or should be documented as a generic
> property. In the latter case, you should use actual number of samples
> (1-32) for the values.
In the term "generic property", do you mean to document it on
bindings/input/touchscreen/touchscreen.txt ?
If so, should I add the "touchscreen-" prefix like all the other
properties in that file?
Grepping bindings/input/touchscreen/, I found two other device drivers
that implement a
similar property - "ti-tsc-adc" and "brcm,iproc-touchscreen" (The
latter, BTW, uses a non
vendor prefixed property name).
Do we want to move from per-vendor properties to a generic one?
If we do, should we deprecate the existing vendor specific properties?
^ permalink raw reply
* [PATCH v4 4/4] arm64: dts: marvell: Enable spi0 on the board Armada-3720-db
From: Romain Perier @ 2016-12-08 14:58 UTC (permalink / raw)
To: Mark Brown, linux-spi-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni, Nadav Haklai, xigu-eYqpPyKDWXRBDgjK7y7TUQ,
dingwei-eYqpPyKDWXRBDgjK7y7TUQ, Romain Perier
In-Reply-To: <20161208145847.7794-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
This commit enables the device node spi0 on the official development
board for the Marvell Armada 3700. It also adds sub-node for the 128Mb
SPI-NOR present on the board.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Tested-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
Changes in v3:
- Added tag "Tested-by" by Gregory
arch/arm64/boot/dts/marvell/armada-3720-db.dts | 30 ++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
index 1372e9a6..0c4eb98 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
@@ -67,6 +67,36 @@
status = "okay";
};
+&spi0 {
+ status = "okay";
+
+ m25p80@0 {
+ compatible = "jedec,spi-nor";
+ reg = <0>;
+ spi-max-frequency = <108000000>;
+ spi-rx-bus-width = <4>;
+ spi-tx-bus-width = <4>;
+
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@0 {
+ label = "bootloader";
+ reg = <0x0 0x200000>;
+ };
+ partition@200000 {
+ label = "U-boot Env";
+ reg = <0x200000 0x10000>;
+ };
+ partition@210000 {
+ label = "Linux";
+ reg = <0x210000 0xDF0000>;
+ };
+ };
+ };
+};
+
/* Exported on the micro USB connector CON32 through an FTDI */
&uart0 {
status = "okay";
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 3/4] arm64: dts: marvell: Add definition of SPI controller for Armada 3700
From: Romain Perier @ 2016-12-08 14:58 UTC (permalink / raw)
To: Mark Brown, linux-spi-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni, Nadav Haklai, xigu-eYqpPyKDWXRBDgjK7y7TUQ,
dingwei-eYqpPyKDWXRBDgjK7y7TUQ, Romain Perier
In-Reply-To: <20161208145847.7794-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Armada 3700 SoC has an SPI Controller, this commit adds the definition
of the SPI device node at the SoC level.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Tested-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
Changes in v3:
- Fixed wrong register size for spi0, as suggested by the maintainer
on the ML.
- Added tag "Tested-by" by Gregory
Changes in v2:
- Removed properties max-frequency and clock-frequency, it is no
longer required and not used by the DT-bindings.
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index e9bd587..fcef9a5 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -98,6 +98,17 @@
/* 32M internal register @ 0xd000_0000 */
ranges = <0x0 0x0 0xd0000000 0x2000000>;
+ spi0: spi@10600 {
+ compatible = "marvell,armada-3700-spi";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x10600 0xA00>;
+ clocks = <&nb_periph_clk 7>;
+ interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ num-cs = <4>;
+ status = "disabled";
+ };
+
uart0: serial@12000 {
compatible = "marvell,armada-3700-uart";
reg = <0x12000 0x400>;
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 2/4] spi: armada-3700: Add documentation for the Armada 3700 SPI Controller
From: Romain Perier @ 2016-12-08 14:58 UTC (permalink / raw)
To: Mark Brown, linux-spi-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni, Nadav Haklai, xigu-eYqpPyKDWXRBDgjK7y7TUQ,
dingwei-eYqpPyKDWXRBDgjK7y7TUQ, Romain Perier
In-Reply-To: <20161208145847.7794-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
This adds the devicetree bindings documentation for the SPI controller
present in the Marvell Armada 3700 SoCs.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Tested-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Changes in v4:
- Added tag "Acked-by" by Rob Herring
Changes in v3:
- Added tag "Tested-by" by Gregory
- Fixed commit title, as requested by Mark Brown
.../devicetree/bindings/spi/spi-armada-3700.txt | 25 ++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/spi-armada-3700.txt
diff --git a/Documentation/devicetree/bindings/spi/spi-armada-3700.txt b/Documentation/devicetree/bindings/spi/spi-armada-3700.txt
new file mode 100644
index 0000000..1564aa8
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-armada-3700.txt
@@ -0,0 +1,25 @@
+* Marvell Armada 3700 SPI Controller
+
+Required Properties:
+
+- compatible: should be "marvell,armada-3700-spi"
+- reg: physical base address of the controller and length of memory mapped
+ region.
+- interrupts: The interrupt number. The interrupt specifier format depends on
+ the interrupt controller and of its driver.
+- clocks: Must contain the clock source, usually from the North Bridge clocks.
+- num-cs: The number of chip selects that is supported by this SPI Controller
+- #address-cells: should be 1.
+- #size-cells: should be 0.
+
+Example:
+
+ spi0: spi@10600 {
+ compatible = "marvell,armada-3700-spi";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x10600 0x5d>;
+ clocks = <&nb_perih_clk 7>;
+ interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ num-cs = <4>;
+ };
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 1/4] spi: Add support for Armada 3700 SPI Controller
From: Romain Perier @ 2016-12-08 14:58 UTC (permalink / raw)
To: Mark Brown, linux-spi-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni, Nadav Haklai, xigu-eYqpPyKDWXRBDgjK7y7TUQ,
dingwei-eYqpPyKDWXRBDgjK7y7TUQ, Romain Perier
In-Reply-To: <20161208145847.7794-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Marvell Armada 3700 SoC comprises an SPI Controller. This Controller
supports up to 4 SPI slave devices, with dedicated chip selects,supports
SPI mode 0/1/2 and 3, CPIO or Fifo mode with DMA transfers and different
SPI transfer mode (Single, Dual or Quad).
This commit adds basic driver support for FIFO mode. In this mode,
dedicated registers are used to store the instruction, the address, the
read mode and the data. Write and Read FIFO are used to store the
outcoming or incoming data. The data FIFOs are accessible via DMA or by
the CPU. Only the CPU is supported for now.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Tested-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
Changes in v4:
- Added missing COMPILE_TEST in KConfig
- Replaced the busy wait for loop by a single busy wait in spi_init()
- Handle IRQ_NONE in the interrupt handler
- Assign pdev->id to master->cs_num directly (no conditionnal operator)
- Moved clock gating into prepare/unprepare callbacks
- Removed dev_info() in the end of the probe() function
- Added flag to the spi master for single duplex transfers
- Squashed patch 1/5 and 2/5 together: remove the non-fifo mode, just keep
the more efficient mode
- So removed the module parameter
Changes in v3:
- Don't enable the fifo mode based on the compatible string, we introduce
a module parameter "pio_mode". By default this option is set to zero, so
the fifo mode is enabled. Pass pio_mode=1 to the driver enables the PIO
mode.
- Added tag "Tested-by" by Gregory
- Fixed wrong variable passed as MODULE_DEVICE_TABLE
- Added missing null terminated entry in a3700_spi_dt_ids
Changes in v2:
- Removed a3700_spi_bytelen_set from the setup function, it was accidentally
let here and not required, as it is configured in the prepare callback now
(defaults to 4 for fifo mode). It solves unrecognized spi-nor flash memory
detection with jedec.
drivers/spi/Kconfig | 7 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-armada-3700.c | 923 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 931 insertions(+)
create mode 100644 drivers/spi/spi-armada-3700.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b799547..1faad2ce 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -67,6 +67,13 @@ config SPI_ATH79
This enables support for the SPI controller present on the
Atheros AR71XX/AR724X/AR913X SoCs.
+config SPI_ARMADA_3700
+ tristate "Marvell Armada 3700 SPI Controller"
+ depends on (ARCH_MVEBU && OF) || COMPILE_TEST
+ help
+ This enables support for the SPI controller present on the
+ Marvell Armada 3700 SoCs.
+
config SPI_ATMEL
tristate "Atmel SPI Controller"
depends on HAS_DMA
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index aa939d9..140ca45 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
+obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c
new file mode 100644
index 0000000..e89da0a
--- /dev/null
+++ b/drivers/spi/spi-armada-3700.c
@@ -0,0 +1,923 @@
+/*
+ * Marvell Armada-3700 SPI controller driver
+ *
+ * Copyright (C) 2016 Marvell Ltd.
+ *
+ * Author: Wilson Ding <dingwei-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
+ * Author: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/spi/spi.h>
+
+#define DRIVER_NAME "armada_3700_spi"
+
+#define A3700_SPI_TIMEOUT 10
+
+/* SPI Register Offest */
+#define A3700_SPI_IF_CTRL_REG 0x00
+#define A3700_SPI_IF_CFG_REG 0x04
+#define A3700_SPI_DATA_OUT_REG 0x08
+#define A3700_SPI_DATA_IN_REG 0x0C
+#define A3700_SPI_IF_INST_REG 0x10
+#define A3700_SPI_IF_ADDR_REG 0x14
+#define A3700_SPI_IF_RMODE_REG 0x18
+#define A3700_SPI_IF_HDR_CNT_REG 0x1C
+#define A3700_SPI_IF_DIN_CNT_REG 0x20
+#define A3700_SPI_IF_TIME_REG 0x24
+#define A3700_SPI_INT_STAT_REG 0x28
+#define A3700_SPI_INT_MASK_REG 0x2C
+
+/* A3700_SPI_IF_CTRL_REG */
+#define A3700_SPI_EN BIT(16)
+#define A3700_SPI_ADDR_NOT_CONFIG BIT(12)
+#define A3700_SPI_WFIFO_OVERFLOW BIT(11)
+#define A3700_SPI_WFIFO_UNDERFLOW BIT(10)
+#define A3700_SPI_RFIFO_OVERFLOW BIT(9)
+#define A3700_SPI_RFIFO_UNDERFLOW BIT(8)
+#define A3700_SPI_WFIFO_FULL BIT(7)
+#define A3700_SPI_WFIFO_EMPTY BIT(6)
+#define A3700_SPI_RFIFO_FULL BIT(5)
+#define A3700_SPI_RFIFO_EMPTY BIT(4)
+#define A3700_SPI_WFIFO_RDY BIT(3)
+#define A3700_SPI_RFIFO_RDY BIT(2)
+#define A3700_SPI_XFER_RDY BIT(1)
+#define A3700_SPI_XFER_DONE BIT(0)
+
+/* A3700_SPI_IF_CFG_REG */
+#define A3700_SPI_WFIFO_THRS BIT(28)
+#define A3700_SPI_RFIFO_THRS BIT(24)
+#define A3700_SPI_AUTO_CS BIT(20)
+#define A3700_SPI_DMA_RD_EN BIT(18)
+#define A3700_SPI_FIFO_MODE BIT(17)
+#define A3700_SPI_SRST BIT(16)
+#define A3700_SPI_XFER_START BIT(15)
+#define A3700_SPI_XFER_STOP BIT(14)
+#define A3700_SPI_INST_PIN BIT(13)
+#define A3700_SPI_ADDR_PIN BIT(12)
+#define A3700_SPI_DATA_PIN1 BIT(11)
+#define A3700_SPI_DATA_PIN0 BIT(10)
+#define A3700_SPI_FIFO_FLUSH BIT(9)
+#define A3700_SPI_RW_EN BIT(8)
+#define A3700_SPI_CLK_POL BIT(7)
+#define A3700_SPI_CLK_PHA BIT(6)
+#define A3700_SPI_BYTE_LEN BIT(5)
+#define A3700_SPI_CLK_PRESCALE BIT(0)
+#define A3700_SPI_CLK_PRESCALE_MASK (0x1f)
+
+#define A3700_SPI_WFIFO_THRS_BIT 28
+#define A3700_SPI_RFIFO_THRS_BIT 24
+#define A3700_SPI_FIFO_THRS_MASK 0x7
+
+#define A3700_SPI_DATA_PIN_MASK 0x3
+
+/* A3700_SPI_IF_HDR_CNT_REG */
+#define A3700_SPI_DUMMY_CNT_BIT 12
+#define A3700_SPI_DUMMY_CNT_MASK 0x7
+#define A3700_SPI_RMODE_CNT_BIT 8
+#define A3700_SPI_RMODE_CNT_MASK 0x3
+#define A3700_SPI_ADDR_CNT_BIT 4
+#define A3700_SPI_ADDR_CNT_MASK 0x7
+#define A3700_SPI_INSTR_CNT_BIT 0
+#define A3700_SPI_INSTR_CNT_MASK 0x3
+
+/* A3700_SPI_IF_TIME_REG */
+#define A3700_SPI_CLK_CAPT_EDGE BIT(7)
+
+/* Flags and macros for struct a3700_spi */
+#define A3700_INSTR_CNT 1
+#define A3700_ADDR_CNT 3
+#define A3700_DUMMY_CNT 1
+
+struct a3700_spi {
+ struct spi_master *master;
+ void __iomem *base;
+ struct clk *clk;
+ unsigned int irq;
+ unsigned int flags;
+ bool xmit_data;
+ const u8 *tx_buf;
+ u8 *rx_buf;
+ size_t buf_len;
+ u8 byte_len;
+ u32 wait_mask;
+ struct completion done;
+ u32 addr_cnt;
+ u32 instr_cnt;
+ size_t hdr_cnt;
+};
+
+static u32 spireg_read(struct a3700_spi *a3700_spi, u32 offset)
+{
+ return readl(a3700_spi->base + offset);
+}
+
+static void spireg_write(struct a3700_spi *a3700_spi, u32 offset, u32 data)
+{
+ writel(data, a3700_spi->base + offset);
+}
+
+static void a3700_spi_auto_cs_unset(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~A3700_SPI_AUTO_CS;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_activate_cs(struct a3700_spi *a3700_spi, unsigned int cs)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ val |= (A3700_SPI_EN << cs);
+ spireg_write(a3700_spi, A3700_SPI_IF_CTRL_REG, val);
+}
+
+static void a3700_spi_deactivate_cs(struct a3700_spi *a3700_spi,
+ unsigned int cs)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ val &= ~(A3700_SPI_EN << cs);
+ spireg_write(a3700_spi, A3700_SPI_IF_CTRL_REG, val);
+}
+
+static int a3700_spi_pin_mode_set(struct a3700_spi *a3700_spi,
+ unsigned int pin_mode)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~(A3700_SPI_INST_PIN | A3700_SPI_ADDR_PIN);
+ val &= ~(A3700_SPI_DATA_PIN0 | A3700_SPI_DATA_PIN1);
+
+ switch (pin_mode) {
+ case 1:
+ break;
+ case 2:
+ val |= A3700_SPI_DATA_PIN0;
+ break;
+ case 4:
+ val |= A3700_SPI_DATA_PIN1;
+ break;
+ default:
+ dev_err(&a3700_spi->master->dev, "wrong pin mode %u", pin_mode);
+ return -EINVAL;
+ }
+
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ return 0;
+}
+
+static void a3700_spi_fifo_mode_set(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_FIFO_MODE;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_mode_set(struct a3700_spi *a3700_spi,
+ unsigned int mode_bits)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+
+ if (mode_bits & SPI_CPOL)
+ val |= A3700_SPI_CLK_POL;
+ else
+ val &= ~A3700_SPI_CLK_POL;
+
+ if (mode_bits & SPI_CPHA)
+ val |= A3700_SPI_CLK_PHA;
+ else
+ val &= ~A3700_SPI_CLK_PHA;
+
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_clock_set(struct a3700_spi *a3700_spi,
+ unsigned int speed_hz, u16 mode)
+{
+ u32 val;
+ u32 prescale;
+
+ prescale = DIV_ROUND_UP(clk_get_rate(a3700_spi->clk), speed_hz);
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val = val & ~A3700_SPI_CLK_PRESCALE_MASK;
+
+ val = val | (prescale & A3700_SPI_CLK_PRESCALE_MASK);
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ if (prescale <= 2) {
+ val = spireg_read(a3700_spi, A3700_SPI_IF_TIME_REG);
+ val |= A3700_SPI_CLK_CAPT_EDGE;
+ spireg_write(a3700_spi, A3700_SPI_IF_TIME_REG, val);
+ }
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~(A3700_SPI_CLK_POL | A3700_SPI_CLK_PHA);
+
+ if (mode & SPI_CPOL)
+ val |= A3700_SPI_CLK_POL;
+
+ if (mode & SPI_CPHA)
+ val |= A3700_SPI_CLK_PHA;
+
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_bytelen_set(struct a3700_spi *a3700_spi, unsigned int len)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ if (len == 4)
+ val |= A3700_SPI_BYTE_LEN;
+ else
+ val &= ~A3700_SPI_BYTE_LEN;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ a3700_spi->byte_len = len;
+}
+
+static int a3700_spi_fifo_flush(struct a3700_spi *a3700_spi)
+{
+ int timeout = A3700_SPI_TIMEOUT;
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_FIFO_FLUSH;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ while (--timeout) {
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ if (!(val & A3700_SPI_FIFO_FLUSH))
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int a3700_spi_init(struct a3700_spi *a3700_spi)
+{
+ struct spi_master *master = a3700_spi->master;
+ u32 val;
+ int i, ret = 0;
+
+ /* Reset SPI unit */
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_SRST;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ udelay(A3700_SPI_TIMEOUT);
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~A3700_SPI_SRST;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ /* Disable AUTO_CS and deactivate all chip-selects */
+ a3700_spi_auto_cs_unset(a3700_spi);
+ for (i = 0; i < master->num_chipselect; i++)
+ a3700_spi_deactivate_cs(a3700_spi, i);
+
+ /* Enable FIFO mode */
+ a3700_spi_fifo_mode_set(a3700_spi);
+
+ /* Set SPI mode */
+ a3700_spi_mode_set(a3700_spi, master->mode_bits);
+
+ /* Reset counters */
+ spireg_write(a3700_spi, A3700_SPI_IF_HDR_CNT_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG, 0);
+
+ /* Mask the interrupts and clear cause bits */
+ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, ~0U);
+
+ return ret;
+}
+
+static irqreturn_t a3700_spi_interrupt(int irq, void *dev_id)
+{
+ struct spi_master *master = dev_id;
+ struct a3700_spi *a3700_spi;
+ u32 cause;
+
+ a3700_spi = spi_master_get_devdata(master);
+
+ /* Get interrupt causes */
+ cause = spireg_read(a3700_spi, A3700_SPI_INT_STAT_REG);
+
+ if (!cause || !(a3700_spi->wait_mask & cause))
+ return IRQ_NONE;
+
+ /* mask and acknowledge the SPI interrupts */
+ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, cause);
+
+ /* Wake up the transfer */
+ if (a3700_spi->wait_mask & cause)
+ complete(&a3700_spi->done);
+
+ return IRQ_HANDLED;
+}
+
+static bool a3700_spi_wait_completion(struct spi_device *spi)
+{
+ struct a3700_spi *a3700_spi;
+ unsigned int timeout;
+ unsigned int ctrl_reg;
+ unsigned long timeout_jiffies;
+
+ a3700_spi = spi_master_get_devdata(spi->master);
+
+ /* SPI interrupt is edge-triggered, which means an interrupt will
+ * be generated only when detecting a specific status bit changed
+ * from '0' to '1'. So when we start waiting for a interrupt, we
+ * need to check status bit in control reg first, if it is already 1,
+ * then we do not need to wait for interrupt
+ */
+ ctrl_reg = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ if (a3700_spi->wait_mask & ctrl_reg)
+ return true;
+
+ reinit_completion(&a3700_spi->done);
+
+ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG,
+ a3700_spi->wait_mask);
+
+ timeout_jiffies = msecs_to_jiffies(A3700_SPI_TIMEOUT);
+ timeout = wait_for_completion_timeout(&a3700_spi->done,
+ timeout_jiffies);
+
+ a3700_spi->wait_mask = 0;
+
+ if (timeout)
+ return true;
+
+ /* there might be the case that right after we checked the
+ * status bits in this routine and before start to wait for
+ * interrupt by wait_for_completion_timeout, the interrupt
+ * happens, to avoid missing it we need to double check
+ * status bits in control reg, if it is already 1, then
+ * consider that we have the interrupt successfully and
+ * return true.
+ */
+ ctrl_reg = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ if (a3700_spi->wait_mask & ctrl_reg)
+ return true;
+
+ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0);
+
+ return true;
+}
+
+static bool a3700_spi_transfer_wait(struct spi_device *spi,
+ unsigned int bit_mask)
+{
+ struct a3700_spi *a3700_spi;
+
+ a3700_spi = spi_master_get_devdata(spi->master);
+ a3700_spi->wait_mask = bit_mask;
+
+ return a3700_spi_wait_completion(spi);
+}
+
+static void a3700_spi_fifo_thres_set(struct a3700_spi *a3700_spi,
+ unsigned int bytes)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~(A3700_SPI_FIFO_THRS_MASK << A3700_SPI_RFIFO_THRS_BIT);
+ val |= (bytes - 1) << A3700_SPI_RFIFO_THRS_BIT;
+ val &= ~(A3700_SPI_FIFO_THRS_MASK << A3700_SPI_WFIFO_THRS_BIT);
+ val |= (7 - bytes) << A3700_SPI_WFIFO_THRS_BIT;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_transfer_setup(struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct a3700_spi *a3700_spi;
+ unsigned int byte_len;
+
+ a3700_spi = spi_master_get_devdata(spi->master);
+
+ a3700_spi_clock_set(a3700_spi, xfer->speed_hz, spi->mode);
+
+ byte_len = xfer->bits_per_word >> 3;
+
+ a3700_spi_fifo_thres_set(a3700_spi, byte_len);
+}
+
+static void a3700_spi_set_cs(struct spi_device *spi, bool enable)
+{
+ struct a3700_spi *a3700_spi = spi_master_get_devdata(spi->master);
+
+ if (!enable)
+ a3700_spi_activate_cs(a3700_spi, spi->chip_select);
+ else
+ a3700_spi_deactivate_cs(a3700_spi, spi->chip_select);
+}
+
+static void a3700_spi_header_set(struct a3700_spi *a3700_spi)
+{
+ u32 instr_cnt = 0, addr_cnt = 0, dummy_cnt = 0;
+ u32 val = 0;
+
+ /* Clear the header registers */
+ spireg_write(a3700_spi, A3700_SPI_IF_INST_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_IF_ADDR_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_IF_RMODE_REG, 0);
+
+ /* Set header counters */
+ if (a3700_spi->tx_buf) {
+ if (a3700_spi->buf_len <= a3700_spi->instr_cnt) {
+ instr_cnt = a3700_spi->buf_len;
+ } else if (a3700_spi->buf_len <= (a3700_spi->instr_cnt +
+ a3700_spi->addr_cnt)) {
+ instr_cnt = a3700_spi->instr_cnt;
+ addr_cnt = a3700_spi->buf_len - instr_cnt;
+ } else if (a3700_spi->buf_len <= a3700_spi->hdr_cnt) {
+ instr_cnt = a3700_spi->instr_cnt;
+ addr_cnt = a3700_spi->addr_cnt;
+ /* Need to handle the normal write case with 1 byte
+ * data
+ */
+ if (!a3700_spi->tx_buf[instr_cnt + addr_cnt])
+ dummy_cnt = a3700_spi->buf_len - instr_cnt -
+ addr_cnt;
+ }
+ val |= ((instr_cnt & A3700_SPI_INSTR_CNT_MASK)
+ << A3700_SPI_INSTR_CNT_BIT);
+ val |= ((addr_cnt & A3700_SPI_ADDR_CNT_MASK)
+ << A3700_SPI_ADDR_CNT_BIT);
+ val |= ((dummy_cnt & A3700_SPI_DUMMY_CNT_MASK)
+ << A3700_SPI_DUMMY_CNT_BIT);
+ }
+ spireg_write(a3700_spi, A3700_SPI_IF_HDR_CNT_REG, val);
+
+ /* Update the buffer length to be transferred */
+ a3700_spi->buf_len -= (instr_cnt + addr_cnt + dummy_cnt);
+
+ /* Set Instruction */
+ val = 0;
+ while (instr_cnt--) {
+ val = (val << 8) | a3700_spi->tx_buf[0];
+ a3700_spi->tx_buf++;
+ }
+ spireg_write(a3700_spi, A3700_SPI_IF_INST_REG, val);
+
+ /* Set Address */
+ val = 0;
+ while (addr_cnt--) {
+ val = (val << 8) | a3700_spi->tx_buf[0];
+ a3700_spi->tx_buf++;
+ }
+ spireg_write(a3700_spi, A3700_SPI_IF_ADDR_REG, val);
+}
+
+static int a3700_is_wfifo_full(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ return (val & A3700_SPI_WFIFO_FULL);
+}
+
+static int a3700_spi_fifo_write(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+ int i = 0;
+
+ while (!a3700_is_wfifo_full(a3700_spi) && a3700_spi->buf_len) {
+ val = 0;
+ if (a3700_spi->buf_len >= 4) {
+ val = cpu_to_le32(*(u32 *)a3700_spi->tx_buf);
+ spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, val);
+
+ a3700_spi->buf_len -= 4;
+ a3700_spi->tx_buf += 4;
+ } else {
+ /*
+ * If the remained buffer length is less than 4-bytes,
+ * we should pad the write buffer with all ones. So that
+ * it avoids overwrite the unexpected bytes following
+ * the last one.
+ */
+ val = GENMASK(31, 0);
+ while (a3700_spi->buf_len) {
+ val &= ~(0xff << (8 * i));
+ val |= *a3700_spi->tx_buf++ << (8 * i);
+ i++;
+ a3700_spi->buf_len--;
+
+ spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG,
+ val);
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int a3700_is_rfifo_empty(struct a3700_spi *a3700_spi)
+{
+ u32 val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+
+ return (val & A3700_SPI_RFIFO_EMPTY);
+}
+
+static int a3700_spi_fifo_read(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+
+ while (!a3700_is_rfifo_empty(a3700_spi) && a3700_spi->buf_len) {
+ val = spireg_read(a3700_spi, A3700_SPI_DATA_IN_REG);
+ if (a3700_spi->buf_len >= 4) {
+ u32 data = le32_to_cpu(val);
+ memcpy(a3700_spi->rx_buf, &data, 4);
+
+ a3700_spi->buf_len -= 4;
+ a3700_spi->rx_buf += 4;
+ } else {
+ /*
+ * When remain bytes is not larger than 4, we should
+ * avoid memory overwriting and just write the left rx
+ * buffer bytes.
+ */
+ while (a3700_spi->buf_len) {
+ *a3700_spi->rx_buf = val & 0xff;
+ val >>= 8;
+
+ a3700_spi->buf_len--;
+ a3700_spi->rx_buf++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void a3700_spi_transfer_abort_fifo(struct a3700_spi *a3700_spi)
+{
+ int timeout = A3700_SPI_TIMEOUT;
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_XFER_STOP;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ while (--timeout) {
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ if (!(val & A3700_SPI_XFER_START))
+ break;
+ udelay(1);
+ }
+
+ a3700_spi_fifo_flush(a3700_spi);
+
+ val &= ~A3700_SPI_XFER_STOP;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static int a3700_spi_prepare_message(struct spi_master *master,
+ struct spi_message *message)
+{
+ struct a3700_spi *a3700_spi = spi_master_get_devdata(master);
+ struct spi_device *spi = message->spi;
+ int ret;
+
+ ret = clk_enable(a3700_spi->clk);
+ if (ret) {
+ dev_err(&spi->dev, "failed to enable clk with error %d\n", ret);
+ return ret;
+ }
+
+ /* Flush the FIFOs */
+ ret = a3700_spi_fifo_flush(a3700_spi);
+ if (ret)
+ return ret;
+
+ a3700_spi_bytelen_set(a3700_spi, 4);
+
+ return 0;
+}
+
+static int a3700_spi_transfer_one(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct a3700_spi *a3700_spi = spi_master_get_devdata(master);
+ int ret = 0, timeout = A3700_SPI_TIMEOUT;
+ unsigned int nbits = 0;
+ u32 val;
+
+ a3700_spi_transfer_setup(spi, xfer);
+
+ a3700_spi->tx_buf = xfer->tx_buf;
+ a3700_spi->rx_buf = xfer->rx_buf;
+ a3700_spi->buf_len = xfer->len;
+
+ /* SPI transfer headers */
+ a3700_spi_header_set(a3700_spi);
+
+ if (xfer->tx_buf)
+ nbits = xfer->tx_nbits;
+ else if (xfer->rx_buf)
+ nbits = xfer->rx_nbits;
+
+ a3700_spi_pin_mode_set(a3700_spi, nbits);
+
+ if (xfer->rx_buf) {
+ /* Set read data length */
+ spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG,
+ a3700_spi->buf_len);
+ /* Start READ transfer */
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~A3700_SPI_RW_EN;
+ val |= A3700_SPI_XFER_START;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+ } else if (xfer->tx_buf) {
+ /* Start Write transfer */
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= (A3700_SPI_XFER_START | A3700_SPI_RW_EN);
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ /*
+ * If there are data to be written to the SPI device, xmit_data
+ * flag is set true; otherwise the instruction in SPI_INSTR does
+ * not require data to be written to the SPI device, then
+ * xmit_data flag is set false.
+ */
+ a3700_spi->xmit_data = (a3700_spi->buf_len != 0);
+ }
+
+ while (a3700_spi->buf_len) {
+ if (a3700_spi->tx_buf) {
+ /* Wait wfifo ready */
+ if (!a3700_spi_transfer_wait(spi,
+ A3700_SPI_WFIFO_RDY)) {
+ dev_err(&spi->dev,
+ "wait wfifo ready timed out\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+ /* Fill up the wfifo */
+ ret = a3700_spi_fifo_write(a3700_spi);
+ if (ret)
+ goto error;
+ } else if (a3700_spi->rx_buf) {
+ /* Wait rfifo ready */
+ if (!a3700_spi_transfer_wait(spi,
+ A3700_SPI_RFIFO_RDY)) {
+ dev_err(&spi->dev,
+ "wait rfifo ready timed out\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+ /* Drain out the rfifo */
+ ret = a3700_spi_fifo_read(a3700_spi);
+ if (ret)
+ goto error;
+ }
+ }
+
+ /*
+ * Stop a write transfer in fifo mode:
+ * - wait all the bytes in wfifo to be shifted out
+ * - set XFER_STOP bit
+ * - wait XFER_START bit clear
+ * - clear XFER_STOP bit
+ * Stop a read transfer in fifo mode:
+ * - the hardware is to reset the XFER_START bit
+ * after the number of bytes indicated in DIN_CNT
+ * register
+ * - just wait XFER_START bit clear
+ */
+ if (a3700_spi->tx_buf) {
+ if (a3700_spi->xmit_data) {
+ /*
+ * If there are data written to the SPI device, wait
+ * until SPI_WFIFO_EMPTY is 1 to wait for all data to
+ * transfer out of write FIFO.
+ */
+ if (!a3700_spi_transfer_wait(spi,
+ A3700_SPI_WFIFO_EMPTY)) {
+ dev_err(&spi->dev, "wait wfifo empty timed out\n");
+ return -ETIMEDOUT;
+ }
+ } else {
+ /*
+ * If the instruction in SPI_INSTR does not require data
+ * to be written to the SPI device, wait until SPI_RDY
+ * is 1 for the SPI interface to be in idle.
+ */
+ if (!a3700_spi_transfer_wait(spi, A3700_SPI_XFER_RDY)) {
+ dev_err(&spi->dev, "wait xfer ready timed out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_XFER_STOP;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+ }
+
+ while (--timeout) {
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ if (!(val & A3700_SPI_XFER_START))
+ break;
+ udelay(1);
+ }
+
+ if (timeout == 0) {
+ dev_err(&spi->dev, "wait transfer start clear timed out\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ val &= ~A3700_SPI_XFER_STOP;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+ goto out;
+
+error:
+ a3700_spi_transfer_abort_fifo(a3700_spi);
+out:
+ spi_finalize_current_transfer(master);
+
+ return ret;
+}
+
+static int a3700_spi_unprepare_message(struct spi_master *master,
+ struct spi_message *message)
+{
+ struct a3700_spi *a3700_spi = spi_master_get_devdata(master);
+
+ clk_disable(a3700_spi->clk);
+
+ return 0;
+}
+
+static const struct of_device_id a3700_spi_dt_ids[] = {
+ { .compatible = "marvell,armada-3700-spi", .data = NULL },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, a3700_spi_dt_ids);
+
+static int a3700_spi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *of_node = dev->of_node;
+ struct resource *res;
+ struct spi_master *master;
+ struct a3700_spi *spi;
+ u32 num_cs = 0;
+ int ret = 0;
+
+ master = spi_alloc_master(dev, sizeof(*spi));
+ if (!master) {
+ dev_err(dev, "master allocation failed\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (of_property_read_u32(of_node, "num-cs", &num_cs)) {
+ dev_err(dev, "could not find num-cs\n");
+ ret = -ENXIO;
+ goto error;
+ }
+
+ master->bus_num = pdev->id;
+ master->dev.of_node = of_node;
+ master->mode_bits = SPI_MODE_3;
+ master->num_chipselect = num_cs;
+ master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(32);
+ master->prepare_message = a3700_spi_prepare_message;
+ master->transfer_one = a3700_spi_transfer_one;
+ master->unprepare_message = a3700_spi_unprepare_message;
+ master->set_cs = a3700_spi_set_cs;
+ master->flags = SPI_MASTER_HALF_DUPLEX;
+ master->mode_bits |= (SPI_RX_DUAL | SPI_RX_DUAL |
+ SPI_RX_QUAD | SPI_TX_QUAD);
+
+ platform_set_drvdata(pdev, master);
+
+ spi = spi_master_get_devdata(master);
+ memset(spi, 0, sizeof(struct a3700_spi));
+
+ spi->master = master;
+ spi->instr_cnt = A3700_INSTR_CNT;
+ spi->addr_cnt = A3700_ADDR_CNT;
+ spi->hdr_cnt = A3700_INSTR_CNT + A3700_ADDR_CNT +
+ A3700_DUMMY_CNT;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ spi->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(spi->base)) {
+ ret = PTR_ERR(spi->base);
+ goto error;
+ }
+
+ spi->irq = platform_get_irq(pdev, 0);
+ if (spi->irq < 0) {
+ dev_err(dev, "could not get irq: %d\n", spi->irq);
+ ret = -ENXIO;
+ goto error;
+ }
+
+ init_completion(&spi->done);
+
+ spi->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(spi->clk)) {
+ dev_err(dev, "could not find clk: %ld\n", PTR_ERR(spi->clk));
+ goto error;
+ }
+
+ ret = clk_prepare(spi->clk);
+ if (ret) {
+ dev_err(dev, "could not prepare clk: %d\n", ret);
+ goto error;
+ }
+
+ ret = a3700_spi_init(spi);
+ if (ret)
+ goto error_clk;
+
+ ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0,
+ dev_name(dev), master);
+ if (ret) {
+ dev_err(dev, "could not request IRQ: %d\n", ret);
+ goto error_clk;
+ }
+
+ ret = devm_spi_register_master(dev, master);
+ if (ret) {
+ dev_err(dev, "Failed to register master\n");
+ goto error_clk;
+ }
+
+ return 0;
+
+error_clk:
+ clk_disable_unprepare(spi->clk);
+error:
+ spi_master_put(master);
+out:
+ return ret;
+}
+
+static int a3700_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct a3700_spi *spi = spi_master_get_devdata(master);
+
+ clk_unprepare(spi->clk);
+ spi_master_put(master);
+
+ return 0;
+}
+
+static struct platform_driver a3700_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(a3700_spi_dt_ids),
+ },
+ .probe = a3700_spi_probe,
+ .remove = a3700_spi_remove,
+};
+
+module_platform_driver(a3700_spi_driver);
+
+MODULE_DESCRIPTION("Armada-3700 SPI driver");
+MODULE_AUTHOR("Wilson Ding <dingwei-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 0/4] Add support for the Armada 3700 SPI controller
From: Romain Perier @ 2016-12-08 14:58 UTC (permalink / raw)
To: Mark Brown, linux-spi-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni, Nadav Haklai, xigu-eYqpPyKDWXRBDgjK7y7TUQ,
dingwei-eYqpPyKDWXRBDgjK7y7TUQ, Romain Perier
The Marvell Armada 3700 SoC includes an SPI controller. This controller
supports up to 4 SPI slave devices, with dedicated chip selects, CPIO or
FIFO mode with DMA or CPU transfers and different SPI transfer modes
(Standard single, Dual or Quad).
This set of patches adds a basic support for the FIFO mode, (CPU-side
only, DMA not supported yet). It also adds the required definitions of
the spi nodes to the devicetree.
Romain Perier (4):
spi: Add support for Armada 3700 SPI Controller
spi: armada-3700: Add documentation for the Armada 3700 SPI Controller
arm64: dts: marvell: Add definition of SPI controller for Armada 3700
arm64: dts: marvell: Enable spi0 on the board Armada-3720-db
.../devicetree/bindings/spi/spi-armada-3700.txt | 25 +
arch/arm64/boot/dts/marvell/armada-3720-db.dts | 30 +
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 11 +
drivers/spi/Kconfig | 7 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-armada-3700.c | 923 +++++++++++++++++++++
6 files changed, 997 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/spi-armada-3700.txt
create mode 100644 drivers/spi/spi-armada-3700.c
--
2.9.3
--
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/2] clk: zte: add audio clocks for zx296718
From: Jun Nie @ 2016-12-08 14:55 UTC (permalink / raw)
To: Shawn Guo
Cc: Michael Turquette, Stephen Boyd, Rob Herring, Mark Rutland,
Baoyou Xie, linux-clk, devicetree, linux-arm-kernel, Shawn Guo
In-Reply-To: <1481189157-8995-2-git-send-email-shawnguo@kernel.org>
2016-12-08 17:25 GMT+08:00 Shawn Guo <shawnguo@kernel.org>:
> From: Jun Nie <jun.nie@linaro.org>
>
> The audio related clock support is missing from the existing zx296718
> clock driver. Let's add it, so that the upstream ZX SPDIF driver can
> work for HDMI audio support.
>
> Signed-off-by: Jun Nie <jun.nie@linaro.org>
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
> drivers/clk/zte/clk-zx296718.c | 150 +++++++++++++++++++++++++++++++++++++++++
> drivers/clk/zte/clk.c | 149 ++++++++++++++++++++++++++++++++++++++++
> drivers/clk/zte/clk.h | 28 ++++++++
> 3 files changed, 327 insertions(+)
>
> diff --git a/drivers/clk/zte/clk-zx296718.c b/drivers/clk/zte/clk-zx296718.c
> index 707d62956e9b..eed8581b1b25 100644
> --- a/drivers/clk/zte/clk-zx296718.c
> +++ b/drivers/clk/zte/clk-zx296718.c
> @@ -888,10 +888,160 @@ static int __init lsp1_clocks_init(struct device_node *np)
> return 0;
> }
>
> +PNAME(audio_wclk_common_p) = {
> + "audio_99m",
> + "audio_24m",
> +};
> +
> +PNAME(audio_timer_p) = {
> + "audio_24m",
> + "audio_32k",
> +};
> +
> +static struct zx_clk_mux audio_mux_clk[] = {
> + MUX(0, "i2s0_wclk_mux", audio_wclk_common_p, AUDIO_I2S0_CLK, 0, 1),
> + MUX(0, "i2s1_wclk_mux", audio_wclk_common_p, AUDIO_I2S1_CLK, 0, 1),
> + MUX(0, "i2s2_wclk_mux", audio_wclk_common_p, AUDIO_I2S2_CLK, 0, 1),
> + MUX(0, "i2s3_wclk_mux", audio_wclk_common_p, AUDIO_I2S3_CLK, 0, 1),
> + MUX(0, "i2c0_wclk_mux", audio_wclk_common_p, AUDIO_I2C0_CLK, 0, 1),
> + MUX(0, "spdif0_wclk_mux", audio_wclk_common_p, AUDIO_SPDIF0_CLK, 0, 1),
> + MUX(0, "spdif1_wclk_mux", audio_wclk_common_p, AUDIO_SPDIF1_CLK, 0, 1),
> + MUX(0, "timer_wclk_mux", audio_timer_p, AUDIO_TIMER_CLK, 0, 1),
> +};
> +
> +struct zx_clk_audio_div_table i2s_wclk_div_table[] = {
> + {2048000, 0x3000030, 0xffff5700},
> + {4096000, 0x3000018, 0xffff2b80},
> + {2822400, 0x3000011, 0xffff89cb},
> + {3072000, 0x3000010, 0xffff1d00},
> + {4096000, 0x300000c, 0xffff15c0},
> + {5644800, 0x3000008, 0xffffc4e5},
> + {6144000, 0x3000008, 0xffff0e80},
> + {11289600, 0x3000004, 0xffff6273},
> + {12288000, 0x3000004, 0xffff0740},
> + {22579200, 0x3000002, 0xffff3139},
> + {24576000, 0x3000002, 0xffff03a0},
> +};
> +
> +struct zx_clk_audio_div_table spdif_wclk_div_table[] = {
> + {2822400, 0x00023, 0xffff1397},
> + {3072000, 0x00020, 0xffff3a00},
> + {4096000, 0x00018, 0xffff2b80},
> + {5644800, 0x00011, 0xffff89cb},
> + {6144000, 0x00010, 0xffff1d00},
> + {11289600, 0x00008, 0xffffc4e5},
> + {12288000, 0x00008, 0xffff0e80},
> + {22579200, 0x00004, 0xffff6273},
> + {24576000, 0x00004, 0xffff0740},
> +};
You can remove these two tables and table pointer member as I already
removed table pointer assignment in macro AUDIO_DIV in this code. I am
sorry for not cleaning code properly.
> +
> +struct clk_zx_audio_divider audio_adiv_clk[] = {
> + AUDIO_DIV(0, "i2s0_wclk_div", "i2s0_wclk_mux", AUDIO_I2S0_DIV_CFG1, i2s_wclk_div_table),
> + AUDIO_DIV(0, "i2s1_wclk_div", "i2s1_wclk_mux", AUDIO_I2S1_DIV_CFG1, i2s_wclk_div_table),
> + AUDIO_DIV(0, "i2s2_wclk_div", "i2s2_wclk_mux", AUDIO_I2S2_DIV_CFG1, i2s_wclk_div_table),
> + AUDIO_DIV(0, "i2s3_wclk_div", "i2s3_wclk_mux", AUDIO_I2S3_DIV_CFG1, i2s_wclk_div_table),
> + AUDIO_DIV(0, "spdif0_wclk_div", "spdif0_wclk_mux", AUDIO_SPDIF0_DIV_CFG1, spdif_wclk_div_table),
> + AUDIO_DIV(0, "spdif1_wclk_div", "spdif1_wclk_mux", AUDIO_SPDIF1_DIV_CFG1, spdif_wclk_div_table),
> +};
> +
> +struct zx_clk_div audio_div_clk[] = {
> + DIV_T(0, "tdm_wclk_div", "audio_16m384", AUDIO_TDM_CLK, 8, 4, 0, common_div_table),
> +};
> +
> +struct zx_clk_gate audio_gate_clk[] = {
> + GATE(AUDIO_I2S0_WCLK, "i2s0_wclk", "i2s0_wclk_div", AUDIO_I2S0_CLK, 9, CLK_SET_RATE_PARENT, 0),
> + GATE(AUDIO_I2S1_WCLK, "i2s1_wclk", "i2s1_wclk_div", AUDIO_I2S1_CLK, 9, CLK_SET_RATE_PARENT, 0),
> + GATE(AUDIO_I2S2_WCLK, "i2s2_wclk", "i2s2_wclk_div", AUDIO_I2S2_CLK, 9, CLK_SET_RATE_PARENT, 0),
> + GATE(AUDIO_I2S3_WCLK, "i2s3_wclk", "i2s3_wclk_div", AUDIO_I2S3_CLK, 9, CLK_SET_RATE_PARENT, 0),
> + GATE(AUDIO_I2C0_WCLK, "i2c0_wclk", "i2c0_wclk_mux", AUDIO_I2C0_CLK, 9, CLK_SET_RATE_PARENT, 0),
> + GATE(AUDIO_SPDIF0_WCLK, "spdif0_wclk", "spdif0_wclk_div", AUDIO_SPDIF0_CLK, 9, CLK_SET_RATE_PARENT, 0),
> + GATE(AUDIO_SPDIF1_WCLK, "spdif1_wclk", "spdif1_wclk_div", AUDIO_SPDIF1_CLK, 9, CLK_SET_RATE_PARENT, 0),
> + GATE(AUDIO_TDM_WCLK, "tdm_wclk", "tdm_wclk_div", AUDIO_TDM_CLK, 17, CLK_SET_RATE_PARENT, 0),
> + GATE(AUDIO_TS_PCLK, "tempsensor_pclk", "clk49m5", AUDIO_TS_CLK, 1, 0, 0),
> +};
> +
> +static struct clk_hw_onecell_data audio_hw_onecell_data = {
> + .num = AUDIO_NR_CLKS,
> + .hws = {
> + [AUDIO_NR_CLKS - 1] = NULL,
> + },
> +};
> +
> +static int __init audio_clocks_init(struct device_node *np)
> +{
> + void __iomem *reg_base;
> + int i, ret;
> +
> + reg_base = of_iomap(np, 0);
> + if (!reg_base) {
> + pr_err("%s: Unable to map audio clk base\n", __func__);
> + return -ENXIO;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(audio_mux_clk); i++) {
> + if (audio_mux_clk[i].id)
> + audio_hw_onecell_data.hws[audio_mux_clk[i].id] =
> + &audio_mux_clk[i].mux.hw;
> +
> + audio_mux_clk[i].mux.reg += (u64)reg_base;
Fix build test failure on 32bit system.
audio_mux_clk[i].mux.reg += (uintptr_t)reg_base;
> + ret = clk_hw_register(NULL, &audio_mux_clk[i].mux.hw);
> + if (ret) {
> + pr_warn("audio clk %s init error!\n",
> + audio_mux_clk[i].mux.hw.init->name);
> + }
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(audio_adiv_clk); i++) {
> + if (audio_adiv_clk[i].id)
> + audio_hw_onecell_data.hws[audio_adiv_clk[i].id] =
> + &audio_adiv_clk[i].hw;
> +
> + audio_adiv_clk[i].reg_base += (u64)reg_base;
The same to this line and below cases.
> + ret = clk_hw_register(NULL, &audio_adiv_clk[i].hw);
> + if (ret) {
> + pr_warn("audio clk %s init error!\n",
> + audio_adiv_clk[i].hw.init->name);
> + }
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(audio_div_clk); i++) {
> + if (audio_div_clk[i].id)
> + audio_hw_onecell_data.hws[audio_div_clk[i].id] =
> + &audio_div_clk[i].div.hw;
> +
> + audio_div_clk[i].div.reg += (u64)reg_base;
> + ret = clk_hw_register(NULL, &audio_div_clk[i].div.hw);
> + if (ret) {
> + pr_warn("audio clk %s init error!\n",
> + audio_div_clk[i].div.hw.init->name);
> + }
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(audio_gate_clk); i++) {
> + if (audio_gate_clk[i].id)
> + audio_hw_onecell_data.hws[audio_gate_clk[i].id] =
> + &audio_gate_clk[i].gate.hw;
> +
> + audio_gate_clk[i].gate.reg += (u64)reg_base;
> + ret = clk_hw_register(NULL, &audio_gate_clk[i].gate.hw);
> + if (ret) {
> + pr_warn("audio clk %s init error!\n",
> + audio_gate_clk[i].gate.hw.init->name);
> + }
> + }
> +
> + if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &audio_hw_onecell_data))
> + panic("could not register clk provider\n");
> + pr_info("audio-clk init over, nr:%d\n", AUDIO_NR_CLKS);
> +
> + return 0;
> +}
> +
> static const struct of_device_id zx_clkc_match_table[] = {
> { .compatible = "zte,zx296718-topcrm", .data = &top_clocks_init },
> { .compatible = "zte,zx296718-lsp0crm", .data = &lsp0_clocks_init },
> { .compatible = "zte,zx296718-lsp1crm", .data = &lsp1_clocks_init },
> + { .compatible = "zte,zx296718-audiocrm", .data = &audio_clocks_init },
> { }
> };
>
> diff --git a/drivers/clk/zte/clk.c b/drivers/clk/zte/clk.c
> index c4c1251bc1e7..ea97024b37aa 100644
> --- a/drivers/clk/zte/clk.c
> +++ b/drivers/clk/zte/clk.c
> @@ -9,6 +9,7 @@
>
> #include <linux/clk-provider.h>
> #include <linux/err.h>
> +#include <linux/gcd.h>
> #include <linux/io.h>
> #include <linux/iopoll.h>
> #include <linux/slab.h>
> @@ -310,3 +311,151 @@ struct clk *clk_register_zx_audio(const char *name,
>
> return clk;
> }
> +
> +#define CLK_AUDIO_DIV_FRAC BIT(0)
> +#define CLK_AUDIO_DIV_INT BIT(1)
> +#define CLK_AUDIO_DIV_UNCOMMON BIT(1)
> +
> +#define CLK_AUDIO_DIV_FRAC_NSHIFT 16
> +#define CLK_AUDIO_DIV_INT_FRAC_RE BIT(16)
> +#define CLK_AUDIO_DIV_INT_FRAC_MAX (0xffff)
> +#define CLK_AUDIO_DIV_INT_FRAC_MIN (0x2)
> +#define CLK_AUDIO_DIV_INT_INT_SHIFT 24
> +#define CLK_AUDIO_DIV_INT_INT_WIDTH 4
> +
> +#define to_clk_zx_audio_div(_hw) container_of(_hw, struct clk_zx_audio_divider, hw)
> +
> +static unsigned long audio_calc_rate(struct clk_zx_audio_divider *audio_div,
> + u32 reg_frac, u32 reg_int,
> + unsigned long parent_rate)
> +{
> + unsigned long rate, m, n;
> +
> + if (audio_div->table) {
> + const struct zx_clk_audio_div_table *divt = audio_div->table;
> +
> + for (; divt->rate; divt++) {
> + if ((divt->int_reg == reg_int) && (divt->frac_reg == reg_frac))
> + return divt->rate;
> + }
> + }
> + if (audio_div->table)
> + pr_warn("cannot found the config(int_reg:0x%x, frac_reg:0x%x) in table, we will caculate it\n",
> + reg_int, reg_frac);
Logic of register value table can be removed now.
> +
> + m = reg_frac & 0xffff;
> + n = (reg_frac >> 16) & 0xffff;
> +
> + m = (reg_int & 0xffff) * n + m;
> + rate = (parent_rate * n) / m;
> +
> + return rate;
> +}
> +
> +static void audio_calc_reg(struct clk_zx_audio_divider *audio_div,
> + struct zx_clk_audio_div_table *div_table,
> + unsigned long rate, unsigned long parent_rate)
> +{
> + unsigned int reg_int, reg_frac;
> + unsigned long m, n, div;
> +
> + if (audio_div->table) {
> + const struct zx_clk_audio_div_table *divt = audio_div->table;
> +
> + for (; divt->rate; divt++) {
> + if (divt->rate == rate) {
> + div_table->rate = divt->rate;
> + div_table->int_reg = divt->int_reg;
> + div_table->frac_reg = divt->frac_reg;
> + return;
> + }
> + }
> + }
> + if (audio_div->table)
> + pr_warn("cannot found the rate(%ld) in table, we will caculate the config\n",
> + rate);
Table is not used here actually neither.
> +
> + reg_int = parent_rate / rate;
> +
> + if (reg_int > CLK_AUDIO_DIV_INT_FRAC_MAX)
> + reg_int = CLK_AUDIO_DIV_INT_FRAC_MAX;
> + else if (reg_int < CLK_AUDIO_DIV_INT_FRAC_MIN)
> + reg_int = 0;
> + m = parent_rate - rate * reg_int;
> + n = rate;
> +
> + div = gcd(m, n);
> + m = m / div;
> + n = n / div;
> +
> + if ((m >> 16) || (n >> 16)) {
> + if (m > n) {
> + n = n * 0xffff / m;
> + m = 0xffff;
> + } else {
> + m = m * 0xffff / n;
> + n = 0xffff;
> + }
> + }
> + reg_frac = m | (n << 16);
> +
> + div_table->rate = (ulong)(parent_rate * n) / ((ulong)reg_int * n + m);
> + div_table->int_reg = reg_int;
> + div_table->frac_reg = reg_frac;
> +}
> +
> +static unsigned long zx_audio_div_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw);
> + u32 reg_frac, reg_int;
> +
> + reg_frac = readl_relaxed(zx_audio_div->reg_base);
> + reg_int = readl_relaxed(zx_audio_div->reg_base + 0x4);
> +
> + return audio_calc_rate(zx_audio_div, reg_frac, reg_int, parent_rate);
> +}
> +
> +static long zx_audio_div_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *prate)
> +{
> + struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw);
> + struct zx_clk_audio_div_table divt;
> +
> + audio_calc_reg(zx_audio_div, &divt, rate, *prate);
> +
> + return audio_calc_rate(zx_audio_div, divt.frac_reg, divt.int_reg, *prate);
> +}
> +
> +static int zx_audio_div_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw);
> + struct zx_clk_audio_div_table divt;
> + unsigned int val;
> +
> + audio_calc_reg(zx_audio_div, &divt, rate, parent_rate);
> + if (divt.rate != rate)
> + pr_info("the real rate is:%ld", divt.rate);
> +
> + writel_relaxed(divt.frac_reg, zx_audio_div->reg_base);
> +
> + val = readl_relaxed(zx_audio_div->reg_base + 0x4);
> + val &= ~0xffff;
> + val |= divt.int_reg | CLK_AUDIO_DIV_INT_FRAC_RE;
> + writel_relaxed(val, zx_audio_div->reg_base + 0x4);
> +
> + mdelay(1);
> +
> + val = readl_relaxed(zx_audio_div->reg_base + 0x4);
> + val &= ~CLK_AUDIO_DIV_INT_FRAC_RE;
> + writel_relaxed(val, zx_audio_div->reg_base + 0x4);
> +
> + return 0;
> +}
> +
> +const struct clk_ops zx_audio_div_ops = {
> + .recalc_rate = zx_audio_div_recalc_rate,
> + .round_rate = zx_audio_div_round_rate,
> + .set_rate = zx_audio_div_set_rate,
> +};
> diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h
> index 0df3474b2cf3..6e7ccb752c24 100644
> --- a/drivers/clk/zte/clk.h
> +++ b/drivers/clk/zte/clk.h
> @@ -153,6 +153,32 @@ struct zx_clk_div {
> .id = _id, \
> }
>
> +struct zx_clk_audio_div_table {
> + unsigned long rate;
> + unsigned int int_reg;
> + unsigned int frac_reg;
> +};
> +
> +struct clk_zx_audio_divider {
> + struct clk_hw hw;
> + void __iomem *reg_base;
> + const struct zx_clk_audio_div_table *table;
> + unsigned int rate_count;
> + spinlock_t *lock;
> + u16 id;
> +};
> +
> +#define AUDIO_DIV(_id, _name, _parent, _reg, _table) \
Remove unused table here.
> +{ \
> + .reg_base = (void __iomem *) _reg, \
> + .lock = &clk_lock, \
> + .hw.init = CLK_HW_INIT(_name, \
> + _parent, \
> + &zx_audio_div_ops, \
> + 0), \
> + .id = _id, \
> +}
> +
> struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
> unsigned long flags, void __iomem *reg_base,
> const struct zx_pll_config *lookup_table, int count, spinlock_t *lock);
> @@ -167,4 +193,6 @@ struct clk *clk_register_zx_audio(const char *name,
> unsigned long flags, void __iomem *reg_base);
>
> extern const struct clk_ops zx_pll_ops;
> +extern const struct clk_ops zx_audio_div_ops;
> +
> #endif
> --
> 1.9.1
>
^ permalink raw reply
* [PATCH v2 6/6] iio: bmi160: Support hardware fifo
From: Marcin Niestroj @ 2016-12-08 14:22 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
Daniel Baluta, Gregor Boirie, Sanchayan Maity, Rob Herring,
Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Marcin Niestroj
In-Reply-To: <20161208142259.26230-1-m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
This patch was developed primarily based on bmc150_accel hardware fifo
implementation.
IRQ handler was added, which for now is responsible only for handling
watermark interrupts. The BMI160 chip has two interrupt outputs. By
default INT is considered to be connected. If INT2 is used instead, the
interrupt-names device-tree property can be used to specify that.
Signed-off-by: Marcin Niestroj <m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
---
Depends on patch 4 in series
Changes v1 -> v2:
* add __ prefix to all functions that should be called with mutex held
(suggested by Peter)
* get rid of non-constant array size (suggested by Peter)
* disable irq on init failure and module removal (suggested by Peter)
* make bmi160_buffer_predisable() the reverse order of the
bmi160_buffer_postenable() (suggested by Jonathan)
* remove realignment for iio_info structs (suggested by Jonathan)
drivers/iio/imu/bmi160/bmi160.h | 3 +-
drivers/iio/imu/bmi160/bmi160_core.c | 618 ++++++++++++++++++++++++++++++++++-
drivers/iio/imu/bmi160/bmi160_i2c.c | 7 +-
drivers/iio/imu/bmi160/bmi160_spi.c | 3 +-
4 files changed, 613 insertions(+), 18 deletions(-)
diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h
index d2ae6ed..4a7c10e 100644
--- a/drivers/iio/imu/bmi160/bmi160.h
+++ b/drivers/iio/imu/bmi160/bmi160.h
@@ -4,7 +4,8 @@
extern const struct regmap_config bmi160_regmap_config;
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
- const char *name, bool use_spi);
+ const char *name, int irq,
+ bool use_spi, bool block_supported);
void bmi160_core_remove(struct device *dev);
#endif /* BMI160_H_ */
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index 88bcf3f..26404b4 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -10,7 +10,7 @@
*
* IIO core driver for BMI160, with support for I2C/SPI busses
*
- * TODO: magnetometer, interrupts, hardware FIFO
+ * TODO: magnetometer, interrupts
*/
#include <linux/module.h>
#include <linux/regmap.h>
@@ -23,8 +23,12 @@
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
+#include <linux/of_irq.h>
+
#include "bmi160.h"
+#define BMI160_IRQ_NAME "bmi160_event"
+
#define BMI160_REG_CHIP_ID 0x00
#define BMI160_CHIP_ID_VAL 0xD1
@@ -35,6 +39,21 @@
#define BMI160_REG_DATA_GYRO_XOUT_L 0x0C
#define BMI160_REG_DATA_ACCEL_XOUT_L 0x12
+#define BMI160_REG_STATUS 0x1B
+#define BMI160_STATUS_MAG_MAN_OP BIT(2)
+
+#define BMI160_REG_INT_STATUS0 0x1C
+
+#define BMI160_REG_INT_STATUS1 0x1D
+#define BMI160_INT_STATUS_FWM BIT(6)
+
+#define BMI160_REG_INT_STATUS2 0x1E
+
+#define BMI160_REG_INT_STATUS3 0x1F
+
+#define BMI160_REG_FIFO_LENGTH 0x22
+#define BMI160_REG_FIFO_DATA 0x24
+
#define BMI160_REG_ACCEL_CONFIG 0x40
#define BMI160_ACCEL_CONFIG_ODR_MASK GENMASK(3, 0)
#define BMI160_ACCEL_CONFIG_BWP_MASK GENMASK(6, 4)
@@ -56,6 +75,36 @@
#define BMI160_GYRO_RANGE_250DPS 0x03
#define BMI160_GYRO_RANGE_125DPS 0x04
+#define BMI160_REG_FIFO_CONFIG_0 0x46
+
+#define BMI160_REG_FIFO_CONFIG_1 0x47
+#define BMI160_FIFO_GYRO_EN BIT(7)
+#define BMI160_FIFO_ACCEL_EN BIT(6)
+#define BMI160_FIFO_MAGN_EN BIT(5)
+#define BMI160_FIFO_HEADER_EN BIT(4)
+#define BMI160_FIFO_TAG_INT1_EN BIT(3)
+#define BMI160_FIFO_TAG_INT2_EN BIT(2)
+#define BMI160_FIFO_TIME_EN BIT(1)
+
+#define BMI160_REG_INT_EN_1 0x51
+#define BMI160_INT_FWM_EN BIT(6)
+#define BMI160_INT_FFULL_EN BIT(5)
+#define BMI160_INT_DRDY_EN BIT(4)
+
+#define BMI160_REG_INT_OUT_CTRL 0x53
+#define BMI160_INT2_OUTPUT_EN BIT(7)
+#define BMI160_INT1_OUTPUT_EN BIT(3)
+
+#define BMI160_REG_INT_LATCH 0x54
+
+#define BMI160_REG_INT_MAP_1 0x56
+#define BMI160_INT1_MAP_DRDY BIT(7)
+#define BMI160_INT1_MAP_FWM BIT(6)
+#define BMI160_INT1_MAP_FFULL BIT(5)
+#define BMI160_INT2_MAP_DRDY BIT(3)
+#define BMI160_INT2_MAP_FWM BIT(2)
+#define BMI160_INT2_MAP_FFULL BIT(1)
+
#define BMI160_REG_CMD 0x7E
#define BMI160_CMD_ACCEL_PM_SUSPEND 0x10
#define BMI160_CMD_ACCEL_PM_NORMAL 0x11
@@ -67,6 +116,8 @@
#define BMI160_REG_DUMMY 0x7F
+#define BMI160_FIFO_LENGTH 1024
+
#define BMI160_ACCEL_PMU_MIN_USLEEP 3800
#define BMI160_GYRO_PMU_MIN_USLEEP 80000
#define BMI160_SOFTRESET_USLEEP 1000
@@ -109,9 +160,34 @@ enum bmi160_sensor_type {
BMI160_NUM_SENSORS /* must be last */
};
+struct bmi160_irq_data {
+ unsigned int map_fwm;
+ unsigned int output_en;
+};
+
+static const struct bmi160_irq_data bmi160_irq1_data = {
+ .map_fwm = BMI160_INT1_MAP_FWM,
+ .output_en = BMI160_INT1_OUTPUT_EN,
+};
+
+static const struct bmi160_irq_data bmi160_irq2_data = {
+ .map_fwm = BMI160_INT2_MAP_FWM,
+ .output_en = BMI160_INT2_OUTPUT_EN,
+};
+
struct bmi160_data {
struct regmap *regmap;
struct mutex mutex;
+ const struct bmi160_irq_data *irq_data;
+ int irq;
+ bool irq_enabled;
+ int64_t timestamp;
+ int64_t fifo_sample_period;
+ bool fifo_enabled;
+ unsigned int fifo_config;
+ unsigned int fifo_sample_size;
+ u8 *fifo_buffer;
+ unsigned int watermark;
};
const struct regmap_config bmi160_regmap_config = {
@@ -374,16 +450,20 @@ int bmi160_set_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
return ret;
}
-static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
- int *odr, int *uodr)
+static int64_t bmi160_frequency_to_period(int odr, int uodr)
{
- int i, val, ret;
+ uint64_t period = 1000000000000000;
+ int64_t frequency = (int64_t) odr * 1000000 + uodr;
- mutex_lock(&data->mutex);
- ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
- mutex_unlock(&data->mutex);
- if (ret < 0)
- return ret;
+ do_div(period, frequency);
+
+ return period;
+}
+
+static const struct bmi160_odr *bmi160_reg_to_odr(enum bmi160_sensor_type t,
+ unsigned int val)
+{
+ int i;
val &= bmi160_regs[t].config_odr_mask;
@@ -392,10 +472,52 @@ static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
break;
if (i >= bmi160_odr_table[t].num)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- *odr = bmi160_odr_table[t].tbl[i].odr;
- *uodr = bmi160_odr_table[t].tbl[i].uodr;
+ return &bmi160_odr_table[t].tbl[i];
+}
+
+static int __bmi160_get_sample_period(struct bmi160_data *data,
+ enum bmi160_sensor_type t,
+ int64_t *sample_period)
+{
+ const struct bmi160_odr *odr_entry;
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
+ if (ret < 0)
+ return ret;
+
+ odr_entry = bmi160_reg_to_odr(t, val);
+ if (IS_ERR(odr_entry))
+ return PTR_ERR(odr_entry);
+
+ *sample_period = bmi160_frequency_to_period(odr_entry->odr,
+ odr_entry->uodr);
+
+ return 0;
+}
+
+static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
+ int *odr, int *uodr)
+{
+ const struct bmi160_odr *odr_entry;
+ int ret;
+ unsigned int val;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ return ret;
+
+ odr_entry = bmi160_reg_to_odr(t, val);
+ if (IS_ERR(odr_entry))
+ return PTR_ERR(odr_entry);
+
+ *odr = odr_entry->odr;
+ *uodr = odr_entry->uodr;
return 0;
}
@@ -504,6 +626,356 @@ static const struct attribute_group bmi160_attrs_group = {
.attrs = bmi160_attrs,
};
+static int __bmi160_read_sample_period(struct bmi160_data *data,
+ enum bmi160_sensor_type sensor_type)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ int64_t uninitialized_var(sample_period);
+ int ret;
+
+ ret = __bmi160_get_sample_period(data, sensor_type, &sample_period);
+ if (ret < 0)
+ return ret;
+
+ if (data->fifo_sample_period) {
+ if (data->fifo_sample_period != sample_period) {
+ dev_warn(dev, "Enabled sensors have unequal ODR values\n");
+ return -EINVAL;
+ }
+ } else {
+ data->fifo_sample_period = sample_period;
+ }
+
+ return 0;
+}
+
+static int __bmi160_fifo_enable(struct iio_dev *indio_dev,
+ struct bmi160_data *data)
+{
+ struct regmap *regmap = data->regmap;
+ struct device *dev = regmap_get_device(regmap);
+ int ret;
+ int i;
+ unsigned int val;
+ unsigned int fifo_config = 0;
+
+ /* Set fifo sample size and period */
+ for_each_set_bit(i, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ if (i <= BMI160_SCAN_GYRO_Z)
+ fifo_config |= BMI160_FIFO_GYRO_EN;
+ else if (i <= BMI160_SCAN_ACCEL_Z)
+ fifo_config |= BMI160_FIFO_ACCEL_EN;
+ }
+
+ data->fifo_sample_period = 0;
+ data->fifo_sample_size = 0;
+ if (fifo_config & BMI160_FIFO_GYRO_EN) {
+ data->fifo_sample_size += 6;
+ ret = __bmi160_read_sample_period(data, BMI160_GYRO);
+ if (ret < 0)
+ return ret;
+ }
+ if (fifo_config & BMI160_FIFO_ACCEL_EN) {
+ data->fifo_sample_size += 6;
+ ret = __bmi160_read_sample_period(data, BMI160_ACCEL);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * Set watermark level and write real value back, as it will be used
+ * in timestamp calculation.
+ */
+ val = data->watermark * data->fifo_sample_size;
+ if (val > BMI160_FIFO_LENGTH - 1) {
+ val = BMI160_FIFO_LENGTH - 1;
+ data->watermark = val / data->fifo_sample_size;
+ }
+ val = data->watermark * data->fifo_sample_size / 4;
+
+ ret = regmap_write(regmap, BMI160_REG_FIFO_CONFIG_0, val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set watermark\n");
+ return ret;
+ }
+
+ /* Enable FIFO channels */
+ ret = regmap_write(regmap, BMI160_REG_FIFO_CONFIG_1,
+ fifo_config);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write FIFO_CONFIG_1\n");
+ return ret;
+ }
+
+ data->fifo_config = fifo_config;
+ data->fifo_enabled = true;
+
+ return 0;
+}
+
+static int __bmi160_fifo_disable(struct bmi160_data *data)
+{
+ struct regmap *regmap = data->regmap;
+ struct device *dev = regmap_get_device(regmap);
+ int ret;
+
+ /* Disable all FIFO channels */
+ ret = regmap_write(regmap, BMI160_REG_FIFO_CONFIG_1, 0);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write FIFO_CONFIG_1\n");
+ return ret;
+ }
+
+ data->fifo_enabled = false;
+
+ return 0;
+}
+
+static int bmi160_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct bmi160_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ return iio_triggered_buffer_postenable(indio_dev);
+
+ mutex_lock(&data->mutex);
+ ret = __bmi160_fifo_enable(indio_dev, data);
+ if (ret < 0)
+ goto unlock;
+
+ ret = regmap_update_bits(data->regmap, BMI160_REG_INT_MAP_1,
+ data->irq_data->map_fwm, data->irq_data->map_fwm);
+ if (ret < 0)
+ goto unlock;
+
+ ret = regmap_update_bits(data->regmap, BMI160_REG_INT_EN_1,
+ BMI160_INT_FWM_EN, BMI160_INT_FWM_EN);
+
+unlock:
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int bmi160_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct bmi160_data *data = iio_priv(indio_dev);
+ struct regmap *regmap = data->regmap;
+ int ret = 0;
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ return iio_triggered_buffer_predisable(indio_dev);
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(regmap, BMI160_REG_INT_EN_1,
+ BMI160_INT_FWM_EN, 0);
+ if (ret < 0)
+ goto unlock;
+
+ ret = regmap_update_bits(data->regmap, BMI160_REG_INT_MAP_1,
+ data->irq_data->map_fwm, 0);
+ if (ret < 0)
+ goto unlock;
+
+ ret = __bmi160_fifo_disable(data);
+
+unlock:
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops bmi160_buffer_ops = {
+ .postenable = bmi160_buffer_postenable,
+ .predisable = bmi160_buffer_predisable,
+};
+
+static ssize_t bmi160_get_fifo_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct bmi160_data *data = iio_priv(indio_dev);
+ bool state;
+
+ mutex_lock(&data->mutex);
+ state = data->fifo_enabled;
+ mutex_unlock(&data->mutex);
+
+ return sprintf(buf, "%d\n", (int) state);
+}
+
+static ssize_t bmi160_get_fifo_watermark(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct bmi160_data *data = iio_priv(indio_dev);
+ int wm;
+
+ mutex_lock(&data->mutex);
+ wm = data->watermark;
+ mutex_unlock(&data->mutex);
+
+ return sprintf(buf, "%d\n", wm);
+}
+
+static IIO_CONST_ATTR(hwfifo_watermark_min, "1");
+static IIO_CONST_ATTR(hwfifo_watermark_max,
+ __stringify(BMI160_FIFO_LENGTH));
+static IIO_DEVICE_ATTR(hwfifo_enabled, S_IRUGO,
+ bmi160_get_fifo_state, NULL, 0);
+static IIO_DEVICE_ATTR(hwfifo_watermark, S_IRUGO,
+ bmi160_get_fifo_watermark, NULL, 0);
+
+static const struct attribute *bmi160_fifo_attributes[] = {
+ &iio_const_attr_hwfifo_watermark_min.dev_attr.attr,
+ &iio_const_attr_hwfifo_watermark_max.dev_attr.attr,
+ &iio_dev_attr_hwfifo_watermark.dev_attr.attr,
+ &iio_dev_attr_hwfifo_enabled.dev_attr.attr,
+ NULL,
+};
+
+static int bmi160_set_watermark(struct iio_dev *indio_dev, unsigned int val)
+{
+ struct bmi160_data *data = iio_priv(indio_dev);
+
+ if (val > BMI160_FIFO_LENGTH)
+ val = BMI160_FIFO_LENGTH;
+
+ mutex_lock(&data->mutex);
+ data->watermark = val;
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static int __bmi160_fifo_transfer(struct bmi160_data *data,
+ char *buffer, int num_bytes)
+{
+ struct regmap *regmap = data->regmap;
+ struct device *dev = regmap_get_device(regmap);
+ size_t step = regmap_get_raw_read_max(regmap);
+ int ret = 0;
+ int i;
+
+ if (!step || step > num_bytes)
+ step = num_bytes;
+ else if (step < num_bytes)
+ step = data->fifo_sample_size;
+
+ for (i = 0; i < num_bytes; i += step) {
+ ret = regmap_raw_read(regmap, BMI160_REG_FIFO_DATA,
+ &buffer[i], step);
+
+ if (ret)
+ break;
+ }
+
+ if (ret)
+ dev_err(dev,
+ "Error transferring data from fifo in single steps of %zu\n",
+ step);
+
+ return ret;
+}
+
+static int __bmi160_fifo_flush(struct iio_dev *indio_dev,
+ unsigned int samples, bool irq)
+{
+ struct bmi160_data *data = iio_priv(indio_dev);
+ struct regmap *regmap = data->regmap;
+ struct device *dev = regmap_get_device(regmap);
+ int ret;
+ __le16 fifo_length;
+ unsigned int fifo_samples;
+ unsigned int fifo_bytes;
+ u8 *buffer = data->fifo_buffer;
+ u8 *buffer_iter;
+ int64_t last_timestamp, timestamp;
+ unsigned int last_samples;
+ unsigned int i;
+
+ /* Get the current FIFO length */
+ ret = regmap_bulk_read(regmap, BMI160_REG_FIFO_LENGTH,
+ &fifo_length, sizeof(__le16));
+ if (ret < 0) {
+ dev_err(dev, "Error reading FIFO_LENGTH\n");
+ return ret;
+ }
+
+ fifo_bytes = le16_to_cpu(fifo_length);
+ fifo_samples = fifo_bytes / data->fifo_sample_size;
+
+ if (fifo_bytes % data->fifo_sample_size)
+ dev_warn(dev, "fifo_bytes %u is not dividable by %u\n",
+ fifo_bytes, data->fifo_sample_size);
+
+ if (!fifo_samples)
+ return 0;
+
+ if (samples && fifo_samples > samples) {
+ fifo_samples = samples;
+ fifo_bytes = fifo_samples * data->fifo_sample_size;
+ }
+
+ /*
+ * If we are not called from IRQ, it means that we are flushing data
+ * on demand. In that case we do not have latest timestamp saved in
+ * data->timestamp. Get the time now instead.
+ *
+ * In case of IRQ flush, saved timestamp shows the time when number
+ * of samples configured by watermark were ready. Currently there might
+ * be more samples already.
+ * If we are not called from IRQ, than we are getting the current fifo
+ * length, as we are setting timestamp just after getting it.
+ */
+ if (!irq) {
+ last_timestamp = iio_get_time_ns(indio_dev);
+ last_samples = fifo_samples;
+ } else {
+ last_timestamp = data->timestamp;
+ last_samples = data->watermark;
+ }
+
+ /* Get all measurements */
+ ret = __bmi160_fifo_transfer(data, buffer, fifo_bytes);
+ if (ret)
+ return ret;
+
+ /* Handle demux */
+ timestamp = last_timestamp - (last_samples * data->fifo_sample_period);
+ buffer_iter = buffer;
+ for (i = 0; i < fifo_samples; i++) {
+ u8 tmp_buf[32];
+
+ memcpy(tmp_buf, buffer_iter, data->fifo_sample_size);
+
+ timestamp += data->fifo_sample_period;
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ tmp_buf,
+ timestamp);
+
+ buffer_iter += data->fifo_sample_size;
+ }
+
+ return fifo_samples;
+}
+
+static int bmi160_fifo_flush(struct iio_dev *indio_dev, unsigned int samples)
+{
+ struct bmi160_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = __bmi160_fifo_flush(indio_dev, samples, false);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
static const struct iio_info bmi160_info = {
.driver_module = THIS_MODULE,
.read_raw = bmi160_read_raw,
@@ -511,6 +983,15 @@ static const struct iio_info bmi160_info = {
.attrs = &bmi160_attrs_group,
};
+static const struct iio_info bmi160_info_fifo = {
+ .driver_module = THIS_MODULE,
+ .read_raw = bmi160_read_raw,
+ .write_raw = bmi160_write_raw,
+ .attrs = &bmi160_attrs_group,
+ .hwfifo_set_watermark = bmi160_set_watermark,
+ .hwfifo_flush_to_buffer = bmi160_fifo_flush,
+};
+
static const char *bmi160_match_acpi_device(struct device *dev)
{
const struct acpi_device_id *id;
@@ -572,12 +1053,75 @@ static void bmi160_chip_uninit(struct bmi160_data *data)
bmi160_set_mode(data, BMI160_ACCEL, false);
}
+static int bmi160_enable_irq(struct bmi160_data *data)
+{
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, BMI160_REG_INT_OUT_CTRL,
+ data->irq_data->output_en,
+ data->irq_data->output_en);
+ mutex_unlock(&data->mutex);
+
+ if (ret == 0)
+ data->irq_enabled = true;
+
+ return ret;
+}
+
+static int bmi160_disable_irq(struct bmi160_data *data)
+{
+ int ret;
+
+ if (!data->irq_enabled)
+ return 0;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, BMI160_REG_INT_OUT_CTRL,
+ data->irq_data->output_en, 0);
+ mutex_unlock(&data->mutex);
+
+ if (ret == 0)
+ data->irq_enabled = false;
+
+ return ret;
+}
+
+static irqreturn_t bmi160_irq_thread_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct bmi160_data *data = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(data->regmap);
+
+ mutex_lock(&data->mutex);
+ if (data->fifo_enabled)
+ __bmi160_fifo_flush(indio_dev, BMI160_FIFO_LENGTH, true);
+ else
+ dev_warn(dev,
+ "IRQ has been triggered, but FIFO is not enabled.\n");
+ mutex_unlock(&data->mutex);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bmi160_irq_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct bmi160_data *data = iio_priv(indio_dev);
+
+ data->timestamp = iio_get_time_ns(indio_dev);
+
+ return IRQ_WAKE_THREAD;
+}
+
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
- const char *name, bool use_spi)
+ const char *name, int irq,
+ bool use_spi, bool block_supported)
{
struct iio_dev *indio_dev;
struct bmi160_data *data;
int ret;
+ int irq2;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
@@ -585,6 +1129,7 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
+ data->irq = irq;
data->regmap = regmap;
mutex_init(&data->mutex);
@@ -603,15 +1148,57 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
indio_dev->info = &bmi160_info;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
- bmi160_trigger_handler, NULL);
+ bmi160_trigger_handler,
+ &bmi160_buffer_ops);
if (ret < 0)
goto uninit;
+ if (data->irq > 0) {
+ /* Check which interrupt pin is connected to our board */
+ irq2 = of_irq_get_byname(dev->of_node, "INT2");
+ if (irq2 == data->irq) {
+ dev_dbg(dev, "Using interrupt line INT2\n");
+ data->irq_data = &bmi160_irq2_data;
+ } else {
+ dev_dbg(dev, "Using interrupt line INT1\n");
+ data->irq_data = &bmi160_irq1_data;
+ }
+
+ ret = devm_request_threaded_irq(dev,
+ data->irq,
+ bmi160_irq_handler,
+ bmi160_irq_thread_handler,
+ IRQF_ONESHOT,
+ BMI160_IRQ_NAME,
+ indio_dev);
+ if (ret)
+ goto buffer_cleanup;
+
+ ret = bmi160_enable_irq(data);
+ if (ret < 0)
+ goto buffer_cleanup;
+
+ if (block_supported) {
+ indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
+ indio_dev->info = &bmi160_info_fifo;
+ indio_dev->buffer->attrs = bmi160_fifo_attributes;
+ data->fifo_buffer = devm_kmalloc(dev,
+ BMI160_FIFO_LENGTH,
+ GFP_KERNEL);
+ if (!data->fifo_buffer) {
+ ret = -ENOMEM;
+ goto disable_irq;
+ }
+ }
+ }
+
ret = iio_device_register(indio_dev);
if (ret < 0)
- goto buffer_cleanup;
+ goto disable_irq;
return 0;
+disable_irq:
+ bmi160_disable_irq(data);
buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
uninit:
@@ -626,6 +1213,7 @@ void bmi160_core_remove(struct device *dev)
struct bmi160_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
+ bmi160_disable_irq(data);
iio_triggered_buffer_cleanup(indio_dev);
bmi160_chip_uninit(data);
}
diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c
index 155a31f..1a3f4e1 100644
--- a/drivers/iio/imu/bmi160/bmi160_i2c.c
+++ b/drivers/iio/imu/bmi160/bmi160_i2c.c
@@ -24,6 +24,10 @@ static int bmi160_i2c_probe(struct i2c_client *client,
{
struct regmap *regmap;
const char *name = NULL;
+ bool block_supported =
+ i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK);
regmap = devm_regmap_init_i2c(client, &bmi160_regmap_config);
if (IS_ERR(regmap)) {
@@ -35,7 +39,8 @@ static int bmi160_i2c_probe(struct i2c_client *client,
if (id)
name = id->name;
- return bmi160_core_probe(&client->dev, regmap, name, false);
+ return bmi160_core_probe(&client->dev, regmap, name, client->irq,
+ false, block_supported);
}
static int bmi160_i2c_remove(struct i2c_client *client)
diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c
index d34dfdf..5a53225 100644
--- a/drivers/iio/imu/bmi160/bmi160_spi.c
+++ b/drivers/iio/imu/bmi160/bmi160_spi.c
@@ -26,7 +26,8 @@ static int bmi160_spi_probe(struct spi_device *spi)
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
- return bmi160_core_probe(&spi->dev, regmap, id->name, true);
+ return bmi160_core_probe(&spi->dev, regmap, id->name, spi->irq,
+ true, true);
}
static int bmi160_spi_remove(struct spi_device *spi)
--
2.10.2
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 5/6] iio: bmi160: Fix time needed to sleep after command execution
From: Marcin Niestroj @ 2016-12-08 14:22 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
Daniel Baluta, Gregor Boirie, Sanchayan Maity, Rob Herring,
Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Marcin Niestroj
In-Reply-To: <20161208142259.26230-1-m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
Datasheet specifies typical and maximum execution times for which CMD
register is occupied after previous command execution. We took these
values as minimum and maximum time for usleep_range() call before making
a new command execution.
To be sure, that the CMD register is no longer occupied we need to wait
*at least* the maximum time specified by datasheet.
Signed-off-by: Marcin Niestroj <m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
---
Patch introduced in v2
drivers/iio/imu/bmi160/bmi160_core.c | 25 ++++++-------------------
1 file changed, 6 insertions(+), 19 deletions(-)
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index 095533c..88bcf3f 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -67,10 +67,8 @@
#define BMI160_REG_DUMMY 0x7F
-#define BMI160_ACCEL_PMU_MIN_USLEEP 3200
-#define BMI160_ACCEL_PMU_MAX_USLEEP 3800
-#define BMI160_GYRO_PMU_MIN_USLEEP 55000
-#define BMI160_GYRO_PMU_MAX_USLEEP 80000
+#define BMI160_ACCEL_PMU_MIN_USLEEP 3800
+#define BMI160_GYRO_PMU_MIN_USLEEP 80000
#define BMI160_SOFTRESET_USLEEP 1000
#define BMI160_CHANNEL(_type, _axis, _index) { \
@@ -153,20 +151,9 @@ static struct bmi160_regs bmi160_regs[] = {
},
};
-struct bmi160_pmu_time {
- unsigned long min;
- unsigned long max;
-};
-
-static struct bmi160_pmu_time bmi160_pmu_time[] = {
- [BMI160_ACCEL] = {
- .min = BMI160_ACCEL_PMU_MIN_USLEEP,
- .max = BMI160_ACCEL_PMU_MAX_USLEEP
- },
- [BMI160_GYRO] = {
- .min = BMI160_GYRO_PMU_MIN_USLEEP,
- .max = BMI160_GYRO_PMU_MIN_USLEEP,
- },
+static unsigned long bmi160_pmu_time[] = {
+ [BMI160_ACCEL] = BMI160_ACCEL_PMU_MIN_USLEEP,
+ [BMI160_GYRO] = BMI160_GYRO_PMU_MIN_USLEEP,
};
struct bmi160_scale {
@@ -293,7 +280,7 @@ int bmi160_set_mode(struct bmi160_data *data, enum bmi160_sensor_type t,
if (ret < 0)
return ret;
- usleep_range(bmi160_pmu_time[t].min, bmi160_pmu_time[t].max);
+ usleep_range(bmi160_pmu_time[t], bmi160_pmu_time[t] + 1000);
return 0;
}
--
2.10.2
^ permalink raw reply related
* [PATCH v2 4/6] iio: bmi160: Protect data transmission with mutex
From: Marcin Niestroj @ 2016-12-08 14:22 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
Daniel Baluta, Gregor Boirie, Sanchayan Maity, Rob Herring,
Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Marcin Niestroj
In-Reply-To: <20161208142259.26230-1-m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
There are currently two possible cases for data transmission with the
sensor: read/write by iio sysfs and read in trigger interrupt
handler. Hence we need to protect data transmission using regmap_*
functions with mutex.
Signed-off-by: Marcin Niestroj <m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
---
Patch introduced in v2
drivers/iio/imu/bmi160/bmi160_core.c | 39 +++++++++++++++++++++++++++++-------
1 file changed, 32 insertions(+), 7 deletions(-)
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index e0251b8..095533c 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -2,6 +2,7 @@
* BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
*
* Copyright (c) 2016, Intel Corporation.
+ * Copyright (c) 2016, Grinn
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
@@ -112,6 +113,7 @@ enum bmi160_sensor_type {
struct bmi160_data {
struct regmap *regmap;
+ struct mutex mutex;
};
const struct regmap_config bmi160_regmap_config = {
@@ -285,7 +287,9 @@ int bmi160_set_mode(struct bmi160_data *data, enum bmi160_sensor_type t,
else
cmd = bmi160_regs[t].pmu_cmd_suspend;
+ mutex_lock(&data->mutex);
ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
+ mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
@@ -298,6 +302,7 @@ static
int bmi160_set_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
int uscale)
{
+ int ret;
int i;
for (i = 0; i < bmi160_scale_table[t].num; i++)
@@ -307,8 +312,12 @@ int bmi160_set_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
if (i == bmi160_scale_table[t].num)
return -EINVAL;
- return regmap_write(data->regmap, bmi160_regs[t].range,
- bmi160_scale_table[t].tbl[i].bits);
+ mutex_lock(&data->mutex);
+ ret = regmap_write(data->regmap, bmi160_regs[t].range,
+ bmi160_scale_table[t].tbl[i].bits);
+ mutex_unlock(&data->mutex);
+
+ return ret;
}
static
@@ -317,7 +326,9 @@ int bmi160_get_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
{
int i, ret, val;
+ mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
+ mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
@@ -340,7 +351,9 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type,
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(__le16);
+ mutex_lock(&data->mutex);
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(__le16));
+ mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
@@ -353,6 +366,7 @@ static
int bmi160_set_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
int odr, int uodr)
{
+ int ret;
int i;
for (i = 0; i < bmi160_odr_table[t].num; i++)
@@ -363,10 +377,14 @@ int bmi160_set_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
if (i >= bmi160_odr_table[t].num)
return -EINVAL;
- return regmap_update_bits(data->regmap,
- bmi160_regs[t].config,
- bmi160_regs[t].config_odr_mask,
- bmi160_odr_table[t].tbl[i].bits);
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap,
+ bmi160_regs[t].config,
+ bmi160_regs[t].config_odr_mask,
+ bmi160_odr_table[t].tbl[i].bits);
+ mutex_unlock(&data->mutex);
+
+ return ret;
}
static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
@@ -374,7 +392,9 @@ static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
{
int i, val, ret;
+ mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
+ mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
@@ -402,14 +422,18 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
__le16 sample;
+ mutex_lock(&data->mutex);
for_each_set_bit(i, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = regmap_bulk_read(data->regmap, base + i * sizeof(__le16),
&sample, sizeof(__le16));
- if (ret < 0)
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
goto done;
+ }
buf[j++] = sample;
}
+ mutex_unlock(&data->mutex);
iio_push_to_buffers_with_timestamp(indio_dev, buf,
iio_get_time_ns(indio_dev));
@@ -575,6 +599,7 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
data->regmap = regmap;
+ mutex_init(&data->mutex);
ret = bmi160_chip_init(data, use_spi);
if (ret < 0)
--
2.10.2
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 3/6] Documentation: DT: Add bmi160 imu binding
From: Marcin Niestroj @ 2016-12-08 14:22 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
Daniel Baluta, Gregor Boirie, Sanchayan Maity, Rob Herring,
Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Marcin Niestroj
In-Reply-To: <20161208142259.26230-1-m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
This adds documentation for Bosch BMI160 Inertial Measurement Unit
device-tree bindings.
Signed-off-by: Marcin Niestroj <m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
---
Changes v1 -> v2 (suggested by Rob):
* remove "gpio" keyword from interrupts property description
* describe "INT1" and "INT2" cases for interrupt-names property
.../devicetree/bindings/iio/imu/bmi160.txt | 36 ++++++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/imu/bmi160.txt
diff --git a/Documentation/devicetree/bindings/iio/imu/bmi160.txt b/Documentation/devicetree/bindings/iio/imu/bmi160.txt
new file mode 100644
index 0000000..ae0112c
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/imu/bmi160.txt
@@ -0,0 +1,36 @@
+Bosch BMI160 - Inertial Measurement Unit with Accelerometer, Gyroscope
+and externally connectable Magnetometer
+
+https://www.bosch-sensortec.com/bst/products/all_products/bmi160
+
+Required properties:
+ - compatible : should be "bosch,bmi160"
+ - reg : the I2C address or SPI chip select number of the sensor
+ - spi-max-frequency : set maximum clock frequency (only for SPI)
+
+Optional properties:
+ - interrupt-parent : should be the phandle of the interrupt controller
+ - interrupts : interrupt mapping for IRQ, must be IRQ_TYPE_LEVEL_LOW
+ - interrupt-names : set to "INT1" if INT1 pin should be used as interrupt
+ input, set to "INT2" if INT2 pin should be used instead
+
+Examples:
+
+bmi160@68 {
+ compatible = "bosch,bmi160";
+ reg = <0x68>;
+
+ interrupt-parent = <&gpio4>;
+ interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "INT1";
+};
+
+bmi160@0 {
+ compatible = "bosch,bmi160";
+ reg = <0>;
+ spi-max-frequency = <10000000>;
+
+ interrupt-parent = <&gpio2>;
+ interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "INT2";
+};
--
2.10.2
^ permalink raw reply related
* [PATCH v2 2/6] iio: bmi160: Add of device table for spi
From: Marcin Niestroj @ 2016-12-08 14:22 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
Daniel Baluta, Gregor Boirie, Sanchayan Maity, Rob Herring,
Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Marcin Niestroj
In-Reply-To: <20161208142259.26230-1-m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
>From now on we can add bmi160 device to device-tree by specifying
compatible string.
Signed-off-by: Marcin Niestroj <m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
---
Patch introduced in v2
drivers/iio/imu/bmi160/bmi160_spi.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c
index 1ec8b12..d34dfdf 100644
--- a/drivers/iio/imu/bmi160/bmi160_spi.c
+++ b/drivers/iio/imu/bmi160/bmi160_spi.c
@@ -7,10 +7,11 @@
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*/
+#include <linux/acpi.h>
#include <linux/module.h>
-#include <linux/spi/spi.h>
+#include <linux/of.h>
#include <linux/regmap.h>
-#include <linux/acpi.h>
+#include <linux/spi/spi.h>
#include "bmi160.h"
@@ -47,13 +48,22 @@ static const struct acpi_device_id bmi160_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
+#ifdef CONFIG_OF
+static const struct of_device_id bmi160_of_match[] = {
+ { .compatible = "bosch,bmi160" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bmi160_of_match);
+#endif
+
static struct spi_driver bmi160_spi_driver = {
.probe = bmi160_spi_probe,
.remove = bmi160_spi_remove,
.id_table = bmi160_spi_id,
.driver = {
- .acpi_match_table = ACPI_PTR(bmi160_acpi_match),
- .name = "bmi160_spi",
+ .acpi_match_table = ACPI_PTR(bmi160_acpi_match),
+ .of_match_table = of_match_ptr(bmi160_of_match),
+ .name = "bmi160_spi",
},
};
module_spi_driver(bmi160_spi_driver);
--
2.10.2
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 1/6] iio: bmi160: Add of device table for i2c
From: Marcin Niestroj @ 2016-12-08 14:22 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
Daniel Baluta, Gregor Boirie, Sanchayan Maity, Rob Herring,
Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Marcin Niestroj
In-Reply-To: <20161208142259.26230-1-m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
>From now on we can add bmi160 device to device-tree by specifying
compatible string.
Signed-off-by: Marcin Niestroj <m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
---
Patch introduced in v2
drivers/iio/imu/bmi160/bmi160_i2c.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c
index 07a179d..155a31f 100644
--- a/drivers/iio/imu/bmi160/bmi160_i2c.c
+++ b/drivers/iio/imu/bmi160/bmi160_i2c.c
@@ -11,10 +11,11 @@
* - 0x68 if SDO is pulled to GND
* - 0x69 if SDO is pulled to VDDIO
*/
-#include <linux/module.h>
+#include <linux/acpi.h>
#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
#include <linux/regmap.h>
-#include <linux/acpi.h>
#include "bmi160.h"
@@ -56,10 +57,19 @@ static const struct acpi_device_id bmi160_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
+#ifdef CONFIG_OF
+static const struct of_device_id bmi160_of_match[] = {
+ { .compatible = "bosch,bmi160" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bmi160_of_match);
+#endif
+
static struct i2c_driver bmi160_i2c_driver = {
.driver = {
.name = "bmi160_i2c",
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
+ .of_match_table = of_match_ptr(bmi160_of_match),
},
.probe = bmi160_i2c_probe,
.remove = bmi160_i2c_remove,
--
2.10.2
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 0/6] iio: bmi160: cleanups and hardware fifo support
From: Marcin Niestroj @ 2016-12-08 14:22 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Peter Meerwald-Stadler, Hartmut Knaack, Lars-Peter Clausen,
Daniel Baluta, Gregor Boirie, Sanchayan Maity, Rob Herring,
Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Marcin Niestroj
Hi,
This patch series consists of cleanups, of device tables, device-tree
bindings documentation and finally support for hardware FIFO.
Patches have been rebased and tested on 4.9.0-rc8.
Changes v1 -> v2:
* add of device tables for spi and i2c (suggested by Jonathan)
* device-tree bindings documentation: remove "gpio" keyword from
interrupts property description, describe "INT1" and "INT2" cases
for interrupt-names property (suggested by Rob)
* introduce mutex protection of data transmission as a separate patch
(suggested by Peter)
* fix time needed to sleep after command execution
* add __ prefix to all functions that should be called with mutex held
(suggested by Peter)
* get rid of non-constant array size (suggested by Peter)
* disable irq on init failure and module removal (suggested by Peter)
* make bmi160_buffer_predisable() the reverse order of the
bmi160_buffer_postenable() (suggested by Jonathan)
* remove realignment for iio_info structs (suggested by Jonathan)
Marcin Niestroj (6):
iio: bmi160: Add of device table for i2c
iio: bmi160: Add of device table for spi
Documentation: DT: Add bmi160 imu binding
iio: bmi160: Protect data transmission with mutex
iio: bmi160: Fix time needed to sleep after command execution
iio: bmi160: Support hardware fifo
.../devicetree/bindings/iio/imu/bmi160.txt | 36 ++
drivers/iio/imu/bmi160/bmi160.h | 3 +-
drivers/iio/imu/bmi160/bmi160_core.c | 678 +++++++++++++++++++--
drivers/iio/imu/bmi160/bmi160_i2c.c | 21 +-
drivers/iio/imu/bmi160/bmi160_spi.c | 21 +-
5 files changed, 711 insertions(+), 48 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/imu/bmi160.txt
--
2.10.2
--
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 v4 2/2] mmc: sdhci-cadence: add Cadence SD4HC support
From: Ulf Hansson @ 2016-12-08 14:05 UTC (permalink / raw)
To: Masahiro Yamada
Cc: linux-mmc@vger.kernel.org, Adrian Hunter, Douglas Anderson,
devicetree@vger.kernel.org, Al Cooper,
linux-kernel@vger.kernel.org, Stefan Wahren, Rob Herring,
Andrei Pistirica, Wolfram Sang, Joshua Henderson, Mark Rutland,
Simon Horman, Eric Anholt
In-Reply-To: <CAK7LNAQkJO0vN9T-pB3ys4N=3pG9wdWZ9z+VkYToL7F_fLo9dA@mail.gmail.com>
On 8 December 2016 at 13:52, Masahiro Yamada
<yamada.masahiro@socionext.com> wrote:
> Hi Ulf,
>
> 2016-12-08 21:32 GMT+09:00 Ulf Hansson <ulf.hansson@linaro.org>:
>> On 5 December 2016 at 03:10, Masahiro Yamada
>> <yamada.masahiro@socionext.com> wrote:
>>> Add a driver for the Cadence SD4HC SD/SDIO/eMMC Controller.
>>>
>>> For SD, it basically relies on the SDHCI standard code.
>>> For eMMC, this driver provides some callbacks to support the
>>> hardware part that is specific to this IP design.
>>>
>>> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
>>
>> Thanks, applied for next!
>>
>
>
> Very sorry for my fix at the last minute.
>
> I've just posted v5.
>
> Please make sure to apply v5.
Okay, I have replaced v4 with v5.
Perhaps you should have a look at my next branch to make sure it's all okay.
Kind regards
Uffe
^ permalink raw reply
* Re: [PATCH v3 6/6] net: smmac: allow configuring lower pbl values
From: Andreas Färber @ 2016-12-08 13:44 UTC (permalink / raw)
To: Alexandre Torgue, Niklas Cassel, Rob Herring, Mark Rutland,
Jonathan Corbet, Giuseppe Cavallaro, David S. Miller, Phil Reid,
Eric Engestrom, Pavel Machek, Joachim Eastwood, Vincent Palatin,
Gabriel Fernandez
Cc: Niklas Cassel, netdev, devicetree, linux-kernel, linux-doc
In-Reply-To: <4d43479f-8373-308b-8c7d-cfed5346dc53@st.com>
Hi,
In subject: s/smmac/stmmac/
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
^ permalink raw reply
* Re: [PATCH v4 2/2] mmc: sdhci-cadence: add Cadence SD4HC support
From: Masahiro Yamada @ 2016-12-08 12:52 UTC (permalink / raw)
To: Ulf Hansson
Cc: linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Adrian Hunter,
Douglas Anderson,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Al Cooper,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Stefan Wahren, Rob Herring, Andrei Pistirica, Wolfram Sang,
Joshua Henderson, Mark Rutland, Simon Horman, Eric Anholt
In-Reply-To: <CAPDyKFqbAzCyXArikgTUeTrs9vOzfwKWJcs3TgF7D-4f_xfjfQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Ulf,
2016-12-08 21:32 GMT+09:00 Ulf Hansson <ulf.hansson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>:
> On 5 December 2016 at 03:10, Masahiro Yamada
> <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org> wrote:
>> Add a driver for the Cadence SD4HC SD/SDIO/eMMC Controller.
>>
>> For SD, it basically relies on the SDHCI standard code.
>> For eMMC, this driver provides some callbacks to support the
>> hardware part that is specific to this IP design.
>>
>> Signed-off-by: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
>
> Thanks, applied for next!
>
Very sorry for my fix at the last minute.
I've just posted v5.
Please make sure to apply v5.
Thanks!
--
Best Regards
Masahiro Yamada
--
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
* [PATCH v5 2/2] mmc: sdhci-cadence: add Cadence SD4HC support
From: Masahiro Yamada @ 2016-12-08 12:50 UTC (permalink / raw)
To: linux-mmc
Cc: Adrian Hunter, Ulf Hansson, Masahiro Yamada, Douglas Anderson,
devicetree, Al Cooper, linux-kernel, Stefan Wahren, Rob Herring,
Andrei Pistirica, Wolfram Sang, Mark Rutland, Simon Horman
In-Reply-To: <1481201455-3483-1-git-send-email-yamada.masahiro@socionext.com>
Add a driver for the Cadence SD4HC SD/SDIO/eMMC Controller.
For SD, it basically relies on the SDHCI standard code.
For eMMC, this driver provides some callbacks to support the
hardware part that is specific to this IP design.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes in v5:
- Fix calculation of the center of the tuned window
Changes in v4:
- Override mmc_host_ops.execute_tuning instead of the
.platform_execute_tuning implementation
Changes in v3:
- Remove unneeded explanation about HRS and SRS from DT binding;
the offsets to HRS/SRS are fixed for this hardware and this is
quite normal, like each hardware has a fixed register view except
the register base. The detailed register map is what the driver
cares about, so no need to explain it in the binding.
Changes in v2:
- Remove unnecessary "select MMC_SDHCI_IO_ACCESSORS"
.../devicetree/bindings/mmc/sdhci-cadence.txt | 30 +++
drivers/mmc/host/Kconfig | 11 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-cadence.c | 283 +++++++++++++++++++++
4 files changed, 325 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-cadence.txt
create mode 100644 drivers/mmc/host/sdhci-cadence.c
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-cadence.txt b/Documentation/devicetree/bindings/mmc/sdhci-cadence.txt
new file mode 100644
index 0000000..750374f
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-cadence.txt
@@ -0,0 +1,30 @@
+* Cadence SD/SDIO/eMMC Host Controller
+
+Required properties:
+- compatible: should be "cdns,sd4hc".
+- reg: offset and length of the register set for the device.
+- interrupts: a single interrupt specifier.
+- clocks: phandle to the input clock.
+
+Optional properties:
+For eMMC configuration, supported speed modes are not indicated by the SDHCI
+Capabilities Register. Instead, the following properties should be specified
+if supported. See mmc.txt for details.
+- mmc-ddr-1_8v
+- mmc-ddr-1_2v
+- mmc-hs200-1_8v
+- mmc-hs200-1_2v
+- mmc-hs400-1_8v
+- mmc-hs400-1_2v
+
+Example:
+ emmc: sdhci@5a000000 {
+ compatible = "cdns,sd4hc";
+ reg = <0x5a000000 0x400>;
+ interrupts = <0 78 4>;
+ clocks = <&clk 4>;
+ bus-width = <8>;
+ mmc-ddr-1_8v;
+ mmc-hs200-1_8v;
+ mmc-hs400-1_8v;
+ };
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index ab9181e..8ac1640 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -164,6 +164,17 @@ config MMC_SDHCI_OF_HLWD
If unsure, say N.
+config MMC_SDHCI_CADENCE
+ tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
+ depends on MMC_SDHCI_PLTFM
+ depends on OF
+ help
+ This selects the Cadence SD/SDIO/eMMC driver.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_SDHCI_CNS3XXX
tristate "SDHCI support on the Cavium Networks CNS3xxx SoC"
depends on ARCH_CNS3XXX
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e49a82a..55f7193 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
+obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
new file mode 100644
index 0000000..1501cfd
--- /dev/null
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mmc/host.h>
+
+#include "sdhci-pltfm.h"
+
+/* HRS - Host Register Set (specific to Cadence) */
+#define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
+#define SDHCI_CDNS_HRS04_ACK BIT(26)
+#define SDHCI_CDNS_HRS04_RD BIT(25)
+#define SDHCI_CDNS_HRS04_WR BIT(24)
+#define SDHCI_CDNS_HRS04_RDATA_SHIFT 12
+#define SDHCI_CDNS_HRS04_WDATA_SHIFT 8
+#define SDHCI_CDNS_HRS04_ADDR_SHIFT 0
+
+#define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
+#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
+#define SDHCI_CDNS_HRS06_TUNE_SHIFT 8
+#define SDHCI_CDNS_HRS06_TUNE_MASK 0x3f
+#define SDHCI_CDNS_HRS06_MODE_MASK 0x7
+#define SDHCI_CDNS_HRS06_MODE_SD 0x0
+#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
+#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
+
+/* SRS - Slot Register Set (SDHCI-compatible) */
+#define SDHCI_CDNS_SRS_BASE 0x200
+
+/* PHY */
+#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
+#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
+#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
+#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
+#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
+#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
+
+/*
+ * The tuned val register is 6 bit-wide, but not the whole of the range is
+ * available. The range 0-42 seems to be available (then 43 wraps around to 0)
+ * but I am not quite sure if it is official. Use only 0 to 39 for safety.
+ */
+#define SDHCI_CDNS_MAX_TUNING_LOOP 40
+
+struct sdhci_cdns_priv {
+ void __iomem *hrs_addr;
+};
+
+static void sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
+ u8 addr, u8 data)
+{
+ void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04;
+ u32 tmp;
+
+ tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) |
+ (addr << SDHCI_CDNS_HRS04_ADDR_SHIFT);
+ writel(tmp, reg);
+
+ tmp |= SDHCI_CDNS_HRS04_WR;
+ writel(tmp, reg);
+
+ tmp &= ~SDHCI_CDNS_HRS04_WR;
+ writel(tmp, reg);
+}
+
+static void sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)
+{
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_SD_HS, 4);
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_SD_DEFAULT, 4);
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_EMMC_LEGACY, 9);
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_EMMC_SDR, 2);
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_EMMC_DDR, 3);
+}
+
+static inline void *sdhci_cdns_priv(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+ return sdhci_pltfm_priv(pltfm_host);
+}
+
+static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
+{
+ /*
+ * Cadence's spec says the Timeout Clock Frequency is the same as the
+ * Base Clock Frequency. Divide it by 1000 to return a value in kHz.
+ */
+ return host->max_clk / 1000;
+}
+
+static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int timing)
+{
+ struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+ u32 mode, tmp;
+
+ switch (timing) {
+ case MMC_TIMING_MMC_HS:
+ mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
+ break;
+ case MMC_TIMING_MMC_DDR52:
+ mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;
+ break;
+ case MMC_TIMING_MMC_HS200:
+ mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
+ break;
+ case MMC_TIMING_MMC_HS400:
+ mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
+ break;
+ default:
+ mode = SDHCI_CDNS_HRS06_MODE_SD;
+ break;
+ }
+
+ /* The speed mode for eMMC is selected by HRS06 register */
+ tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
+ tmp &= ~SDHCI_CDNS_HRS06_MODE_MASK;
+ tmp |= mode;
+ writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
+
+ /* For SD, fall back to the default handler */
+ if (mode == SDHCI_CDNS_HRS06_MODE_SD)
+ sdhci_set_uhs_signaling(host, timing);
+}
+
+static const struct sdhci_ops sdhci_cdns_ops = {
+ .set_clock = sdhci_set_clock,
+ .get_timeout_clock = sdhci_cdns_get_timeout_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = {
+ .ops = &sdhci_cdns_ops,
+};
+
+static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
+{
+ struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+ void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
+ u32 tmp;
+
+ if (WARN_ON(val > SDHCI_CDNS_HRS06_TUNE_MASK))
+ return -EINVAL;
+
+ tmp = readl(reg);
+ tmp &= ~(SDHCI_CDNS_HRS06_TUNE_MASK << SDHCI_CDNS_HRS06_TUNE_SHIFT);
+ tmp |= val << SDHCI_CDNS_HRS06_TUNE_SHIFT;
+ tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
+ writel(tmp, reg);
+
+ return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
+ 0, 1);
+}
+
+static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ int cur_streak = 0;
+ int max_streak = 0;
+ int end_of_streak = 0;
+ int i;
+
+ /*
+ * This handler only implements the eMMC tuning that is specific to
+ * this controller. Fall back to the standard method for SD timing.
+ */
+ if (host->timing != MMC_TIMING_MMC_HS200)
+ return sdhci_execute_tuning(mmc, opcode);
+
+ if (WARN_ON(opcode != MMC_SEND_TUNING_BLOCK_HS200))
+ return -EINVAL;
+
+ for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) {
+ if (sdhci_cdns_set_tune_val(host, i) ||
+ mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */
+ cur_streak = 0;
+ } else { /* good */
+ cur_streak++;
+ if (cur_streak > max_streak) {
+ max_streak = cur_streak;
+ end_of_streak = i;
+ }
+ }
+ }
+
+ if (!max_streak) {
+ dev_err(mmc_dev(host->mmc), "no tuning point found\n");
+ return -EIO;
+ }
+
+ return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
+}
+
+static int sdhci_cdns_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_cdns_priv *priv;
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ host = sdhci_pltfm_init(pdev, &sdhci_cdns_pltfm_data, sizeof(*priv));
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ goto disable_clk;
+ }
+
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->clk = clk;
+
+ priv = sdhci_cdns_priv(host);
+ priv->hrs_addr = host->ioaddr;
+ host->ioaddr += SDHCI_CDNS_SRS_BASE;
+ host->mmc_host_ops.execute_tuning = sdhci_cdns_execute_tuning;
+
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto free;
+
+ sdhci_cdns_phy_init(priv);
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto free;
+
+ return 0;
+free:
+ sdhci_pltfm_free(pdev);
+disable_clk:
+ clk_disable_unprepare(clk);
+
+ return ret;
+}
+
+static const struct of_device_id sdhci_cdns_match[] = {
+ { .compatible = "cdns,sd4hc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
+
+static struct platform_driver sdhci_cdns_driver = {
+ .driver = {
+ .name = "sdhci-cdns",
+ .pm = &sdhci_pltfm_pmops,
+ .of_match_table = sdhci_cdns_match,
+ },
+ .probe = sdhci_cdns_probe,
+ .remove = sdhci_pltfm_unregister,
+};
+module_platform_driver(sdhci_cdns_driver);
+
+MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
+MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver");
+MODULE_LICENSE("GPL");
--
2.7.4
^ 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