* Re: [PATCH v2 3/4] Regulators: TPS65218: Add Regulator driver for TPS65218 PMIC
From: Mark Brown @ 2014-02-07 17:27 UTC (permalink / raw)
To: Keerthy
Cc: Keerthy, rob.herring-bsGFqQB8/DxBDgjK7y7TUQ,
pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
swarren-3lzwWm7+Weoh9ZMKESR00Q,
ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg, rob-VoJi6FS/r0vR7s880joybQ,
sameo-VuQAYsv1563Yd54FQh9/CA, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
grant.likely-QSEj5FYQhm4dnm+yROfE0A,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <52F4E685.4060605-l0cyMroinI0@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 265 bytes --]
On Fri, Feb 07, 2014 at 07:28:29PM +0530, Keerthy wrote:
> On Thursday 06 February 2014 11:20 AM, Keerthy wrote:
> If there are no further comments on this could you
> please pull this?
You've sent this mail a day after sending the original patch, don't do
that.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] spi: Add Qualcomm QUP SPI controller support
From: Josh Cartwright @ 2014-02-07 17:20 UTC (permalink / raw)
To: Mark Brown
Cc: Kumar Gala, Ivan T. Ivanov, Grant Likely, Rob Herring, linux-spi,
linux-arm-msm, linux-kernel, devicetree, Alok Chauhan,
Gilad Avidov, Kiran Gunda, Sagar Dharia, Andy Gross
In-Reply-To: <20140207171834.GF1757@sirena.org.uk>
Hey Mark-
On Fri, Feb 07, 2014 at 05:18:34PM +0000, Mark Brown wrote:
> On Fri, Feb 07, 2014 at 10:51:27AM -0600, Josh Cartwright wrote:
>
> > config SPI_QUP
> > tristate "Qualcomm SPI Support with QUP interface"
> > depends on OF
> > depends on ARM
>
> Does this really depend on ARM? If so why?
The ARM dependency is there for the use of _relaxed io accessor
variants.
> > depends on ARCH_MSM_DT || COMPILE_TEST
>
> > With Kumar's pending the ARCH_MSM_DT -> ARCH_QCOM rename, we'll
> > introduce a arm-soc/spi tree dependency here that we'll need to keep
> > track of.
>
> It seems simpler to just depend on MSM_DT || ARCH_QCOM or whatever.
ARCH_MSM_DT is going away, so maybe this is the best option for the
short term (a later patch can remove ARCH_MSM_DT from here at some point
in the future).
Josh
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* Re: [PATCH 2/2] spi: Add Qualcomm QUP SPI controller support
From: Mark Brown @ 2014-02-07 17:18 UTC (permalink / raw)
To: Josh Cartwright
Cc: Kumar Gala, Ivan T. Ivanov, Grant Likely, Rob Herring,
linux-spi-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Alok Chauhan, Gilad Avidov,
Kiran Gunda, Sagar Dharia, Andy Gross
In-Reply-To: <20140207165127.GV20228-OP5zVEFNDbfdOxZ39nK119BPR1lH4CV8@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 484 bytes --]
On Fri, Feb 07, 2014 at 10:51:27AM -0600, Josh Cartwright wrote:
> config SPI_QUP
> tristate "Qualcomm SPI Support with QUP interface"
> depends on OF
> depends on ARM
Does this really depend on ARM? If so why?
> depends on ARCH_MSM_DT || COMPILE_TEST
> With Kumar's pending the ARCH_MSM_DT -> ARCH_QCOM rename, we'll
> introduce a arm-soc/spi tree dependency here that we'll need to keep
> track of.
It seems simpler to just depend on MSM_DT || ARCH_QCOM or whatever.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: Fwd: [PATCH 2/2] spi: Add Qualcomm QUP SPI controller support
From: Mark Brown @ 2014-02-07 17:16 UTC (permalink / raw)
To: dsneddon
Cc: iivanov, grant.likely, robh+dt, linux-spi, linux-arm-msm,
inux-kernel, devicetree, alokc, gavidov, kgunda, sdharia,
Andy Gross
In-Reply-To: <214fe9fc7e62ab30bdfbb4ac5d1ee250.squirrel@www.codeaurora.org>
[-- Attachment #1: Type: text/plain, Size: 453 bytes --]
On Fri, Feb 07, 2014 at 04:34:25PM -0000, dsneddon@codeaurora.org wrote:
> > From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
> >
> > Qualcomm Universal Peripheral (QUP) core is an AHB slave that
> > provides a common data path (an output FIFO and an input FIFO)
Folks, please remember to delete irrelevant context from your e-mails,
it's easy for the reader not to see things if they have to go through
pages of quote to find one or two lines of new text.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] spi: Add Qualcomm QUP SPI controller support
From: Mark Brown @ 2014-02-07 17:12 UTC (permalink / raw)
To: Ivan T. Ivanov
Cc: Grant Likely, Rob Herring, linux-spi-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Alok Chauhan, Gilad Avidov,
Kiran Gunda, Sagar Dharia
In-Reply-To: <1391705868-20091-3-git-send-email-iivanov-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 4601 bytes --]
On Thu, Feb 06, 2014 at 06:57:48PM +0200, Ivan T. Ivanov wrote:
This looks mostly good, there's a few odd things and missing use of
framework features.
> Qualcomm Universal Peripheral (QUP) core is an AHB slave that
> provides a common data path (an output FIFO and an input FIFO)
> for serial peripheral interface (SPI) mini-core. SPI in master mode
> support up to 50MHz, up to four chip selects, and a programmable
> data path from 4 bits to 32 bits; MODE0..3 protocols
The grammar in this and the Kconfig text is a bit garbled, might want to
give it a once over (support -> supports for example).
> +static void spi_qup_deassert_cs(struct spi_qup *controller,
> + struct spi_qup_device *chip)
> +{
> + if (chip->mode & SPI_CS_HIGH)
> + iocontol &= ~mask;
> + else
> + iocontol |= mask;
Implement a set_cs() operation and let the core worry about all this
for you as well as saving two implementations.
> + word = 0;
> + for (idx = 0; idx < controller->bytes_per_word &&
> + controller->tx_bytes < xfer->len; idx++,
> + controller->tx_bytes++) {
> +
> + if (!tx_buf)
> + continue;
Do you need to set the _MUST_TX flag?
> + qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
> + spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS);
> + opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
> +
> + writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
> + writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
> + writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
> +
> + if (!xfer)
> + return IRQ_HANDLED;
Are you sure? It seems wrong to just ignore interrupts, some comments
would help explain why.
> +static int spi_qup_transfer_do(struct spi_qup *controller,
> + struct spi_qup_device *chip,
> + struct spi_transfer *xfer)
This looks like a transfer_one() function, please use the framework
features where you can.
> + if (controller->speed_hz != chip->speed_hz) {
> + ret = clk_set_rate(controller->cclk, chip->speed_hz);
> + if (ret) {
> + dev_err(controller->dev, "fail to set frequency %d",
> + chip->speed_hz);
> + return -EIO;
> + }
> + }
Is calling into the clock framework really so expensive that we need to
avoid doing it? You also shouldn't be interacting with the hardware in
setup().
> + if (chip->bits_per_word <= 8)
> + controller->bytes_per_word = 1;
> + else if (chip->bits_per_word <= 16)
> + controller->bytes_per_word = 2;
> + else
> + controller->bytes_per_word = 4;
This looks like a switch statement, and looking at the above it's not
clear that the device actually supports anything other than whole bytes.
I'm not sure what that would mean from an API point of view.
> +static int spi_qup_transfer_one(struct spi_master *master,
> + struct spi_message *msg)
> +{
This entire function can be removed, the core can do it for you.
> + if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq))
> + max_freq = 19200000;
> +
> + if (!max_freq) {
> + dev_err(dev, "invalid clock frequency %d\n", max_freq);
> + return -ENXIO;
> + }
> +
> + ret = clk_set_rate(cclk, max_freq);
> + if (ret)
> + dev_warn(dev, "fail to set SPI frequency %d\n", max_freq);
You set the clock rate per transfer so why bother setting it here,
perhaps we support the rate the devices request but not this maximum
rate?
> + master->num_chipselect = SPI_NUM_CHIPSELECTS;
> + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
Are you *sure* the device supports anything other than whole bytes?
> + ret = devm_spi_register_master(dev, master);
> + if (!ret) {
> + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
> + pm_runtime_use_autosuspend(dev);
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
> + return ret;
> + }
This is really unclearly written, the success case looks like error
handling.
> +#ifdef CONFIG_PM_RUNTIME
> +static int spi_qup_pm_suspend_runtime(struct device *device)
> +{
> + struct spi_master *master = dev_get_drvdata(device);
> + struct spi_qup *controller = spi_master_get_devdata(master);
> +
> + disable_irq(controller->irq);
Why do you need to disable the interrupt? Will the hardware generate
spurious interrupts, if so some documentation is in order.
> +static int spi_qup_pm_resume_runtime(struct device *device)
> +{
> + struct spi_master *master = dev_get_drvdata(device);
> + struct spi_qup *controller = spi_master_get_devdata(master);
> +
> + clk_prepare_enable(controller->cclk);
> + clk_prepare_enable(controller->iclk);
> + enable_irq(controller->irq);
No error checking here...
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v6 05/19] watchdog: orion: Make sure the watchdog is initially stopped
From: Guenter Roeck @ 2014-02-07 16:55 UTC (permalink / raw)
To: Jason Cooper
Cc: Ezequiel Garcia, Jason Gunthorpe,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Wim Van Sebroeck,
Thomas Petazzoni, Gregory Clement, Lior Amsalem,
Sebastian Hesselbarth, Andrew Lunn
In-Reply-To: <20140207154453.GA8533-u4khhh1J0LxI1Ri9qeTfzeTW4wlIGRCZ@public.gmane.org>
On Fri, Feb 07, 2014 at 10:44:53AM -0500, Jason Cooper wrote:
> On Fri, Feb 07, 2014 at 12:17:28PM -0300, Ezequiel Garcia wrote:
> > On Fri, Feb 07, 2014 at 05:38:09AM -0800, Guenter Roeck wrote:
> > > On 02/07/2014 02:40 AM, Ezequiel Garcia wrote:
> > > > On Thu, Feb 06, 2014 at 06:02:56PM -0800, Guenter Roeck wrote:
> > > >> On 02/06/2014 09:20 AM, Ezequiel Garcia wrote:
> > > >>> Having the watchdog initially fully stopped is important to avoid
> > > >>> any spurious watchdog triggers, in case the registers are not in
> > > >>> its reset state.
> > > >>>
> > > >>> Reviewed-by: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
> > > >>> Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> > > >>> Tested-by: Willy Tarreau <w@1wt.eu>
> > > >>> Signed-off-by: Ezequiel Garcia <ezequiel.garcia-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > > >>> ---
> > > >>> drivers/watchdog/orion_wdt.c | 3 +++
> > > >>> 1 file changed, 3 insertions(+)
> > > >>>
> > > >>> diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
> > > >>> index 6746033..2dbeee9 100644
> > > >>> --- a/drivers/watchdog/orion_wdt.c
> > > >>> +++ b/drivers/watchdog/orion_wdt.c
> > > >>> @@ -142,6 +142,9 @@ static int orion_wdt_probe(struct platform_device *pdev)
> > > >>> orion_wdt.max_timeout = wdt_max_duration;
> > > >>> watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev);
> > > >>>
> > > >>> + /* Let's make sure the watchdog is fully stopped */
> > > >>> + orion_wdt_stop(&orion_wdt);
> > > >>> +
> > > >>
> > > >> Actually we just had that in another driver, and I stumbled over it there.
> > > >>
> > > >> Problem with stopping the watchdog in probe unconditionally is that you can
> > > >> use it to defeat nowayout: unload the module, then load it again,
> > > >> and the watchdog is stopped even if nowayout is true.
> > > >>
>
> How often would a user legitimately want to unload/load the watchdog
> module?
>
Not sure what you are saying here. If you don't think the nowayout option
should be supported, drop it. Don't claim it is supported when it isn't.
> > > >
> > > > Hm... I see.
> > > >
> > > >> Is this really what you want ? Or, in other words, what is the problem
> > > >> you are trying to solve ?
> > > >>
> > > >
> > > > Well, this is related to the discussion about the bootloader not
> > > > reseting the watchdog properly, provoking spurious watchdog triggering.
> > > >
> > > > Jason Gunthorpe explained [1] that we needed a particular sequence:
> > > >
> > > > 1. Disable WDT
> > > > 2. Clear bridge
> > > > 3. Enable WDT
> > > >
> > > > We added the irq handling to satisfy (2), and the watchdog stop for (1).
> > > >
> > > > The watchdog stop was agreed specifically [2].
> > > >
> > > > Ideas?
> > > >
> > >
> > > Other drivers assume that if the watchdog is running, it is supposed
> > > to be running. The more common approach in such cases is to ping the
> > > watchdog once to give userspace more time to get ready, but leave
> > > it enabled. So you could check if the watchdog is enabled, and if
> > > it was enabled re-enable it after initialization is complete
> > > (and maybe log a message stating that the watchdog is enabled).
> > >
> > > If you don't want to do that, and if you are defeating nowayout
> > > on purpose to fix a problem with a broken bootloader,
> > > you should at least put in comment describing the problem you are
> > > trying to solve, and that you accept breaking nowayout with your fix.
>
> Yes, this should be commented.
>
> > I'm not fond of not having "nowayout" option on our driver, given I'm sure
> > it's a watchdog feature for a good reason.
> >
> > On the other side, I can't see how can we distinguish a previously
> > and explicitly enabled watchdog, from a spurious enable by broken bootloader.
>
> How about we just don't define module_exit() and leave a comment as
> such? It's not unprecedented, a couple of the atm drivers are
> explicitly setup like this (uPD98402.c, zatm.c, eni.c).
>
Or don't support tristate in the first place. There was some argument from
others earlier that a watchdog should never be optional. Or drop the nowayout
option from the driver.
There is one problem, though, if you don't support a pre-enabled watchdog:
If the system dies before the watchdog application starts running, it will
hang. This is the reason why the watchdog is enabled on purpose by some
boot loaders.
Guenter
--
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] spi: Add Qualcomm QUP SPI controller support
From: Josh Cartwright @ 2014-02-07 16:51 UTC (permalink / raw)
To: Kumar Gala
Cc: Ivan T. Ivanov, Mark Brown, Grant Likely, Rob Herring, linux-spi,
linux-arm-msm, linux-kernel, devicetree, Alok Chauhan,
Gilad Avidov, Kiran Gunda, Sagar Dharia, Andy Gross
In-Reply-To: <20140207073952.GA2610@qualcomm.com>
On Fri, Feb 07, 2014 at 01:39:52AM -0600, Andy Gross wrote:
> On Thu, Feb 06, 2014 at 06:57:48PM +0200, Ivan T. Ivanov wrote:
> > From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
> >
> > Qualcomm Universal Peripheral (QUP) core is an AHB slave that
> > provides a common data path (an output FIFO and an input FIFO)
> > for serial peripheral interface (SPI) mini-core. SPI in master mode
> > support up to 50MHz, up to four chip selects, and a programmable
> > data path from 4 bits to 32 bits; MODE0..3 protocols
> >
> > Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
> > Cc: Alok Chauhan <alokc@codeaurora.org>
> > Cc: Gilad Avidov <gavidov@codeaurora.org>
> > Cc: Kiran Gunda <kgunda@codeaurora.org>
> > Cc: Sagar Dharia <sdharia@codeaurora.org>
> > ---
> > drivers/spi/Kconfig | 14 +
> > drivers/spi/Makefile | 1 +
> > drivers/spi/spi-qup.c | 898 +++++++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 913 insertions(+)
> > create mode 100644 drivers/spi/spi-qup.c
> >
> > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> > index ba9310b..bf8ce6b 100644
> > --- a/drivers/spi/Kconfig
> > +++ b/drivers/spi/Kconfig
> > @@ -381,6 +381,20 @@ config SPI_RSPI
> > help
> > SPI driver for Renesas RSPI blocks.
> >
> > +config SPI_QUP
> > + tristate "Qualcomm SPI Support with QUP interface"
> > + depends on ARCH_MSM
>
> I'd change to ARCH_MSM_DT. This ensures the OF component is there.
I'd rather explicitly include the CONFIG_OF dependency, but I'm not too
opinionated.
config SPI_QUP
tristate "Qualcomm SPI Support with QUP interface"
depends on OF
depends on ARM
depends on ARCH_MSM_DT || COMPILE_TEST
With Kumar's pending the ARCH_MSM_DT -> ARCH_QCOM rename, we'll
introduce a arm-soc/spi tree dependency here that we'll need to keep
track of.
Kumar-
How would you like to handle this? Would it make sense for this to go
through the SPI tree with depending on ARCH_QCOM instead of ARCH_MSM_DT?
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* Re: [PATCH v2 2/2] Documentation: devicetree: Add boost-frequency binding to list boost mode frequency
From: Nishanth Menon @ 2014-02-07 16:43 UTC (permalink / raw)
To: Sudeep Holla
Cc: Thomas Abraham, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
rjw@rjwysocki.net, linux-samsung-soc@vger.kernel.org,
kgene.kim@samsung.com, t.figa@samsung.com, l.majewski@samsung.com,
viresh.kumar@linaro.org, thomas.ab@samsung.com, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala
In-Reply-To: <52F509BD.70903@arm.com>
On Fri, Feb 7, 2014 at 10:28 AM, Sudeep Holla <Sudeep.Holla@arm.com> wrote:
> On 07/02/14 16:15, Sudeep Holla wrote:
>> On 07/02/14 15:19, Thomas Abraham wrote:
>>> From: Thomas Abraham <thomas.ab@samsung.com>
>>>
>>> Add a new optional boost-frequency binding for specifying the frequencies
>>> usable in boost mode.
>>>
>>> Cc: Nishanth Menon <nm@ti.com>
>>> Cc: Lukasz Majewski <l.majewski@samsung.com>
>>> Cc: Rob Herring <robh+dt@kernel.org>
>>> Cc: Pawel Moll <pawel.moll@arm.com>
>>> Cc: Mark Rutland <mark.rutland@arm.com>
>>> Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
>>> Cc: Kumar Gala <galak@codeaurora.org>
>>> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
>>> ---
>>> Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt | 11 +++++++++++
>>> 1 file changed, 11 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>>> new file mode 100644
>>> index 0000000..d925e38
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>>> @@ -0,0 +1,11 @@
>>> +* Device tree binding for CPU boost frequency (aka over-clocking)
>>> +
>>> +Certain CPU's can be operated in optional 'boost' mode (or sometimes referred as
>>> +overclocking) in which the CPU can operate in frequencies beyond the normal
>>> +operating conditions.
>>> +
>>> +Optional Properties:
>>> +- boost-frequency: list of frequencies in KHz to be used only in boost mode.
>>> + This list should be a subset of frequencies listed in "operating-points"
>>> + property. Refer to Documentation/devicetree/bindings/power/opp.txt for
>>> + details about "operating-points" property.
>>>
>>
>> Won't single entry for boost frequency suffice which would be the starting
>> frequency in the boost range. IOW will there be OPP list with frequencies:
>> A > B > C > D, but only B and C are boost frequency. That seems little odd,
>> unless it's some configuration chosen purely on software basis rather than
>> hardware. For me B marks the beginning of over-clocking.
>>
> Ah, I meant A < B < C < D in the above example.
Should'nt we let the SoC dts define that - traditionally, yes, but
consider the following:
A, B, C uses clk_parent X which describes B, C as overclocked.
and say D uses clk_parent Y which is not "over clocked", then you have
the scenario that on the first look seems counter-intutive.
Regards,
Nishanth Menon
^ permalink raw reply
* Re: Fwd: [PATCH 2/2] spi: Add Qualcomm QUP SPI controller support
From: dsneddon @ 2014-02-07 16:34 UTC (permalink / raw)
To: iivanov, broonie, grant.likely, robh+dt
Cc: linux-spi, linux-arm-msm, inux-kernel, devicetree, alokc, gavidov,
kgunda, sdharia
In-Reply-To: <CACceFXdUobQvN2hcv5kh+QL=o8bWM_PVkAtrOx+euZSeVDm8hQ@mail.gmail.com>
> From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
>
> Qualcomm Universal Peripheral (QUP) core is an AHB slave that
> provides a common data path (an output FIFO and an input FIFO)
> for serial peripheral interface (SPI) mini-core. SPI in master mode
> support up to 50MHz, up to four chip selects, and a programmable
> data path from 4 bits to 32 bits; MODE0..3 protocols
>
> Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
> Cc: Alok Chauhan <alokc@codeaurora.org>
> Cc: Gilad Avidov <gavidov@codeaurora.org>
> Cc: Kiran Gunda <kgunda@codeaurora.org>
> Cc: Sagar Dharia <sdharia@codeaurora.org>
> ---
> drivers/spi/Kconfig | 14 +
> drivers/spi/Makefile | 1 +
> drivers/spi/spi-qup.c | 898
> +++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 913 insertions(+)
> create mode 100644 drivers/spi/spi-qup.c
>
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index ba9310b..bf8ce6b 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -381,6 +381,20 @@ config SPI_RSPI
> help
> SPI driver for Renesas RSPI blocks.
>
> +config SPI_QUP
> + tristate "Qualcomm SPI Support with QUP interface"
> + depends on ARCH_MSM
> + help
> + Qualcomm Universal Peripheral (QUP) core is an AHB slave that
> + provides a common data path (an output FIFO and an input FIFO)
> + for serial peripheral interface (SPI) mini-core. SPI in master
> + mode support up to 50MHz, up to four chip selects, and a
> + programmable data path from 4 bits to 32 bits; supports numerous
> + protocol variants.
> +
> + This driver can also be built as a module. If so, the module
> + will be called spi_qup.
> +
> config SPI_S3C24XX
> tristate "Samsung S3C24XX series SPI"
> depends on ARCH_S3C24XX
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index 95af48d..e598147 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -59,6 +59,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) +=
> spi-pxa2xx-pxadma.o
> spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
> obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
> obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
> +obj-$(CONFIG_SPI_QUP) += spi-qup.o
> obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
> obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
> spi-s3c24xx-hw-y := spi-s3c24xx.o
> diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
> new file mode 100644
> index 0000000..5eb5e8f
> --- /dev/null
> +++ b/drivers/spi/spi-qup.c
> @@ -0,0 +1,898 @@
> +/*
> + * Copyright (c) 2008-2014, The Linux foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License rev 2 and
> + * only rev 2 as published by the free Software foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/spi/spi.h>
> +
> +#define QUP_CONFIG 0x0000
> +#define QUP_STATE 0x0004
> +#define QUP_IO_M_MODES 0x0008
> +#define QUP_SW_RESET 0x000c
> +#define QUP_OPERATIONAL 0x0018
> +#define QUP_ERROR_FLAGS 0x001c
> +#define QUP_ERROR_FLAGS_EN 0x0020
> +#define QUP_OPERATIONAL_MASK 0x0028
> +#define QUP_HW_VERSION 0x0030
> +#define QUP_MX_OUTPUT_CNT 0x0100
> +#define QUP_OUTPUT_FIFO 0x0110
> +#define QUP_MX_WRITE_CNT 0x0150
> +#define QUP_MX_INPUT_CNT 0x0200
> +#define QUP_MX_READ_CNT 0x0208
> +#define QUP_INPUT_FIFO 0x0218
> +
> +#define SPI_CONFIG 0x0300
> +#define SPI_IO_CONTROL 0x0304
> +#define SPI_ERROR_FLAGS 0x0308
> +#define SPI_ERROR_FLAGS_EN 0x030c
> +
> +/* QUP_CONFIG fields */
> +#define QUP_CONFIG_SPI_MODE (1 << 8)
> +#define QUP_CONFIG_NO_INPUT BIT(7)
> +#define QUP_CONFIG_NO_OUTPUT BIT(6)
> +#define QUP_CONFIG_N 0x001f
> +
> +/* QUP_STATE fields */
> +#define QUP_STATE_VALID BIT(2)
> +#define QUP_STATE_RESET 0
> +#define QUP_STATE_RUN 1
> +#define QUP_STATE_PAUSE 3
> +#define QUP_STATE_MASK 3
> +#define QUP_STATE_CLEAR 2
> +
> +#define QUP_HW_VERSION_2_1_1 0x20010001
> +
> +/* QUP_IO_M_MODES fields */
> +#define QUP_IO_M_PACK_EN BIT(15)
> +#define QUP_IO_M_UNPACK_EN BIT(14)
> +#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12
> +#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10
> +#define QUP_IO_M_INPUT_MODE_MASK (3 <<
> QUP_IO_M_INPUT_MODE_MASK_SHIFT)
> +#define QUP_IO_M_OUTPUT_MODE_MASK (3 <<
> QUP_IO_M_OUTPUT_MODE_MASK_SHIFT)
> +
> +#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0)
> +#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2)
> +#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5)
> +#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7)
> +
> +#define QUP_IO_M_MODE_FIFO 0
> +#define QUP_IO_M_MODE_BLOCK 1
> +#define QUP_IO_M_MODE_DMOV 2
> +#define QUP_IO_M_MODE_BAM 3
> +
> +/* QUP_OPERATIONAL fields */
> +#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11)
> +#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10)
> +#define QUP_OP_IN_SERVICE_FLAG BIT(9)
> +#define QUP_OP_OUT_SERVICE_FLAG BIT(8)
> +#define QUP_OP_IN_FIFO_FULL BIT(7)
> +#define QUP_OP_OUT_FIFO_FULL BIT(6)
> +#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5)
> +#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4)
> +
> +/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */
> +#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5)
> +#define QUP_ERROR_INPUT_UNDER_RUN BIT(4)
> +#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3)
> +#define QUP_ERROR_INPUT_OVER_RUN BIT(2)
> +
> +/* SPI_CONFIG fields */
> +#define SPI_CONFIG_HS_MODE BIT(10)
> +#define SPI_CONFIG_INPUT_FIRST BIT(9)
> +#define SPI_CONFIG_LOOPBACK BIT(8)
> +
> +/* SPI_IO_CONTROL fields */
> +#define SPI_IO_C_FORCE_CS BIT(11)
> +#define SPI_IO_C_CLK_IDLE_HIGH BIT(10)
> +#define SPI_IO_C_MX_CS_MODE BIT(8)
> +#define SPI_IO_C_CS_N_POLARITY_0 BIT(4)
> +#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2)
> +#define SPI_IO_C_CS_SELECT_MASK 0x000c
> +#define SPI_IO_C_TRISTATE_CS BIT(1)
> +#define SPI_IO_C_NO_TRI_STATE BIT(0)
> +
> +/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */
> +#define SPI_ERROR_CLK_OVER_RUN BIT(1)
> +#define SPI_ERROR_CLK_UNDER_RUN BIT(0)
> +
> +#define SPI_NUM_CHIPSELECTS 4
> +
> +/* high speed mode is when bus rate is greater then 26MHz */
> +#define SPI_HS_MIN_RATE 26000000
> +
> +#define SPI_DELAY_THRESHOLD 1
> +#define SPI_DELAY_RETRY 10
> +
> +struct spi_qup_device {
> + int bits_per_word;
> + int chip_select;
> + int speed_hz;
> + u16 mode;
> +};
> +
> +struct spi_qup {
> + void __iomem *base;
> + struct device *dev;
> + struct clk *cclk; /* core clock */
> + struct clk *iclk; /* interface clock */
> + int irq;
> + u32 max_speed_hz;
> + u32 speed_hz;
> +
> + int in_fifo_sz;
> + int out_fifo_sz;
> + int in_blk_sz;
> + int out_blk_sz;
> +
> + struct spi_transfer *xfer;
> + struct completion done;
> + int error;
> + int bytes_per_word;
> + int tx_bytes;
> + int rx_bytes;
> +};
> +
> +
> +static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
> +{
> + u32 opstate = readl_relaxed(controller->base + QUP_STATE);
> +
> + return opstate & QUP_STATE_VALID;
> +}
> +
> +static int spi_qup_set_state(struct spi_qup *controller, u32 state)
> +{
> + unsigned long loop = 0;
> + u32 cur_state;
> +
> + cur_state = readl_relaxed(controller->base + QUP_STATE);
Make sure the state is valid before you read the current state.
> + /*
> + * Per spec: for PAUSE_STATE to RESET_STATE, two writes
> + * of (b10) are required
> + */
> + if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) &&
> + (state == QUP_STATE_RESET)) {
> + writel_relaxed(QUP_STATE_CLEAR, controller->base +
> QUP_STATE);
> + writel_relaxed(QUP_STATE_CLEAR, controller->base +
> QUP_STATE);
> + } else {
Make sure you don't transition from RESET to PAUSE.
> + cur_state &= ~QUP_STATE_MASK;
> + cur_state |= state;
> + writel_relaxed(cur_state, controller->base + QUP_STATE);
> + }
> +
> + while (!spi_qup_is_valid_state(controller)) {
> +
> + usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD *
> 2);
> +
> + if (++loop > SPI_DELAY_RETRY)
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static void spi_qup_deassert_cs(struct spi_qup *controller,
> + struct spi_qup_device *chip)
> +{
> + u32 iocontol, mask;
> +
> + iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL);
> +
> + /* Disable auto CS toggle and use manual */
> + iocontol &= ~SPI_IO_C_MX_CS_MODE;
> + iocontol |= SPI_IO_C_FORCE_CS;
> +
> + iocontol &= ~SPI_IO_C_CS_SELECT_MASK;
> + iocontol |= SPI_IO_C_CS_SELECT(chip->chip_select);
> +
> + mask = SPI_IO_C_CS_N_POLARITY_0 << chip->chip_select;
> +
> + if (chip->mode & SPI_CS_HIGH)
> + iocontol &= ~mask;
> + else
> + iocontol |= mask;
> +
> + writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL);
> +}
> +
> +static void spi_qup_assert_cs(struct spi_qup *controller,
> + struct spi_qup_device *chip)
> +{
> + u32 iocontol, mask;
> +
> + iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL);
> +
> + /* Disable auto CS toggle and use manual */
> + iocontol &= ~SPI_IO_C_MX_CS_MODE;
> + iocontol |= SPI_IO_C_FORCE_CS;
> +
> + iocontol &= ~SPI_IO_C_CS_SELECT_MASK;
> + iocontol |= SPI_IO_C_CS_SELECT(chip->chip_select);
> +
> + mask = SPI_IO_C_CS_N_POLARITY_0 << chip->chip_select;
> +
> + if (chip->mode & SPI_CS_HIGH)
> + iocontol |= mask;
> + else
> + iocontol &= ~mask;
> +
> + writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL);
> +}
> +
> +static void spi_qup_fifo_read(struct spi_qup *controller,
> + struct spi_transfer *xfer)
> +{
> + u8 *rx_buf = xfer->rx_buf;
> + u32 word, state;
> + int idx, shift;
> +
> + while (controller->rx_bytes < xfer->len) {
> +
> + state = readl_relaxed(controller->base + QUP_OPERATIONAL);
> + if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
> + break;
> +
> + word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
> +
> + for (idx = 0; idx < controller->bytes_per_word &&
> + controller->rx_bytes < xfer->len; idx++,
> + controller->rx_bytes++) {
> +
> + if (!rx_buf)
> + continue;
If there is no rx_buf just set rx_bytes to xfer->len and skip the loop
entirely.
> + /*
> + * The data format depends on bytes_per_word:
> + * 4 bytes: 0x12345678
> + * 2 bytes: 0x00001234
> + * 1 byte : 0x00000012
> + */
> + shift = BITS_PER_BYTE;
> + shift *= (controller->bytes_per_word - idx - 1);
> + rx_buf[controller->rx_bytes] = word >> shift;
> + }
> + }
> +}
> +
> +static void spi_qup_fifo_write(struct spi_qup *controller,
> + struct spi_transfer *xfer)
> +{
> + const u8 *tx_buf = xfer->tx_buf;
> + u32 word, state, data;
> + int idx;
> +
> + while (controller->tx_bytes < xfer->len) {
> +
> + state = readl_relaxed(controller->base + QUP_OPERATIONAL);
> + if (state & QUP_OP_OUT_FIFO_FULL)
> + break;
> +
> + word = 0;
> + for (idx = 0; idx < controller->bytes_per_word &&
> + controller->tx_bytes < xfer->len; idx++,
> + controller->tx_bytes++) {
> +
> + if (!tx_buf)
> + continue;
Just set tx_bytes to xfer->len and return prior to entering loop.
> +
> + data = tx_buf[controller->tx_bytes];
> + word |= data << (BITS_PER_BYTE * (3 - idx));
> + }
> +
> + writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
> + }
> +}
> +
> +static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +{
> + struct spi_qup *controller = dev_id;
> + struct spi_transfer *xfer;
> + u32 opflags, qup_err, spi_err;
> +
> + xfer = controller->xfer;
> +
> + qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
> + spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS);
> + opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
> +
> + writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
> + writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
> + writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
> +
> + if (!xfer)
> + return IRQ_HANDLED;
> +
> + if (qup_err) {
> + if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN)
> + dev_warn(controller->dev, "OUTPUT_OVER_RUN\n");
> + if (qup_err & QUP_ERROR_INPUT_UNDER_RUN)
> + dev_warn(controller->dev, "INPUT_UNDER_RUN\n");
> + if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN)
> + dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n");
> + if (qup_err & QUP_ERROR_INPUT_OVER_RUN)
> + dev_warn(controller->dev, "INPUT_OVER_RUN\n");
> +
> + controller->error = -EIO;
> + }
> +
> + if (spi_err) {
> + if (spi_err & SPI_ERROR_CLK_OVER_RUN)
> + dev_warn(controller->dev, "CLK_OVER_RUN\n");
> + if (spi_err & SPI_ERROR_CLK_UNDER_RUN)
> + dev_warn(controller->dev, "CLK_UNDER_RUN\n");
> +
> + controller->error = -EIO;
> + }
> +
> + if (opflags & QUP_OP_IN_SERVICE_FLAG) {
> + writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
> + controller->base + QUP_OPERATIONAL);
Write is not necessary since already cleared above.
> + spi_qup_fifo_read(controller, xfer);
> + }
> +
> + if (opflags & QUP_OP_OUT_SERVICE_FLAG) {
> + writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
> + controller->base + QUP_OPERATIONAL);
Write is not necessary since already cleared above.
> + spi_qup_fifo_write(controller, xfer);
> + }
> +
> + if (controller->rx_bytes == xfer->len ||
> + controller->error)
> + complete(&controller->done);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int spi_qup_transfer_do(struct spi_qup *controller,
> + struct spi_qup_device *chip,
> + struct spi_transfer *xfer)
> +{
> + unsigned long timeout;
> + int ret = -EIO;
> +
> + reinit_completion(&controller->done);
> +
> + timeout = DIV_ROUND_UP(controller->speed_hz, MSEC_PER_SEC);
> + timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
> + timeout = 100 * msecs_to_jiffies(timeout);
> +
> + controller->rx_bytes = 0;
> + controller->tx_bytes = 0;
> + controller->error = 0;
> + controller->xfer = xfer;
> +
> + if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
> + dev_warn(controller->dev, "cannot set RUN state\n");
> + goto exit;
> + }
> +
> + if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
> + dev_warn(controller->dev, "cannot set PAUSE state\n");
> + goto exit;
> + }
> +
> + spi_qup_fifo_write(controller, xfer);
> +
> + if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
> + dev_warn(controller->dev, "cannot set EXECUTE state\n");
> + goto exit;
> + }
> +
> + if (!wait_for_completion_timeout(&controller->done, timeout))
> + ret = -ETIMEDOUT;
> + else
> + ret = controller->error;
> +exit:
> + controller->xfer = NULL;
> + controller->error = 0;
> + controller->rx_bytes = 0;
> + controller->tx_bytes = 0;
> + spi_qup_set_state(controller, QUP_STATE_RESET);
> + return ret;
> +}
> +
> +static int spi_qup_setup(struct spi_device *spi)
> +{
> + struct spi_qup *controller = spi_master_get_devdata(spi->master);
> + struct spi_qup_device *chip = spi_get_ctldata(spi);
> +
> + if (spi->chip_select >= spi->master->num_chipselect) {
> + dev_err(controller->dev, "invalid chip_select %d\n",
> + spi->chip_select);
> + return -EINVAL;
> + }
> +
> + if (spi->max_speed_hz > controller->max_speed_hz) {
> + dev_err(controller->dev, "invalid max_speed_hz %d\n",
> + spi->max_speed_hz);
> + return -EINVAL;
> + }
> +
> + if (!chip) {
> + /* First setup */
> + chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> + if (!chip) {
> + dev_err(controller->dev, "no memory for chip
> data\n");
> + return -ENOMEM;
> + }
> +
> + spi_set_ctldata(spi, chip);
> + }
> +
> + return 0;
> +}
> +
> +static void spi_qup_cleanup(struct spi_device *spi)
> +{
> + struct spi_qup_device *chip = spi_get_ctldata(spi);
> +
> + if (!chip)
> + return;
> +
> + spi_set_ctldata(spi, NULL);
> + kfree(chip);
> +}
> +
> +/* set clock freq, clock ramp, bits per work */
> +static int spi_qup_io_setup(struct spi_device *spi,
> + struct spi_transfer *xfer)
> +{
> + struct spi_qup *controller = spi_master_get_devdata(spi->master);
> + struct spi_qup_device *chip = spi_get_ctldata(spi);
> + u32 iocontol, config, iomode, mode;
> + int ret, n_words;
> +
> + if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
> + dev_err(controller->dev, "too big size for loopback %d >
> %d\n",
> + xfer->len, controller->in_fifo_sz);
> + return -EIO;
> + }
> +
> + chip->mode = spi->mode;
> + chip->speed_hz = spi->max_speed_hz;
> + if (xfer->speed_hz)
> + chip->speed_hz = xfer->speed_hz;
> +
> + if (controller->speed_hz != chip->speed_hz) {
> + ret = clk_set_rate(controller->cclk, chip->speed_hz);
> + if (ret) {
> + dev_err(controller->dev, "fail to set frequency
> %d",
> + chip->speed_hz);
> + return -EIO;
> + }
> + }
> +
> + controller->speed_hz = chip->speed_hz;
> +
> + chip->bits_per_word = spi->bits_per_word;
> + if (xfer->bits_per_word)
> + chip->bits_per_word = xfer->bits_per_word;
> +
> + if (chip->bits_per_word <= 8)
> + controller->bytes_per_word = 1;
> + else if (chip->bits_per_word <= 16)
> + controller->bytes_per_word = 2;
> + else
> + controller->bytes_per_word = 4;
> +
> + if (controller->bytes_per_word > xfer->len ||
> + xfer->len % controller->bytes_per_word != 0){
> + /* No partial transfers */
> + dev_err(controller->dev, "invalid len %d for %d bits\n",
> + xfer->len, chip->bits_per_word);
> + return -EIO;
> + }
> +
> + n_words = xfer->len / controller->bytes_per_word;
> +
> + if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
> + dev_err(controller->dev, "cannot set RESET state\n");
> + return -EIO;
> + }
> +
> + if (n_words <= controller->in_fifo_sz) {
> + mode = QUP_IO_M_MODE_FIFO;
> + writel_relaxed(n_words, controller->base +
> QUP_MX_READ_CNT);
> + writel_relaxed(n_words, controller->base +
> QUP_MX_WRITE_CNT);
> + /* must be zero for FIFO */
> + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
> + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
> + } else {
> + mode = QUP_IO_M_MODE_BLOCK;
> + writel_relaxed(n_words, controller->base +
> QUP_MX_INPUT_CNT);
> + writel_relaxed(n_words, controller->base +
> QUP_MX_OUTPUT_CNT);
> + /* must be zero for BLOCK and BAM */
> + writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
> + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
> + }
> +
> + iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
> + /* Set input and output transfer mode */
> + iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
> + iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
> + iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
> + iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
> +
> + writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
> +
> + config = readl_relaxed(controller->base + SPI_CONFIG);
> +
> + if (chip->mode & SPI_LOOP)
> + config |= SPI_CONFIG_LOOPBACK;
> + else
> + config &= ~SPI_CONFIG_LOOPBACK;
> +
> + if (chip->mode & SPI_CPHA)
> + config &= ~SPI_CONFIG_INPUT_FIRST;
> + else
> + config |= SPI_CONFIG_INPUT_FIRST;
> +
> + /*
> + * HS_MODE improves signal stability for spi-clk high rates
> + * but is invalid in loop back mode.
> + */
> + if ((controller->speed_hz >= SPI_HS_MIN_RATE) &&
> + !(chip->mode & SPI_LOOP))
> + config |= SPI_CONFIG_HS_MODE;
> + else
> + config &= ~SPI_CONFIG_HS_MODE;
> +
> + writel_relaxed(config, controller->base + SPI_CONFIG);
> +
> + config = readl_relaxed(controller->base + QUP_CONFIG);
> + config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT |
> QUP_CONFIG_N);
> + config |= chip->bits_per_word - 1;
> + config |= QUP_CONFIG_SPI_MODE;
> + writel_relaxed(config, controller->base + QUP_CONFIG);
> +
> + iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL);
> +
> + /* Disable auto CS toggle */
> + iocontol &= ~SPI_IO_C_MX_CS_MODE;
> +
> + if (chip->mode & SPI_CPOL)
> + iocontol |= SPI_IO_C_CLK_IDLE_HIGH;
> + else
> + iocontol &= ~SPI_IO_C_CLK_IDLE_HIGH;
> +
> + writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL);
> +
> + /*
> + * TODO: In BAM mode mask INPUT and OUTPUT service flags in
> + * to prevent IRQs on FIFO status change.
> + */
Remove TODO
> + writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
> +
> + return 0;
> +}
> +
> +static int spi_qup_transfer_one(struct spi_master *master,
> + struct spi_message *msg)
> +{
> + struct spi_qup *controller = spi_master_get_devdata(master);
> + struct spi_qup_device *chip = spi_get_ctldata(msg->spi);
> + struct spi_transfer *xfer;
> + struct spi_device *spi;
> + unsigned cs_change;
> + int status;
> +
> + spi = msg->spi;
> + cs_change = 1;
> + status = 0;
> +
> + list_for_each_entry(xfer, &msg->transfers, transfer_list) {
> +
> + status = spi_qup_io_setup(spi, xfer);
> + if (status)
> + break;
> +
> + if (cs_change)
> + spi_qup_assert_cs(controller, chip);
> +
> + cs_change = xfer->cs_change;
> +
> + /* Do actual transfer */
> + status = spi_qup_transfer_do(controller, chip, xfer);
> + if (status)
> + break;
> +
> + msg->actual_length += xfer->len;
> +
> + if (xfer->delay_usecs)
> + udelay(xfer->delay_usecs);
> +
> + if (cs_change)
> + spi_qup_deassert_cs(controller, chip);
> + }
> +
> + if (status || !cs_change)
> + spi_qup_deassert_cs(controller, chip);
> +
> + msg->status = status;
> + spi_finalize_current_message(master);
> + return status;
> +}
> +
> +static int spi_qup_probe(struct platform_device *pdev)
> +{
> + struct spi_master *master;
> + struct clk *iclk, *cclk;
> + struct spi_qup *controller;
> + struct resource *res;
> + struct device *dev;
> + void __iomem *base;
> + u32 data, max_freq, iomode;
> + int ret, irq, size;
> +
> + dev = &pdev->dev;
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + irq = platform_get_irq(pdev, 0);
> +
> + if (irq < 0)
> + return irq;
> +
> + cclk = devm_clk_get(dev, "core");
> + if (IS_ERR(cclk)) {
> + dev_err(dev, "cannot get core clock\n");
> + return PTR_ERR(cclk);
> + }
> +
> + iclk = devm_clk_get(dev, "iface");
> + if (IS_ERR(iclk)) {
> + dev_err(dev, "cannot get iface clock\n");
> + return PTR_ERR(iclk);
> + }
> +
> + if (of_property_read_u32(dev->of_node, "spi-max-frequency",
> &max_freq))
> + max_freq = 19200000;
> +
> + if (!max_freq) {
> + dev_err(dev, "invalid clock frequency %d\n", max_freq);
> + return -ENXIO;
> + }
> +
> + ret = clk_set_rate(cclk, max_freq);
> + if (ret)
> + dev_warn(dev, "fail to set SPI frequency %d\n", max_freq);
> +
> + ret = clk_prepare_enable(cclk);
> + if (ret) {
> + dev_err(dev, "cannot enable core clock\n");
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(iclk);
> + if (ret) {
> + clk_disable_unprepare(cclk);
> + dev_err(dev, "cannot enable iface clock\n");
> + return ret;
> + }
> +
> + data = readl_relaxed(base + QUP_HW_VERSION);
> +
> + if (data < QUP_HW_VERSION_2_1_1) {
> + clk_disable_unprepare(cclk);
> + clk_disable_unprepare(iclk);
> + dev_err(dev, "v.%08x is not supported\n", data);
> + return -ENXIO;
> + }
> +
> + master = spi_alloc_master(dev, sizeof(struct spi_qup));
> + if (!master) {
> + clk_disable_unprepare(cclk);
> + clk_disable_unprepare(iclk);
> + dev_err(dev, "cannot allocate master\n");
> + return -ENOMEM;
> + }
> +
> + master->bus_num = pdev->id;
> + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
> + master->num_chipselect = SPI_NUM_CHIPSELECTS;
> + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
> + master->setup = spi_qup_setup;
> + master->cleanup = spi_qup_cleanup;
> + master->transfer_one_message = spi_qup_transfer_one;
> + master->dev.of_node = pdev->dev.of_node;
> + master->auto_runtime_pm = true;
> +
> + platform_set_drvdata(pdev, master);
> +
> + controller = spi_master_get_devdata(master);
> +
> + controller->dev = dev;
> + controller->base = base;
> + controller->iclk = iclk;
> + controller->cclk = cclk;
> + controller->irq = irq;
> + controller->max_speed_hz = clk_get_rate(cclk);
> + controller->speed_hz = controller->max_speed_hz;
> +
> + init_completion(&controller->done);
> +
> + iomode = readl_relaxed(base + QUP_IO_M_MODES);
> +
> + size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode);
> + if (size)
> + controller->out_blk_sz = size * 16;
> + else
> + controller->out_blk_sz = 4;
> +
> + size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode);
> + if (size)
> + controller->in_blk_sz = size * 16;
> + else
> + controller->in_blk_sz = 4;
> +
> + size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode);
> + controller->out_fifo_sz = controller->out_blk_sz * (2 << size);
> +
> + size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
> + controller->in_fifo_sz = controller->in_blk_sz * (2 << size);
> +
> + dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d,
> fifo:%d\n",
> + data, controller->in_blk_sz, controller->in_fifo_sz,
> + controller->out_blk_sz, controller->out_fifo_sz);
> +
> + writel_relaxed(1, base + QUP_SW_RESET);
> +
> + ret = spi_qup_set_state(controller, QUP_STATE_RESET);
> + if (ret) {
> + dev_err(dev, "cannot set RESET state\n");
> + goto error;
> + }
> +
> + writel_relaxed(0, base + QUP_OPERATIONAL);
> + writel_relaxed(0, base + QUP_IO_M_MODES);
> + writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
> + writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
> + base + SPI_ERROR_FLAGS_EN);
> +
> + writel_relaxed(0, base + SPI_CONFIG);
> + writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
> +
> + ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
> + IRQF_TRIGGER_HIGH, pdev->name, controller);
> + if (ret) {
> + dev_err(dev, "cannot request IRQ %d\n", irq);
> + goto error;
> + }
> +
> + ret = devm_spi_register_master(dev, master);
> + if (!ret) {
> + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
> + pm_runtime_use_autosuspend(dev);
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
> + return ret;
> + }
> +error:
> + clk_disable_unprepare(cclk);
> + clk_disable_unprepare(iclk);
> + spi_master_put(master);
> + return ret;
> +}
> +
> +#ifdef CONFIG_PM_RUNTIME
> +static int spi_qup_pm_suspend_runtime(struct device *device)
> +{
> + struct spi_master *master = dev_get_drvdata(device);
> + struct spi_qup *controller = spi_master_get_devdata(master);
> +
> + disable_irq(controller->irq);
> + clk_disable_unprepare(controller->cclk);
> + clk_disable_unprepare(controller->iclk);
> + dev_dbg(device, "suspend runtime\n");
> + return 0;
> +}
> +
> +static int spi_qup_pm_resume_runtime(struct device *device)
> +{
> + struct spi_master *master = dev_get_drvdata(device);
> + struct spi_qup *controller = spi_master_get_devdata(master);
> +
> + clk_prepare_enable(controller->cclk);
> + clk_prepare_enable(controller->iclk);
> + enable_irq(controller->irq);
> + dev_dbg(device, "resume runtime\n");
> + return 0;
> +}
> +#endif /* CONFIG_PM_RUNTIME */
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int spi_qup_suspend(struct device *device)
> +{
> + struct spi_master *master = dev_get_drvdata(device);
> + struct spi_qup *controller = spi_master_get_devdata(master);
> + int status;
> +
> + status = spi_master_suspend(master);
> + if (!status) {
> + disable_irq(controller->irq);
> + clk_disable_unprepare(controller->cclk);
> + clk_disable_unprepare(controller->iclk);
> + }
> +
> + dev_dbg(device, "system suspend %d\n", status);
> + return status;
> +}
> +
> +static int spi_qup_resume(struct device *device)
> +{
> + struct spi_master *master = dev_get_drvdata(device);
> + struct spi_qup *controller = spi_master_get_devdata(master);
> + int status;
> +
> + clk_prepare_enable(controller->cclk);
> + clk_prepare_enable(controller->iclk);
> +
> + status = spi_master_resume(master);
> +
> + dev_dbg(device, "system resume %d\n", status);
> + return status;
> +}
> +#endif /* CONFIG_PM_SLEEP */
> +
> +static int spi_qup_remove(struct platform_device *pdev)
> +{
> + struct spi_master *master = dev_get_drvdata(&pdev->dev);
> + struct spi_qup *controller = spi_master_get_devdata(master);
> +
> + pm_runtime_get_sync(&pdev->dev);
> +
> + clk_disable_unprepare(controller->cclk);
> + clk_disable_unprepare(controller->iclk);
> +
> + pm_runtime_put_noidle(&pdev->dev);
> + pm_runtime_disable(&pdev->dev);
> + return 0;
> +}
> +
> +static struct of_device_id spi_qup_dt_match[] = {
> + { .compatible = "qcom,spi-qup-v2", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, spi_qup_dt_match);
> +
> +static const struct dev_pm_ops spi_qup_dev_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume)
> + SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime,
> + spi_qup_pm_resume_runtime,
> + NULL)
> +};
> +
> +static struct platform_driver spi_qup_driver = {
> + .driver = {
> + .name = "spi_qup",
> + .owner = THIS_MODULE,
> + .pm = &spi_qup_dev_pm_ops,
> + .of_match_table = spi_qup_dt_match,
> + },
> + .probe = spi_qup_probe,
> + .remove = spi_qup_remove,
> +};
> +module_platform_driver(spi_qup_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_VERSION("0.4");
> +MODULE_ALIAS("platform:spi_qup");
^ permalink raw reply
* [PATCH v2] ARM: sunxi: Add driver for sunxi usb phy
From: Hans de Goede @ 2014-02-07 16:33 UTC (permalink / raw)
To: Kishon Vijay Abraham I
Cc: Maxime Ripard, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree, linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede
The Allwinner A1x / A2x SoCs have 2 or 3 usb phys which are all accessed
through a single set of registers. Besides this there are also some other
phy related bits which need poking, which are per phy, but shared between the
ohci and ehci controllers, so these are also controlled from this new phy
driver.
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
.../devicetree/bindings/phy/sun4i-usb-phy.txt | 28 ++
drivers/phy/Kconfig | 11 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-sun4i-usb.c | 326 +++++++++++++++++++++
4 files changed, 366 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
create mode 100644 drivers/phy/phy-sun4i-usb.c
diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
new file mode 100644
index 0000000..f7eccb2
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
@@ -0,0 +1,28 @@
+Allwinner sun4i USB PHY
+-----------------------
+
+Required properties:
+- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
+ "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
+- reg : a list of offset + length pairs, the 1st list entry should point to
+ the phy base regs, the 2nd entry to the pmu reg for phy1, and the 3th
+ entry to the pmu reg of phy2 (for devices which have a phy2).
+- #phy-cells : from the generic phy bindings, must be 1
+
+Optional properties:
+- clocks : phandle + clock specifier for the phy clock
+- clock-names : "usb_phy"
+- resets : a list of phandle + reset specifier pairs
+- reset-names : "usb0_reset", "usb1_reset", and / or "usb2_reset"
+
+Example:
+ usbphy: phy@0x01c13400 {
+ #phy-cells = <1>;
+ compatible = "allwinner,sun4i-a10-usb-phy";
+ /* phy base regs, phy1 pmu reg, phy2 pmu reg */
+ reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
+ clocks = <&usb_clk 8>;
+ clock-names = "usb_phy";
+ resets = <&usb_clk 1>, <&usb_clk 2>;
+ reset-names = "usb1_reset", "usb2_reset";
+ };
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index afa2354..6070c99 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -64,4 +64,15 @@ config BCM_KONA_USB2_PHY
help
Enable this to support the Broadcom Kona USB 2.0 PHY.
+config PHY_SUN4I_USB
+ tristate "Allwinner sunxi SoC USB PHY driver"
+ depends on ARCH_SUNXI
+ select GENERIC_PHY
+ help
+ Enable this to support the transceiver that is part of Allwinner
+ sunxi SoCs.
+
+ This driver controls the entire USB PHY block, both the USB OTG
+ parts, as well as the 2 regular USB 2 host PHYs.
+
endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index b57c253..9d4f8bb 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
+obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
new file mode 100644
index 0000000..bd9cb7fa
--- /dev/null
+++ b/drivers/phy/phy-sun4i-usb.c
@@ -0,0 +1,326 @@
+/*
+ * Allwinner sun4i USB phy driver
+ *
+ * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@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 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/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#define REG_ISCR 0x00
+#define REG_PHYCTL 0x04
+#define REG_PHYBIST 0x08
+#define REG_PHYTUNE 0x0c
+
+#define SUNXI_AHB_ICHR8_EN BIT(10)
+#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
+#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
+#define SUNXI_ULPI_BYPASS_EN BIT(0)
+
+/* Common Control Bits for Both PHYs */
+#define PHY_PLL_BW 0x03
+#define PHY_RES45_CAL_EN 0x0c
+
+/* Private Control Bits for Each PHY */
+#define PHY_TX_AMPLITUDE_TUNE 0x20
+#define PHY_TX_SLEWRATE_TUNE 0x22
+#define PHY_VBUSVALID_TH_SEL 0x25
+#define PHY_PULLUP_RES_SEL 0x27
+#define PHY_OTG_FUNC_EN 0x28
+#define PHY_VBUS_DET_EN 0x29
+#define PHY_DISCON_TH_SEL 0x2a
+
+#define MAX_PHYS 3
+
+struct sun4i_usb_phy_data {
+ struct clk *clk;
+ void __iomem *base;
+ struct mutex mutex;
+ int num_phys;
+ u32 disc_thresh;
+ struct sun4i_usb_phy {
+ struct phy *phy;
+ void __iomem *pmu;
+ struct regulator *vbus;
+ struct reset_control *reset;
+ int index;
+ } phys[MAX_PHYS];
+};
+
+#define to_sun4i_usb_phy_data(phy) \
+ container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
+
+static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
+ int len)
+{
+ struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
+ u32 temp, usbc_bit = BIT(phy->index * 2);
+ int i;
+
+ mutex_lock(&phy_data->mutex);
+
+ for (i = 0; i < len; i++) {
+ temp = readl(phy_data->base + REG_PHYCTL);
+
+ /* clear the address portion */
+ temp &= ~(0xff << 8);
+
+ /* set the address */
+ temp |= ((addr + i) << 8);
+ writel(temp, phy_data->base + REG_PHYCTL);
+
+ /* set the data bit and clear usbc bit*/
+ temp = readb(phy_data->base + REG_PHYCTL);
+ if (data & 0x1)
+ temp |= BIT(7);
+ else
+ temp &= ~BIT(7);
+ temp &= ~usbc_bit;
+ writeb(temp, phy_data->base + REG_PHYCTL);
+
+ /* pulse usbc_bit */
+ temp = readb(phy_data->base + REG_PHYCTL);
+ temp |= usbc_bit;
+ writeb(temp, phy_data->base + REG_PHYCTL);
+
+ temp = readb(phy_data->base + REG_PHYCTL);
+ temp &= ~usbc_bit;
+ writeb(temp, phy_data->base + REG_PHYCTL);
+
+ data >>= 1;
+ }
+ mutex_unlock(&phy_data->mutex);
+}
+
+static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
+{
+ u32 bits, reg_value;
+
+ if (!phy->pmu)
+ return;
+
+ bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
+ SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
+
+ reg_value = readl(phy->pmu);
+
+ if (enable)
+ reg_value |= bits;
+ else
+ reg_value &= ~bits;
+
+ writel(reg_value, phy->pmu);
+}
+
+static int sun4i_usb_phy_init(struct phy *_phy)
+{
+ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+ int ret;
+
+ ret = clk_prepare_enable(data->clk);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(phy->reset);
+ if (ret) {
+ clk_disable_unprepare(data->clk);
+ return ret;
+ }
+
+ /* Adjust PHY's magnitude and rate */
+ sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
+
+ /* Disconnect threshold adjustment */
+ sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
+
+ sun4i_usb_phy_passby(phy, 1);
+
+ return 0;
+}
+
+static int sun4i_usb_phy_exit(struct phy *_phy)
+{
+ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+ sun4i_usb_phy_passby(phy, 0);
+ reset_control_assert(phy->reset);
+ clk_disable_unprepare(data->clk);
+
+ return 0;
+}
+
+static int sun4i_usb_phy_power_on(struct phy *_phy)
+{
+ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+ int ret = 0;
+
+ if (phy->vbus)
+ ret = regulator_enable(phy->vbus);
+
+ return ret;
+}
+
+static int sun4i_usb_phy_power_off(struct phy *_phy)
+{
+ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+
+ if (phy->vbus)
+ regulator_disable(phy->vbus);
+
+ return 0;
+}
+
+static struct phy_ops sun4i_usb_phy_ops = {
+ .init = sun4i_usb_phy_init,
+ .exit = sun4i_usb_phy_exit,
+ .power_on = sun4i_usb_phy_power_on,
+ .power_off = sun4i_usb_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *sun4i_usb_phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
+
+ if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
+ return ERR_PTR(-ENODEV);
+
+ return data->phys[args->args[0]].phy;
+}
+
+static int sun4i_usb_phy_probe(struct platform_device *pdev)
+{
+ struct sun4i_usb_phy_data *data;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ void __iomem *pmu = NULL;
+ struct phy_provider *phy_provider;
+ struct reset_control *reset;
+ struct regulator *vbus;
+ struct phy *phy;
+ char name[16];
+ int i;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->mutex);
+
+ if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
+ data->num_phys = 2;
+ else
+ data->num_phys = 3;
+
+ if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
+ data->disc_thresh = 3;
+ else
+ data->disc_thresh = 2;
+
+ data->clk = devm_clk_get(dev, "usb_phy");
+ if (IS_ERR(data->clk)) {
+ dev_err(dev, "could not get usb_phy clock\n");
+ return PTR_ERR(data->clk);
+ }
+
+ /* Skip 0, 0 is the phy for otg which is not yet supported. */
+ for (i = 1; i < data->num_phys; i++) {
+ snprintf(name, sizeof(name), "usb%d_vbus", i);
+ vbus = devm_regulator_get_optional(dev, name);
+ if (IS_ERR(vbus)) {
+ if (PTR_ERR(vbus) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ vbus = NULL;
+ }
+
+ snprintf(name, sizeof(name), "usb%d_reset", i);
+ reset = devm_reset_control_get(dev, name);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to get reset %s\n", name);
+ return PTR_ERR(phy);
+ }
+
+ if (i) { /* No pmu for usbc0 */
+ pmu = devm_ioremap_resource(dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, i));
+ if (IS_ERR(pmu))
+ return PTR_ERR(pmu);
+ }
+
+ phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create PHY %d\n", i);
+ return PTR_ERR(phy);
+ }
+
+ data->phys[i].phy = phy;
+ data->phys[i].pmu = pmu;
+ data->phys[i].vbus = vbus;
+ data->phys[i].reset = reset;
+ data->phys[i].index = i;
+ phy_set_drvdata(phy, &data->phys[i]);
+ }
+
+ data->base = devm_ioremap_resource(dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ if (IS_ERR(data->base))
+ return PTR_ERR(data->base);
+
+ dev_set_drvdata(dev, data);
+ phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ return 0;
+}
+
+static const struct of_device_id sun4i_usb_phy_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-usb-phy" },
+ { .compatible = "allwinner,sun5i-a13-usb-phy" },
+ { .compatible = "allwinner,sun7i-a20-usb-phy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
+
+static struct platform_driver sun4i_usb_phy_driver = {
+ .probe = sun4i_usb_phy_probe,
+ .driver = {
+ .of_match_table = sun4i_usb_phy_of_match,
+ .name = "sun4i-usb-phy",
+ .owner = THIS_MODULE,
+ }
+};
+module_platform_driver(sun4i_usb_phy_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
--
1.8.4.2
^ permalink raw reply related
* Re: [PATCH v2 8/8] regulator: da9055: Remove use of regmap_irq_get_virq()
From: Mark Brown @ 2014-02-07 16:31 UTC (permalink / raw)
To: Adam Thomson
Cc: Lee Jones, alsa-devel, linux-kernel, devicetree, Rob Herring,
Linus Walleij, Dmitry Torokhov, Alessandro Zummo, Guenter Roeck
In-Reply-To: <1234f50ae0bcf6e9fe057cbe26f9c55b042d2c2e.1391705989.git.Adam.Thomson.Opensource@diasemi.com>
[-- Attachment #1: Type: text/plain, Size: 148 bytes --]
On Thu, Feb 06, 2014 at 06:03:21PM +0000, Adam Thomson wrote:
> Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
Applied, thanks.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v2 4/8] regulator: da9055: Add DT support
From: Mark Brown @ 2014-02-07 16:31 UTC (permalink / raw)
To: Adam Thomson
Cc: Lee Jones, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Linus Walleij,
Dmitry Torokhov, Alessandro Zummo, Guenter Roeck
In-Reply-To: <9c9235270af15eb50bd2d5c20daa7a81858910a4.1391705989.git.Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 176 bytes --]
On Thu, Feb 06, 2014 at 06:03:13PM +0000, Adam Thomson wrote:
> Signed-off-by: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
Applied, thanks.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v2 2/2] Documentation: devicetree: Add boost-frequency binding to list boost mode frequency
From: Sudeep Holla @ 2014-02-07 16:28 UTC (permalink / raw)
To: Thomas Abraham, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org
Cc: Sudeep Holla, rjw@rjwysocki.net,
linux-samsung-soc@vger.kernel.org, kgene.kim@samsung.com,
t.figa@samsung.com, l.majewski@samsung.com,
viresh.kumar@linaro.org, thomas.ab@samsung.com, Nishanth Menon,
Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala
In-Reply-To: <52F50698.4060305@arm.com>
On 07/02/14 16:15, Sudeep Holla wrote:
> On 07/02/14 15:19, Thomas Abraham wrote:
>> From: Thomas Abraham <thomas.ab@samsung.com>
>>
>> Add a new optional boost-frequency binding for specifying the frequencies
>> usable in boost mode.
>>
>> Cc: Nishanth Menon <nm@ti.com>
>> Cc: Lukasz Majewski <l.majewski@samsung.com>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Pawel Moll <pawel.moll@arm.com>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
>> Cc: Kumar Gala <galak@codeaurora.org>
>> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
>> ---
>> Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt | 11 +++++++++++
>> 1 file changed, 11 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>>
>> diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>> new file mode 100644
>> index 0000000..d925e38
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>> @@ -0,0 +1,11 @@
>> +* Device tree binding for CPU boost frequency (aka over-clocking)
>> +
>> +Certain CPU's can be operated in optional 'boost' mode (or sometimes referred as
>> +overclocking) in which the CPU can operate in frequencies beyond the normal
>> +operating conditions.
>> +
>> +Optional Properties:
>> +- boost-frequency: list of frequencies in KHz to be used only in boost mode.
>> + This list should be a subset of frequencies listed in "operating-points"
>> + property. Refer to Documentation/devicetree/bindings/power/opp.txt for
>> + details about "operating-points" property.
>>
>
> Won't single entry for boost frequency suffice which would be the starting
> frequency in the boost range. IOW will there be OPP list with frequencies:
> A > B > C > D, but only B and C are boost frequency. That seems little odd,
> unless it's some configuration chosen purely on software basis rather than
> hardware. For me B marks the beginning of over-clocking.
>
Ah, I meant A < B < C < D in the above example.
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH v4 1/5] clk: sunxi: Add support for USB clock-register reset bits
From: Philipp Zabel @ 2014-02-07 16:27 UTC (permalink / raw)
To: Hans de Goede
Cc: Emilio López, Mike Turquette, Maxime Ripard, Grant Likely,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree
In-Reply-To: <1391786513-20780-2-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Am Freitag, den 07.02.2014, 16:21 +0100 schrieb Hans de Goede:
> The usb-clk register is special in that it not only contains clk gate bits,
> but also has a few reset bits. This commit adds support for this by allowing
> gates type sunxi clks to also register a reset controller.
>
> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Acked-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> ---
> drivers/clk/sunxi/clk-sunxi.c | 71 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 71 insertions(+)
>
> diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
> index 64bda21..1e15e4c 100644
> --- a/drivers/clk/sunxi/clk-sunxi.c
> +++ b/drivers/clk/sunxi/clk-sunxi.c
> @@ -18,6 +18,7 @@
> #include <linux/clkdev.h>
> #include <linux/of.h>
> #include <linux/of_address.h>
> +#include <linux/reset-controller.h>
>
> #include "clk-factors.h"
>
> @@ -838,6 +839,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
>
>
> /**
> + * sunxi_gates_reset... - reset bits in leaf gate clk registers handling
> + */
> +
> +struct gates_reset_data {
> + void __iomem *reg;
> + spinlock_t *lock;
> + struct reset_controller_dev rcdev;
> +};
> +
> +static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + struct gates_reset_data *data = container_of(rcdev,
> + struct gates_reset_data,
> + rcdev);
> + unsigned long flags;
> + u32 reg;
> +
> + spin_lock_irqsave(data->lock, flags);
> +
> + reg = readl(data->reg);
> + writel(reg & ~BIT(id), data->reg);
> +
> + spin_unlock_irqrestore(data->lock, flags);
> +
> + return 0;
> +}
> +
> +static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + struct gates_reset_data *data = container_of(rcdev,
> + struct gates_reset_data,
> + rcdev);
> + unsigned long flags;
> + u32 reg;
> +
> + spin_lock_irqsave(data->lock, flags);
> +
> + reg = readl(data->reg);
> + writel(reg | BIT(id), data->reg);
> +
> + spin_unlock_irqrestore(data->lock, flags);
> +
> + return 0;
> +}
> +
> +static struct reset_control_ops sunxi_gates_reset_ops = {
> + .assert = sunxi_gates_reset_assert,
> + .deassert = sunxi_gates_reset_deassert,
> +};
> +
> +/**
> * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
> */
>
> @@ -845,6 +899,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
>
> struct gates_data {
> DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
> + u32 reset_mask;
> };
>
> static const struct gates_data sun4i_axi_gates_data __initconst = {
> @@ -915,6 +970,7 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
> struct gates_data *data)
> {
> struct clk_onecell_data *clk_data;
> + struct gates_reset_data *reset_data;
> const char *clk_parent;
> const char *clk_name;
> void *reg;
> @@ -958,6 +1014,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
> clk_data->clk_num = i;
>
> of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
> +
> + /* Register a reset controler for gates with reset bits */
> + if (data->reset_mask == 0)
> + return;
> +
> + reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
> + if (!reset_data)
> + return;
> +
> + reset_data->reg = reg;
> + reset_data->lock = &clk_lock;
> + reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1;
> + reset_data->rcdev.ops = &sunxi_gates_reset_ops;
> + reset_data->rcdev.of_node = node;
> + reset_controller_register(&reset_data->rcdev);
> }
>
>
^ permalink raw reply
* Re: [PATCH v2 2/8] ASoC: da9055: Add DT support for CODEC
From: Mark Brown @ 2014-02-07 16:26 UTC (permalink / raw)
To: Adam Thomson
Cc: Lee Jones, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Linus Walleij,
Dmitry Torokhov, Alessandro Zummo, Guenter Roeck
In-Reply-To: <20140207162504.GP32298-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 564 bytes --]
On Fri, Feb 07, 2014 at 04:25:04PM +0000, Mark Brown wrote:
> On Thu, Feb 06, 2014 at 06:03:09PM +0000, Adam Thomson wrote:
> > Signed-off-by: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
>
> This doesn't apply against my current for-next or v3.14-rc1, can you
> please check and resend?
Ugh, sorry - it'll be due to patch 1 of course which I'd skipped due to
the MFD bit. If you need to resend it's probably easier all round to
just send the MFD and ASoC bits of that separately since there's no
direct dependency.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v2 2/8] ASoC: da9055: Add DT support for CODEC
From: Mark Brown @ 2014-02-07 16:25 UTC (permalink / raw)
To: Adam Thomson
Cc: Lee Jones, alsa-devel, linux-kernel, devicetree, Rob Herring,
Linus Walleij, Dmitry Torokhov, Alessandro Zummo, Guenter Roeck
In-Reply-To: <9ec36c0633ae8291267be6802a34a469a34ae292.1391705989.git.Adam.Thomson.Opensource@diasemi.com>
[-- Attachment #1: Type: text/plain, Size: 225 bytes --]
On Thu, Feb 06, 2014 at 06:03:09PM +0000, Adam Thomson wrote:
> Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
This doesn't apply against my current for-next or v3.14-rc1, can you
please check and resend?
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* [PATCH v3 07/15] media: rc: img-ir: add base driver
From: James Hogan @ 2014-02-07 16:16 UTC (permalink / raw)
To: Mauro Carvalho Chehab, linux-media-u79uwXL29TY76Z2rM5mHXA
Cc: James Hogan, Grant Likely, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1389967140-20704-8-git-send-email-james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
Add base driver for the ImgTec Infrared decoder block. The driver is
split into separate components for raw (software) decode and hardware
decoder which are in following commits.
Signed-off-by: James Hogan <james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
Cc: Mauro Carvalho Chehab <m.chehab-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Grant Likely <grant.likely-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
v3:
- Use new compatible string "img,ir-rev1"
v2:
- Use new DT binding, with a different compatibility string and get core
clock by name.
- Remove next pointer from struct img_ir_priv. This is related to the
removal of dynamic registration/unregistration of protocol decode
timings from later patches.
- Add io.h include to img-ir.h.
---
drivers/media/rc/img-ir/img-ir-core.c | 176 ++++++++++++++++++++++++++++++++++
drivers/media/rc/img-ir/img-ir.h | 166 ++++++++++++++++++++++++++++++++
2 files changed, 342 insertions(+)
create mode 100644 drivers/media/rc/img-ir/img-ir-core.c
create mode 100644 drivers/media/rc/img-ir/img-ir.h
diff --git a/drivers/media/rc/img-ir/img-ir-core.c b/drivers/media/rc/img-ir/img-ir-core.c
new file mode 100644
index 000000000000..6b7834834fb8
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-core.c
@@ -0,0 +1,176 @@
+/*
+ * ImgTec IR Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * This contains core img-ir code for setting up the driver. The two interfaces
+ * (raw and hardware decode) are handled separately.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include "img-ir.h"
+
+static irqreturn_t img_ir_isr(int irq, void *dev_id)
+{
+ struct img_ir_priv *priv = dev_id;
+ u32 irq_status;
+
+ spin_lock(&priv->lock);
+ /* we have to clear irqs before reading */
+ irq_status = img_ir_read(priv, IMG_IR_IRQ_STATUS);
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_status);
+
+ /* don't handle valid data irqs if we're only interested in matches */
+ irq_status &= img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+
+ /* hand off edge interrupts to raw decode handler */
+ if (irq_status & IMG_IR_IRQ_EDGE && img_ir_raw_enabled(&priv->raw))
+ img_ir_isr_raw(priv, irq_status);
+
+ /* hand off hardware match interrupts to hardware decode handler */
+ if (irq_status & (IMG_IR_IRQ_DATA_MATCH |
+ IMG_IR_IRQ_DATA_VALID |
+ IMG_IR_IRQ_DATA2_VALID) &&
+ img_ir_hw_enabled(&priv->hw))
+ img_ir_isr_hw(priv, irq_status);
+
+ spin_unlock(&priv->lock);
+ return IRQ_HANDLED;
+}
+
+static void img_ir_setup(struct img_ir_priv *priv)
+{
+ /* start off with interrupts disabled */
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, 0);
+
+ img_ir_setup_raw(priv);
+ img_ir_setup_hw(priv);
+
+ if (!IS_ERR(priv->clk))
+ clk_prepare_enable(priv->clk);
+}
+
+static void img_ir_ident(struct img_ir_priv *priv)
+{
+ u32 core_rev = img_ir_read(priv, IMG_IR_CORE_REV);
+
+ dev_info(priv->dev,
+ "IMG IR Decoder (%d.%d.%d.%d) probed successfully\n",
+ (core_rev & IMG_IR_DESIGNER) >> IMG_IR_DESIGNER_SHIFT,
+ (core_rev & IMG_IR_MAJOR_REV) >> IMG_IR_MAJOR_REV_SHIFT,
+ (core_rev & IMG_IR_MINOR_REV) >> IMG_IR_MINOR_REV_SHIFT,
+ (core_rev & IMG_IR_MAINT_REV) >> IMG_IR_MAINT_REV_SHIFT);
+ dev_info(priv->dev, "Modes:%s%s\n",
+ img_ir_hw_enabled(&priv->hw) ? " hardware" : "",
+ img_ir_raw_enabled(&priv->raw) ? " raw" : "");
+}
+
+static int img_ir_probe(struct platform_device *pdev)
+{
+ struct img_ir_priv *priv;
+ struct resource *res_regs;
+ int irq, error, error2;
+
+ /* Get resources from platform device */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "cannot find IRQ resource\n");
+ return irq;
+ }
+
+ /* Private driver data */
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "cannot allocate device data\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, priv);
+ priv->dev = &pdev->dev;
+ spin_lock_init(&priv->lock);
+
+ /* Ioremap the registers */
+ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->reg_base = devm_ioremap_resource(&pdev->dev, res_regs);
+ if (IS_ERR(priv->reg_base))
+ return PTR_ERR(priv->reg_base);
+
+ /* Get core clock */
+ priv->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(priv->clk))
+ dev_warn(&pdev->dev, "cannot get core clock resource\n");
+ /*
+ * The driver doesn't need to know about the system ("sys") or power
+ * modulation ("mod") clocks yet
+ */
+
+ /* Set up raw & hw decoder */
+ error = img_ir_probe_raw(priv);
+ error2 = img_ir_probe_hw(priv);
+ if (error && error2)
+ return (error == -ENODEV) ? error2 : error;
+
+ /* Get the IRQ */
+ priv->irq = irq;
+ error = request_irq(priv->irq, img_ir_isr, 0, "img-ir", priv);
+ if (error) {
+ dev_err(&pdev->dev, "cannot register IRQ %u\n",
+ priv->irq);
+ error = -EIO;
+ goto err_irq;
+ }
+
+ img_ir_ident(priv);
+ img_ir_setup(priv);
+
+ return 0;
+
+err_irq:
+ img_ir_remove_hw(priv);
+ img_ir_remove_raw(priv);
+ return error;
+}
+
+static int img_ir_remove(struct platform_device *pdev)
+{
+ struct img_ir_priv *priv = platform_get_drvdata(pdev);
+
+ free_irq(priv->irq, img_ir_isr);
+ img_ir_remove_hw(priv);
+ img_ir_remove_raw(priv);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(img_ir_pmops, img_ir_suspend, img_ir_resume);
+
+static const struct of_device_id img_ir_match[] = {
+ { .compatible = "img,ir-rev1" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, img_ir_match);
+
+static struct platform_driver img_ir_driver = {
+ .driver = {
+ .name = "img-ir",
+ .owner = THIS_MODULE,
+ .of_match_table = img_ir_match,
+ .pm = &img_ir_pmops,
+ },
+ .probe = img_ir_probe,
+ .remove = img_ir_remove,
+};
+
+module_platform_driver(img_ir_driver);
+
+MODULE_AUTHOR("Imagination Technologies Ltd.");
+MODULE_DESCRIPTION("ImgTec IR");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/img-ir/img-ir.h b/drivers/media/rc/img-ir/img-ir.h
new file mode 100644
index 000000000000..afb189394af9
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir.h
@@ -0,0 +1,166 @@
+/*
+ * ImgTec IR Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ */
+
+#ifndef _IMG_IR_H_
+#define _IMG_IR_H_
+
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+#include "img-ir-raw.h"
+#include "img-ir-hw.h"
+
+/* registers */
+
+/* relative to the start of the IR block of registers */
+#define IMG_IR_CONTROL 0x00
+#define IMG_IR_STATUS 0x04
+#define IMG_IR_DATA_LW 0x08
+#define IMG_IR_DATA_UP 0x0c
+#define IMG_IR_LEAD_SYMB_TIMING 0x10
+#define IMG_IR_S00_SYMB_TIMING 0x14
+#define IMG_IR_S01_SYMB_TIMING 0x18
+#define IMG_IR_S10_SYMB_TIMING 0x1c
+#define IMG_IR_S11_SYMB_TIMING 0x20
+#define IMG_IR_FREE_SYMB_TIMING 0x24
+#define IMG_IR_POW_MOD_PARAMS 0x28
+#define IMG_IR_POW_MOD_ENABLE 0x2c
+#define IMG_IR_IRQ_MSG_DATA_LW 0x30
+#define IMG_IR_IRQ_MSG_DATA_UP 0x34
+#define IMG_IR_IRQ_MSG_MASK_LW 0x38
+#define IMG_IR_IRQ_MSG_MASK_UP 0x3c
+#define IMG_IR_IRQ_ENABLE 0x40
+#define IMG_IR_IRQ_STATUS 0x44
+#define IMG_IR_IRQ_CLEAR 0x48
+#define IMG_IR_IRCORE_ID 0xf0
+#define IMG_IR_CORE_REV 0xf4
+#define IMG_IR_CORE_DES1 0xf8
+#define IMG_IR_CORE_DES2 0xfc
+
+
+/* field masks */
+
+/* IMG_IR_CONTROL */
+#define IMG_IR_DECODEN 0x40000000
+#define IMG_IR_CODETYPE 0x30000000
+#define IMG_IR_CODETYPE_SHIFT 28
+#define IMG_IR_HDRTOG 0x08000000
+#define IMG_IR_LDRDEC 0x04000000
+#define IMG_IR_DECODINPOL 0x02000000 /* active high */
+#define IMG_IR_BITORIEN 0x01000000 /* MSB first */
+#define IMG_IR_D1VALIDSEL 0x00008000
+#define IMG_IR_BITINV 0x00000040 /* don't invert */
+#define IMG_IR_DECODEND2 0x00000010
+#define IMG_IR_BITORIEND2 0x00000002 /* MSB first */
+#define IMG_IR_BITINVD2 0x00000001 /* don't invert */
+
+/* IMG_IR_STATUS */
+#define IMG_IR_RXDVALD2 0x00001000
+#define IMG_IR_IRRXD 0x00000400
+#define IMG_IR_TOGSTATE 0x00000200
+#define IMG_IR_RXDVAL 0x00000040
+#define IMG_IR_RXDLEN 0x0000003f
+#define IMG_IR_RXDLEN_SHIFT 0
+
+/* IMG_IR_LEAD_SYMB_TIMING, IMG_IR_Sxx_SYMB_TIMING */
+#define IMG_IR_PD_MAX 0xff000000
+#define IMG_IR_PD_MAX_SHIFT 24
+#define IMG_IR_PD_MIN 0x00ff0000
+#define IMG_IR_PD_MIN_SHIFT 16
+#define IMG_IR_W_MAX 0x0000ff00
+#define IMG_IR_W_MAX_SHIFT 8
+#define IMG_IR_W_MIN 0x000000ff
+#define IMG_IR_W_MIN_SHIFT 0
+
+/* IMG_IR_FREE_SYMB_TIMING */
+#define IMG_IR_MAXLEN 0x0007e000
+#define IMG_IR_MAXLEN_SHIFT 13
+#define IMG_IR_MINLEN 0x00001f00
+#define IMG_IR_MINLEN_SHIFT 8
+#define IMG_IR_FT_MIN 0x000000ff
+#define IMG_IR_FT_MIN_SHIFT 0
+
+/* IMG_IR_POW_MOD_PARAMS */
+#define IMG_IR_PERIOD_LEN 0x3f000000
+#define IMG_IR_PERIOD_LEN_SHIFT 24
+#define IMG_IR_PERIOD_DUTY 0x003f0000
+#define IMG_IR_PERIOD_DUTY_SHIFT 16
+#define IMG_IR_STABLE_STOP 0x00003f00
+#define IMG_IR_STABLE_STOP_SHIFT 8
+#define IMG_IR_STABLE_START 0x0000003f
+#define IMG_IR_STABLE_START_SHIFT 0
+
+/* IMG_IR_POW_MOD_ENABLE */
+#define IMG_IR_POWER_OUT_EN 0x00000002
+#define IMG_IR_POWER_MOD_EN 0x00000001
+
+/* IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_STATUS, IMG_IR_IRQ_CLEAR */
+#define IMG_IR_IRQ_DEC2_ERR 0x00000080
+#define IMG_IR_IRQ_DEC_ERR 0x00000040
+#define IMG_IR_IRQ_ACT_LEVEL 0x00000020
+#define IMG_IR_IRQ_FALL_EDGE 0x00000010
+#define IMG_IR_IRQ_RISE_EDGE 0x00000008
+#define IMG_IR_IRQ_DATA_MATCH 0x00000004
+#define IMG_IR_IRQ_DATA2_VALID 0x00000002
+#define IMG_IR_IRQ_DATA_VALID 0x00000001
+#define IMG_IR_IRQ_ALL 0x000000ff
+#define IMG_IR_IRQ_EDGE (IMG_IR_IRQ_FALL_EDGE | IMG_IR_IRQ_RISE_EDGE)
+
+/* IMG_IR_CORE_ID */
+#define IMG_IR_CORE_ID 0x00ff0000
+#define IMG_IR_CORE_ID_SHIFT 16
+#define IMG_IR_CORE_CONFIG 0x0000ffff
+#define IMG_IR_CORE_CONFIG_SHIFT 0
+
+/* IMG_IR_CORE_REV */
+#define IMG_IR_DESIGNER 0xff000000
+#define IMG_IR_DESIGNER_SHIFT 24
+#define IMG_IR_MAJOR_REV 0x00ff0000
+#define IMG_IR_MAJOR_REV_SHIFT 16
+#define IMG_IR_MINOR_REV 0x0000ff00
+#define IMG_IR_MINOR_REV_SHIFT 8
+#define IMG_IR_MAINT_REV 0x000000ff
+#define IMG_IR_MAINT_REV_SHIFT 0
+
+struct device;
+struct clk;
+
+/**
+ * struct img_ir_priv - Private driver data.
+ * @dev: Platform device.
+ * @irq: IRQ number.
+ * @clk: Input clock.
+ * @reg_base: Iomem base address of IR register block.
+ * @lock: Protects IR registers and variables in this struct.
+ * @raw: Driver data for raw decoder.
+ * @hw: Driver data for hardware decoder.
+ */
+struct img_ir_priv {
+ struct device *dev;
+ int irq;
+ struct clk *clk;
+ void __iomem *reg_base;
+ spinlock_t lock;
+
+ struct img_ir_priv_raw raw;
+ struct img_ir_priv_hw hw;
+};
+
+/* Hardware access */
+
+static inline void img_ir_write(struct img_ir_priv *priv,
+ unsigned int reg_offs, unsigned int data)
+{
+ iowrite32(data, priv->reg_base + reg_offs);
+}
+
+static inline unsigned int img_ir_read(struct img_ir_priv *priv,
+ unsigned int reg_offs)
+{
+ return ioread32(priv->reg_base + reg_offs);
+}
+
+#endif /* _IMG_IR_H_ */
--
1.8.1.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
* Re: [PATCH v2 2/2] Documentation: devicetree: Add boost-frequency binding to list boost mode frequency
From: Sudeep Holla @ 2014-02-07 16:15 UTC (permalink / raw)
To: Thomas Abraham, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org
Cc: Sudeep.Holla, rjw@rjwysocki.net,
linux-samsung-soc@vger.kernel.org, kgene.kim@samsung.com,
t.figa@samsung.com, l.majewski@samsung.com,
viresh.kumar@linaro.org, thomas.ab@samsung.com, Nishanth Menon,
Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala
In-Reply-To: <1391786342-11812-3-git-send-email-thomas.ab@samsung.com>
On 07/02/14 15:19, Thomas Abraham wrote:
> From: Thomas Abraham <thomas.ab@samsung.com>
>
> Add a new optional boost-frequency binding for specifying the frequencies
> usable in boost mode.
>
> Cc: Nishanth Menon <nm@ti.com>
> Cc: Lukasz Majewski <l.majewski@samsung.com>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Pawel Moll <pawel.moll@arm.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
> Cc: Kumar Gala <galak@codeaurora.org>
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> ---
> Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt | 11 +++++++++++
> 1 file changed, 11 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>
> diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
> new file mode 100644
> index 0000000..d925e38
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
> @@ -0,0 +1,11 @@
> +* Device tree binding for CPU boost frequency (aka over-clocking)
> +
> +Certain CPU's can be operated in optional 'boost' mode (or sometimes referred as
> +overclocking) in which the CPU can operate in frequencies beyond the normal
> +operating conditions.
> +
> +Optional Properties:
> +- boost-frequency: list of frequencies in KHz to be used only in boost mode.
> + This list should be a subset of frequencies listed in "operating-points"
> + property. Refer to Documentation/devicetree/bindings/power/opp.txt for
> + details about "operating-points" property.
>
Won't single entry for boost frequency suffice which would be the starting
frequency in the boost range. IWO will there be OPP list with frequencies:
A > B > C > D, but only B and C are boost frequency. That seems little odd,
unless it's some configuration chosen purely on software basis rather than
hardware. For me B marks the beginning of over-clocking.
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH] ARM: sunxi: Add driver for sunxi usb phy
From: Hans de Goede @ 2014-02-07 16:01 UTC (permalink / raw)
To: Maxime Ripard
Cc: Kishon Vijay Abraham I,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
linux-usb, linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20140115225255.GB31779@lukather>
Hi,
On 01/15/2014 11:52 PM, Maxime Ripard wrote:
> Hi Hans,
>
> Please keep me in CC for all the Allwinner-related patches.
Ok will do.
> On Tue, Jan 14, 2014 at 11:58:25PM +0100, Hans de Goede wrote:
>> The Allwinner A1x / A2x SoCs have 2 or 3 usb phys which are all accessed
>> through a single set of registers. Besides this there are also some other
>> phy related bits which need poking, which are per phy, but shared between the
>> ohci and ehci controllers, so these are also controlled from this new phy
>> driver.
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> ---
>> .../devicetree/bindings/phy/sun4i-usb-phy.txt | 26 ++
>> drivers/phy/Kconfig | 11 +
>> drivers/phy/Makefile | 1 +
>> drivers/phy/phy-sun4i-usb.c | 318 +++++++++++++++++++++
>> 4 files changed, 356 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> create mode 100644 drivers/phy/phy-sun4i-usb.c
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> new file mode 100644
>> index 0000000..6c54b3b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -0,0 +1,26 @@
>> +Allwinner sun4i USB PHY
>> +-----------------------
>> +
>> +Required properties:
>> +- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
>
> It is sun4i-usb-phy.
For completeness sake: this has been discussed elsewhere and we've agreed upon
using sun4i-a10-foo for all compat strings.
>
>> + "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
>> +- reg : 2 or 3 register offset + length pairs, 1 phy base reg pair +
>> + 1 pair for the pmu-irq register of each hcd
>
> In which order they should be set? Maybe you should use reg-names here
> to clarify things. From that documentation, I have no idea how I
> should put the values if I just want the common stuff and the (for
> example) usb1 configuration.
I've improved the doc text in my next revision.
>> +- #phy-cells : from the generic phy bindings, must be 1
>> +
>> +Optional properties:
>> +- clocks : phandle + clock specifier for the phy clock
>> +- clock-names : "usb_phy"
>> +- resets : a list of phandle + reset specifier pairs
>> +- reset-names : "usb0_reset", "usb1_reset", and / or "usb2_reset"
>> +
>> +Example:
>> + usbphy: phy@0x01c13400 {
>> + #phy-cells = <1>;
>> + compatible = "allwinner,sun4i-a10-usb-phy";
>> + reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
>
> If you prefer not to use reg-names after all, please put a comment
> stating what each pair correspond to.
Fixed (added a comment).
>
>> + clocks = <&usb_clk 8>;
>> + clock-names = "usb_phy";
>> + resets = <&usb_clk 1>, <&usb_clk 2>;
>> + reset-names = "usb1_reset", "usb2_reset";
>> + };
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 330ef2d..dcce4cf 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -51,4 +51,15 @@ config PHY_EXYNOS_DP_VIDEO
>> help
>> Support for Display Port PHY found on Samsung EXYNOS SoCs.
>>
>> +config PHY_SUN4I_USB
>> + tristate "Allwinner sunxi SoC USB PHY driver"
>> + depends on ARCH_SUNXI
>> + select GENERIC_PHY
>> + help
>> + Enable this to support the transceiver that is part of Allwinner
>> + sunxi SoCs.
>> +
>> + This driver controls the entire USB PHY block, both the USB OTG
>> + parts, as well as the 2 regular USB 2 host PHYs.
>> +
>> endmenu
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index d0caae9..e9e82f0 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -7,3 +7,4 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
>> obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
>> obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
>> obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
>> +obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> new file mode 100644
>> index 0000000..a15ecc1
>> --- /dev/null
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -0,0 +1,318 @@
>> +/*
>> + * Allwinner sun4i USB phy driver
>> + *
>> + * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> + *
>> + * Based on code from
>> + * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> + *
>> + * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
>> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
>> + * Author: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@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 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/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/reset.h>
>> +
>> +#define REG_ISCR 0x00
>> +#define REG_PHYCTL 0x04
>> +#define REG_PHYBIST 0x08
>> +#define REG_PHYTUNE 0x0c
>> +
>> +#define SUNXI_AHB_ICHR8_EN BIT(10)
>> +#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
>> +#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
>> +#define SUNXI_ULPI_BYPASS_EN BIT(0)
>> +
>> +#define MAX_PHYS 3
>> +
>> +struct sun4i_usb_phy_data {
>> + struct clk *clk;
>> + void __iomem *base;
>> + struct mutex mutex;
>> + int num_phys;
>> + u32 disc_thresh;
>> + struct sun4i_usb_phy {
>> + struct phy *phy;
>> + void __iomem *pmu_irq;
>> + struct regulator *vbus;
>> + struct reset_control *reset;
>> + int index;
>> + } phys[MAX_PHYS];
>> +};
>> +
>> +#define to_sun4i_usb_phy_data(phy) \
>> + container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>> +
>> +static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>> + int len)
>> +{
>> + struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
>> + u32 temp, usbc_bit = BIT(phy->index * 2);
>> + int i;
>> +
>> + mutex_lock(&phy_data->mutex);
>> +
>> + for (i = 0; i < len; i++) {
>> + temp = readl(phy_data->base + REG_PHYCTL);
>> +
>> + /* clear the address portion */
>> + temp &= ~(0xff << 8);
>> +
>> + /* set the address */
>> + temp |= ((addr + i) << 8);
>> + writel(temp, phy_data->base + REG_PHYCTL);
>> +
>> + /* set the data bit and clear usbc bit*/
>> + temp = readb(phy_data->base + REG_PHYCTL);
>> + if (data & 0x1)
>> + temp |= BIT(7);
>> + else
>> + temp &= ~BIT(7);
>> + temp &= ~usbc_bit;
>> + writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> + /* pulse usbc_bit */
>> + temp = readb(phy_data->base + REG_PHYCTL);
>> + temp |= usbc_bit;
>> + writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> + temp = readb(phy_data->base + REG_PHYCTL);
>> + temp &= ~usbc_bit;
>> + writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> + data >>= 1;
>> + }
>> + mutex_unlock(&phy_data->mutex);
>> +}
>> +
>> +static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
>> +{
>> + u32 bits, reg_value;
>> +
>> + if (!phy->pmu_irq)
>> + return;
>> +
>> + bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
>> + SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
>> +
>> + reg_value = readl(phy->pmu_irq);
>> +
>> + if (enable)
>> + reg_value |= bits;
>> + else
>> + reg_value &= ~bits;
>> +
>> + writel(reg_value, phy->pmu_irq);
>> +}
>> +
>> +static int sun4i_usb_phy_init(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> + int ret;
>> +
>> + ret = clk_prepare_enable(data->clk);
>> + if (ret)
>> + return ret;
>> +
>> + ret = reset_control_deassert(phy->reset);
>> + if (ret) {
>> + clk_disable_unprepare(data->clk);
>> + return ret;
>> + }
>> +
>> + /* Adjust PHY's magnitude and rate */
>> + sun4i_usb_phy_write(phy, 0x20, 0x14, 5);
>> +
>> + /* Disconnect threshold adjustment */
>> + sun4i_usb_phy_write(phy, 0x2a, data->disc_thresh, 2);
>> +
>> + sun4i_usb_phy_passby(phy, 1);
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_exit(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> + sun4i_usb_phy_passby(phy, 0);
>> + reset_control_assert(phy->reset);
>> + clk_disable_unprepare(data->clk);
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_power_on(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> + int ret;
>> +
>> + if (phy->vbus) {
>> + ret = regulator_enable(phy->vbus);
>> + if (ret)
>> + return ret;
>> +
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_power_off(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +
>> + if (phy->vbus)
>> + regulator_disable(phy->vbus);
>> +
>> + return 0;
>> +}
>> +
>> +static struct phy_ops sun4i_usb_phy_ops = {
>> + .init = sun4i_usb_phy_init,
>> + .exit = sun4i_usb_phy_exit,
>> + .power_on = sun4i_usb_phy_power_on,
>> + .power_off = sun4i_usb_phy_power_off,
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>> + struct of_phandle_args *args)
>> +{
>> + struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> + if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
>> + return ERR_PTR(-ENODEV);
>> +
>> + return data->phys[args->args[0]].phy;
>> +}
>> +
>> +static int sun4i_usb_phy_probe(struct platform_device *pdev)
>> +{
>> + struct sun4i_usb_phy_data *data;
>> + struct device *dev = &pdev->dev;
>> + struct device_node *np = dev->of_node;
>> + void __iomem *pmu_irq = NULL;
>> + struct phy_provider *phy_provider;
>> + struct reset_control *reset;
>> + struct regulator *vbus;
>> + struct phy *phy;
>> + char name[16];
>> + int i;
>> +
>> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>> + if (!data)
>> + return -ENOMEM;
>> +
>> + mutex_init(&data->mutex);
>> + if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy")) {
>> + data->num_phys = 3;
>> + data->disc_thresh = 3;
>> + } else if (of_device_is_compatible(np,
>> + "allwinner,sun5i-a13-usb-phy")) {
>> + data->num_phys = 2;
>> + data->disc_thresh = 2;
>> + } else { /* allwinner,sun7i-a20-usb-phy */
>> + data->num_phys = 3;
>> + data->disc_thresh = 2;
>> + }
>
> Maybe we can use of_match_data() here instead of having an ever-growing
> list of if-else statements?
I've refactored this a bit to get rid of the if .. else if .. else structure.
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v2 1/2] PM / OPP: Allow boost frequency to be looked up from device tree
From: Sudeep Holla @ 2014-02-07 16:01 UTC (permalink / raw)
To: Thomas Abraham, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org
Cc: Sudeep.Holla, rjw@rjwysocki.net,
linux-samsung-soc@vger.kernel.org, kgene.kim@samsung.com,
t.figa@samsung.com, l.majewski@samsung.com,
viresh.kumar@linaro.org, thomas.ab@samsung.com, Nishanth Menon
In-Reply-To: <1391786342-11812-2-git-send-email-thomas.ab@samsung.com>
On 07/02/14 15:19, Thomas Abraham wrote:
> From: Thomas Abraham <thomas.ab@samsung.com>
>
> Commit 6f19efc0 ("cpufreq: Add boost frequency support in core") adds
> support for CPU boost mode. This patch adds support for finding available
> boost frequencies from device tree and marking them as usable in boost mode.
>
> Cc: Nishanth Menon <nm@ti.com>
> Cc: Lukasz Majewski <l.majewski@samsung.com>
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> ---
> drivers/base/power/opp.c | 34 +++++++++++++++++++++++++++++++++-
> 1 file changed, 33 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
> index fa41874..b636826 100644
> --- a/drivers/base/power/opp.c
> +++ b/drivers/base/power/opp.c
> @@ -628,7 +628,8 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
> struct device_opp *dev_opp;
> struct dev_pm_opp *opp;
> struct cpufreq_frequency_table *freq_table;
> - int i = 0;
> + int i = 0, j, len, ret;
> + u32 *boost_freqs = NULL;
>
> /* Pretend as if I am an updater */
> mutex_lock(&dev_opp_list_lock);
> @@ -650,10 +651,35 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
> return -ENOMEM;
> }
>
> + if (of_find_property(dev->of_node, "boost-frequency", &len)) {
> + if (len == 0 || (len & (sizeof(u32) - 1)) != 0) {
> + dev_err(dev, "%s: invalid boost frequency\n", __func__);
> + ret = -EINVAL;
> + goto err_boost;
> + }
> +
> + boost_freqs = kzalloc(len, GFP_KERNEL);
> + if (!boost_freqs) {
> + dev_warn(dev, "%s: no memory for boost freq table\n",
> + __func__);
> + ret = -ENOMEM;
> + goto err_boost;
> + }
> + of_property_read_u32_array(dev->of_node, "boost-frequency",
> + boost_freqs, len / sizeof(u32));
> + }
> +
> list_for_each_entry(opp, &dev_opp->opp_list, node) {
> if (opp->available) {
> freq_table[i].driver_data = i;
> freq_table[i].frequency = opp->rate / 1000;
> + for (j = 0; j < len / sizeof(u32) && boost_freqs; j++) {
> + if (boost_freqs[j] == freq_table[i].frequency) {
> + freq_table[i].driver_data =
> + CPUFREQ_BOOST_FREQ;
> + break;
> + }
> + }
> i++;
> }
> }
IIRC you had mentioned that the boost-opp was not limited to be a cpufreq, but
this change seems to be cpufreq only.
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH] ARM: sunxi: Add driver for sunxi usb phy
From: Hans de Goede @ 2014-02-07 15:57 UTC (permalink / raw)
To: Kishon Vijay Abraham I
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
linux-usb, linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <52D6A27A.4090600-l0cyMroinI0@public.gmane.org>
Hi,
On 01/15/2014 04:00 PM, Kishon Vijay Abraham I wrote:
> On Wednesday 15 January 2014 04:28 AM, Hans de Goede wrote:
>> The Allwinner A1x / A2x SoCs have 2 or 3 usb phys which are all accessed
>> through a single set of registers. Besides this there are also some other
>> phy related bits which need poking, which are per phy, but shared between the
>> ohci and ehci controllers, so these are also controlled from this new phy
>> driver.
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> ---
>> .../devicetree/bindings/phy/sun4i-usb-phy.txt | 26 ++
>> drivers/phy/Kconfig | 11 +
>> drivers/phy/Makefile | 1 +
>> drivers/phy/phy-sun4i-usb.c | 318 +++++++++++++++++++++
>> 4 files changed, 356 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> create mode 100644 drivers/phy/phy-sun4i-usb.c
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> new file mode 100644
>> index 0000000..6c54b3b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -0,0 +1,26 @@
>> +Allwinner sun4i USB PHY
>> +-----------------------
>> +
>> +Required properties:
>> +- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
>> + "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
>> +- reg : 2 or 3 register offset + length pairs, 1 phy base reg pair +
>> + 1 pair for the pmu-irq register of each hcd
>> +- #phy-cells : from the generic phy bindings, must be 1
>> +
>> +Optional properties:
>> +- clocks : phandle + clock specifier for the phy clock
>> +- clock-names : "usb_phy"
>> +- resets : a list of phandle + reset specifier pairs
>> +- reset-names : "usb0_reset", "usb1_reset", and / or "usb2_reset"
>> +
>> +Example:
>> + usbphy: phy@0x01c13400 {
>> + #phy-cells = <1>;
>> + compatible = "allwinner,sun4i-a10-usb-phy";
>> + reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
>> + clocks = <&usb_clk 8>;
>> + clock-names = "usb_phy";
>> + resets = <&usb_clk 1>, <&usb_clk 2>;
>> + reset-names = "usb1_reset", "usb2_reset";
>> + };
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 330ef2d..dcce4cf 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -51,4 +51,15 @@ config PHY_EXYNOS_DP_VIDEO
>> help
>> Support for Display Port PHY found on Samsung EXYNOS SoCs.
>>
>> +config PHY_SUN4I_USB
>> + tristate "Allwinner sunxi SoC USB PHY driver"
>> + depends on ARCH_SUNXI
>> + select GENERIC_PHY
>> + help
>> + Enable this to support the transceiver that is part of Allwinner
>> + sunxi SoCs.
>> +
>> + This driver controls the entire USB PHY block, both the USB OTG
>> + parts, as well as the 2 regular USB 2 host PHYs.
>> +
>> endmenu
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index d0caae9..e9e82f0 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -7,3 +7,4 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
>> obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
>> obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
>> obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
>> +obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> new file mode 100644
>> index 0000000..a15ecc1
>> --- /dev/null
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -0,0 +1,318 @@
>> +/*
>> + * Allwinner sun4i USB phy driver
>> + *
>> + * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> + *
>> + * Based on code from
>> + * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> + *
>> + * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
>> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
>> + * Author: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@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 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/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/reset.h>
>> +
>> +#define REG_ISCR 0x00
>> +#define REG_PHYCTL 0x04
>> +#define REG_PHYBIST 0x08
>> +#define REG_PHYTUNE 0x0c
>> +
>> +#define SUNXI_AHB_ICHR8_EN BIT(10)
>> +#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
>> +#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
>> +#define SUNXI_ULPI_BYPASS_EN BIT(0)
>> +
>> +#define MAX_PHYS 3
>> +
>> +struct sun4i_usb_phy_data {
>> + struct clk *clk;
>> + void __iomem *base;
>> + struct mutex mutex;
>> + int num_phys;
>> + u32 disc_thresh;
>> + struct sun4i_usb_phy {
>> + struct phy *phy;
>> + void __iomem *pmu_irq;
>
> 'pmu_irq' is misleading. Can you think of a better name?
Changed this to pmu in my local tree, I'll send a v2 after some testing.
>
> Btw Kamil uses syscon interface to set pmu bits. Is it applicable here also?
The syscon interface is useful only for shared registers, this register is only
used by the phy driver, so I see no value in using the syscon interface.
>> + struct regulator *vbus;
>> + struct reset_control *reset;
>> + int index;
>> + } phys[MAX_PHYS];
>> +};
>> +
>> +#define to_sun4i_usb_phy_data(phy) \
>> + container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>> +
>> +static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>> + int len)
>> +{
>> + struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
>> + u32 temp, usbc_bit = BIT(phy->index * 2);
>> + int i;
>> +
>> + mutex_lock(&phy_data->mutex);
>> +
>> + for (i = 0; i < len; i++) {
>> + temp = readl(phy_data->base + REG_PHYCTL);
>> +
>> + /* clear the address portion */
>> + temp &= ~(0xff << 8);
>> +
>> + /* set the address */
>> + temp |= ((addr + i) << 8);
>> + writel(temp, phy_data->base + REG_PHYCTL);
>> +
>> + /* set the data bit and clear usbc bit*/
>> + temp = readb(phy_data->base + REG_PHYCTL);
>> + if (data & 0x1)
>> + temp |= BIT(7);
>> + else
>> + temp &= ~BIT(7);
>> + temp &= ~usbc_bit;
>> + writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> + /* pulse usbc_bit */
>> + temp = readb(phy_data->base + REG_PHYCTL);
>> + temp |= usbc_bit;
>> + writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> + temp = readb(phy_data->base + REG_PHYCTL);
>> + temp &= ~usbc_bit;
>> + writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> + data >>= 1;
>> + }
>> + mutex_unlock(&phy_data->mutex);
>> +}
>> +
>> +static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
>> +{
>> + u32 bits, reg_value;
>> +
>> + if (!phy->pmu_irq)
>> + return;
>> +
>> + bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
>> + SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
>> +
>> + reg_value = readl(phy->pmu_irq);
>> +
>> + if (enable)
>> + reg_value |= bits;
>> + else
>> + reg_value &= ~bits;
>> +
>> + writel(reg_value, phy->pmu_irq);
>> +}
>> +
>> +static int sun4i_usb_phy_init(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> + int ret;
>> +
>> + ret = clk_prepare_enable(data->clk);
>> + if (ret)
>> + return ret;
>> +
>> + ret = reset_control_deassert(phy->reset);
>> + if (ret) {
>> + clk_disable_unprepare(data->clk);
>> + return ret;
>> + }
>> +
>> + /* Adjust PHY's magnitude and rate */
>> + sun4i_usb_phy_write(phy, 0x20, 0x14, 5);
>
> No magic values. Use macros instead.
Fixed for the addresses, the 0x14 will stay magic though, as we've no
idea what it actually does.
>> +
>> + /* Disconnect threshold adjustment */
>> + sun4i_usb_phy_write(phy, 0x2a, data->disc_thresh, 2);
>> +
>> + sun4i_usb_phy_passby(phy, 1);
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_exit(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> + sun4i_usb_phy_passby(phy, 0);
>> + reset_control_assert(phy->reset);
>> + clk_disable_unprepare(data->clk);
>
> Actually PHY API's can be called in interrupt context, in that case
> clk_disable_unprepare can't be used.
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_power_on(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> + int ret;
>> +
>> + if (phy->vbus) {
>> + ret = regulator_enable(phy->vbus);
>> + if (ret)
>> + return ret;
>> +
>> + }
>> +
>> + return 0;
>> +}
>
> This can simply be
> int ret = 0;
> if (phy->vbus)
> ret = regulator_enable(phy->vbus);
> return ret;
Fixed.
>> +
>> +static int sun4i_usb_phy_power_off(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +
>> + if (phy->vbus)
>> + regulator_disable(phy->vbus);
>> +
>> + return 0;
>> +}
>> +
>> +static struct phy_ops sun4i_usb_phy_ops = {
>> + .init = sun4i_usb_phy_init,
>> + .exit = sun4i_usb_phy_exit,
>> + .power_on = sun4i_usb_phy_power_on,
>> + .power_off = sun4i_usb_phy_power_off,
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>> + struct of_phandle_args *args)
>> +{
>> + struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> + if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
>> + return ERR_PTR(-ENODEV);
>> +
>> + return data->phys[args->args[0]].phy;
>> +}
>> +
>> +static int sun4i_usb_phy_probe(struct platform_device *pdev)
>> +{
>> + struct sun4i_usb_phy_data *data;
>> + struct device *dev = &pdev->dev;
>> + struct device_node *np = dev->of_node;
>> + void __iomem *pmu_irq = NULL;
>> + struct phy_provider *phy_provider;
>> + struct reset_control *reset;
>> + struct regulator *vbus;
>> + struct phy *phy;
>> + char name[16];
>> + int i;
>> +
>> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>> + if (!data)
>> + return -ENOMEM;
>> +
>> + mutex_init(&data->mutex);
>> + if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy")) {
>> + data->num_phys = 3;
>> + data->disc_thresh = 3;
>> + } else if (of_device_is_compatible(np,
>> + "allwinner,sun5i-a13-usb-phy")) {
>> + data->num_phys = 2;
>> + data->disc_thresh = 2;
>> + } else { /* allwinner,sun7i-a20-usb-phy */
>> + data->num_phys = 3;
>> + data->disc_thresh = 2;
>> + }
>> +
>> + data->clk = devm_clk_get(dev, "usb_phy");
>> + if (IS_ERR(data->clk)) {
>> + dev_err(dev, "could not get usb_phy clock\n");
>> + return PTR_ERR(data->clk);
>> + }
>> +
>> + /* Skip 0, 0 is the phy for otg which is not yet supported. */
>> + for (i = 1; i < data->num_phys; i++) {
>> + snprintf(name, sizeof(name), "usb%d_vbus", i);
>> + vbus = devm_regulator_get_optional(dev, name);
>> + if (IS_ERR(vbus)) {
>> + if (PTR_ERR(vbus) == -EPROBE_DEFER)
>> + return -EPROBE_DEFER;
>> + vbus = NULL;
>> + }
>> +
>> + snprintf(name, sizeof(name), "usb%d_reset", i);
>> + reset = devm_reset_control_get(dev, name);
>> + if (IS_ERR(phy)) {
>> + dev_err(dev, "failed to get reset %s\n", name);
>> + return PTR_ERR(phy);
>> + }
>> +
>> + if (i) { /* No pmu_irq for usbc0 */
>> + pmu_irq = devm_ioremap_resource(dev,
>> + platform_get_resource(pdev, IORESOURCE_MEM, i));
>> + if (IS_ERR(pmu_irq))
>> + return PTR_ERR(pmu_irq);
>> + }
>> +
>> + phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
>> + if (IS_ERR(phy)) {
>> + dev_err(dev, "failed to create PHY %d\n", i);
>> + return PTR_ERR(phy);
>> + }
>> +
>> + data->phys[i].phy = phy;
>> + data->phys[i].pmu_irq = pmu_irq;
>> + data->phys[i].vbus = vbus;
>> + data->phys[i].reset = reset;
>> + data->phys[i].index = i;
>> + phy_set_drvdata(phy, &data->phys[i]);
>> + }
>> +
>> + data->base = devm_ioremap_resource(dev,
>> + platform_get_resource(pdev, IORESOURCE_MEM, 0));
>> + if (IS_ERR(data->base))
>> + return PTR_ERR(data->base);
>> +
>> + dev_set_drvdata(dev, data);
>> + phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>> + if (IS_ERR(phy_provider))
>> + return PTR_ERR(phy_provider);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id sun4i_usb_phy_of_match[] = {
>> + { .compatible = "allwinner,sun4i-a10-usb-phy" },
>> + { .compatible = "allwinner,sun5i-a13-usb-phy" },
>> + { .compatible = "allwinner,sun7i-a20-usb-phy" },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>> +
>> +static struct platform_driver sun4i_usb_phy_driver = {
>> + .probe = sun4i_usb_phy_probe,
>> + .driver = {
>> + .of_match_table = sun4i_usb_phy_of_match,
>> + .name = "sun4i-usb-phy",
>> + .owner = THIS_MODULE,
>> + }
>> +};
>> +module_platform_driver(sun4i_usb_phy_driver);
>> +
>> +MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
>> +MODULE_AUTHOR("Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
>> +MODULE_LICENSE("GPL");
>
> GPL v2?
Fixed.
> This patch looks good apart from those minor comments.
Ok v2 is on its way.
Regards,
Hans
^ permalink raw reply
* [PATCH v3 3/7] Documentation: devicetree: add cpu clock configuration data binding for Exynos4/5
From: Thomas Abraham @ 2014-02-07 15:55 UTC (permalink / raw)
To: cpufreq, linux-arm-kernel
Cc: linux-samsung-soc, mturquette, shawn.guo, kgene.kim, t.figa,
l.majewski, viresh.kumar, thomas.ab, heiko, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, devicetree
In-Reply-To: <1391788548-13056-1-git-send-email-thomas.ab@samsung.com>
From: Thomas Abraham <thomas.ab@samsung.com>
The clock blocks within the CMU_CPU clock domain are put together into a
new composite clock type called the cpu clock. This clock type requires
configuration data that will be atomically programmed in the multiple
clock blocks encapsulated within the cpu clock type when the parent clock
frequency is changed. This configuration data is held in the clock controller
node. Update clock binding documentation about this configuration data format
for Samsung Exynos4 and Exynos5 platforms.
Cc: Tomasz Figa <t.figa@samsung.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
---
.../devicetree/bindings/clock/exynos4-clock.txt | 37 +++++++++++++++++++
.../devicetree/bindings/clock/exynos5250-clock.txt | 38 ++++++++++++++++++++
2 files changed, 75 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/exynos4-clock.txt b/Documentation/devicetree/bindings/clock/exynos4-clock.txt
index a2ac2d9..b505e17 100644
--- a/Documentation/devicetree/bindings/clock/exynos4-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos4-clock.txt
@@ -15,6 +15,35 @@ Required Properties:
- #clock-cells: should be 1.
+- samsung,armclk-divider-table: when the frequency of the APLL is changed
+ the divider clocks in CMU_CPU clock domain also need to be updated. These
+ divider clocks have SoC specific divider clock output requirements for a
+ specific APLL clock speeds. When APLL clock rate is changed, these divider
+ clocks are reprogrammed with pre-determined values in order to maintain the
+ SoC specific divider clock outputs. This property lists the divider values
+ for divider clocks in the CMU_CPU block for supported APLL clock speeds.
+ The format of each entry included in the arm-frequency-table should be
+ as defined below
+
+ - for Exynos4210 and Exynos4212 based platforms:
+ cell #1: arm clock parent frequency
+ cell #2 ~ cell 9#: value of clock divider in the following order
+ corem0_ratio, corem1_ratio, periph_ratio, atb_ratio,
+ pclk_dbg_ratio, apll_ratio, copy_ratio, hpm_ratio.
+
+ - for Exynos4412 based platforms:
+ cell #1: expected arm clock parent frequency
+ cell #2 ~ cell #10: value of clock divider in the following order
+ corem0_ratio, corem1_ratio, periph_ratio, atb_ratio,
+ pclk_dbg_ratio, apll_ratio, copy_ratio, hpm_ratio, cores_ratio
+
+- samsung,armclk-cells: defines the number of cells in
+ samsung,armclk-divider-table property. The value of this property depends on
+ the SoC type.
+
+ - for Exynos4210 and Exynos4212: the value should be 9.
+ - for Exynos4412: the value should be 10.
+
The following is the list of clocks generated by the controller. Each clock is
assigned an identifier and client nodes use this identifier to specify the
clock which they consume. Some of the clocks are available only on a particular
@@ -275,6 +304,14 @@ Example 1: An example of a clock controller node is listed below.
compatible = "samsung,exynos4210-clock";
reg = <0x10030000 0x20000>;
#clock-cells = <1>;
+
+ samsung,armclk-cells = <9>;
+ samsung,armclk-divider-table = <1200000 3 7 3 4 1 7 5 0>,
+ <1000000 3 7 3 4 1 7 4 0>,
+ < 800000 3 7 3 3 1 7 3 0>,
+ < 500000 3 7 3 3 1 7 3 0>,
+ < 400000 3 7 3 3 1 7 3 0>,
+ < 200000 1 3 1 1 1 0 3 0>;
};
Example 2: UART controller node that consumes the clock generated by the clock
diff --git a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
index 72ce617..9ca818e 100644
--- a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
@@ -13,6 +13,25 @@ Required Properties:
- #clock-cells: should be 1.
+- samsung,armclk-divider-table: when the frequency of the APLL is changed
+ the divider clocks in CMU_CPU clock domain also need to be updated. These
+ divider clocks have SoC specific divider clock output requirements for a
+ specific APLL clock speeds. When APLL clock rate is changed, these divider
+ clocks are reprogrammed with pre-determined values in order to maintain the
+ SoC specific divider clock outputs. This property lists the divider values
+ for divider clocks in the CMU_CPU block for supported APLL clock speeds.
+ The format of each entry included in the arm-frequency-table should be
+ as defined below
+
+ cell #1: expected arm clock parent frequency
+ cell #2 ~ cell #9: value of clock divider in the following order
+ cpud_ratio, acp_ratio, periph_ratio, atb_ratio,
+ pclk_dbg_ratio, apll_ratio, copy_ratio, hpm_ratio
+
+- samsung,armclk-cells: defines the number of cells in
+ samsung,armclk-divider-table property. The value of this property should be 9.
+
+
The following is the list of clocks generated by the controller. Each clock is
assigned an identifier and client nodes use this identifier to specify the
clock which they consume.
@@ -177,6 +196,25 @@ Example 1: An example of a clock controller node is listed below.
compatible = "samsung,exynos5250-clock";
reg = <0x10010000 0x30000>;
#clock-cells = <1>;
+
+ samsung,armclk-cells = <9>;
+ samsung,armclk-divider-table = <1700000 3 7 7 7 3 5 0 2>,
+ <1600000 3 7 7 7 1 4 0 2>,
+ <1500000 2 7 7 7 1 4 0 2>,
+ <1400000 2 7 7 6 1 4 0 2>,
+ <1300000 2 7 7 6 1 3 0 2>,
+ <1200000 2 7 7 5 1 3 0 2>,
+ <1100000 3 7 7 5 1 3 0 2>,
+ <1000000 1 7 7 4 1 2 0 2>,
+ < 900000 1 7 7 4 1 2 0 2>,
+ < 800000 1 7 7 4 1 2 0 2>,
+ < 700000 1 7 7 3 1 1 0 2>,
+ < 600000 1 7 7 3 1 1 0 2>,
+ < 500000 1 7 7 2 1 1 0 2>,
+ < 400000 1 7 7 2 1 1 0 2>,
+ < 300000 1 7 7 1 1 1 0 2>,
+ < 200000 1 7 7 1 1 1 0 2>;
+
};
Example 2: UART controller node that consumes the clock generated by the clock
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH v2 1/2] PM / OPP: Allow boost frequency to be looked up from device tree
From: Nishanth Menon @ 2014-02-07 15:52 UTC (permalink / raw)
To: Thomas Abraham
Cc: dt list, Lukasz Majewski, linux-samsung-soc@vger.kernel.org,
linux-pm, Viresh Kumar, Tomasz Figa, rjw, Kukjin Kim, thomas.ab,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <CAJuA9aiEDXyK8ABNznbzjk7s2uMfwo_diRpmLNsKBFd+pH=ffQ@mail.gmail.com>
On 02/07/2014 09:38 AM, Thomas Abraham wrote:
[...]
>>> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
>>> index fa41874..b636826 100644
>>> --- a/drivers/base/power/opp.c
>>> +++ b/drivers/base/power/opp.c
>>> @@ -628,7 +628,8 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
>>> struct device_opp *dev_opp;
>>> struct dev_pm_opp *opp;
>>> struct cpufreq_frequency_table *freq_table;
>>> - int i = 0;
>>> + int i = 0, j, len, ret;
>>> + u32 *boost_freqs = NULL;
>>>
>>> /* Pretend as if I am an updater */
>>> mutex_lock(&dev_opp_list_lock);
>>> @@ -650,10 +651,35 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
>>> return -ENOMEM;
>>> }
>>>
>>> + if (of_find_property(dev->of_node, "boost-frequency", &len)) {
>>> + if (len == 0 || (len & (sizeof(u32) - 1)) != 0) {
>>> + dev_err(dev, "%s: invalid boost frequency\n", __func__);
>>> + ret = -EINVAL;
>>> + goto err_boost;
>>> + }
>>> +
>>> + boost_freqs = kzalloc(len, GFP_KERNEL);
>>> + if (!boost_freqs) {
>>> + dev_warn(dev, "%s: no memory for boost freq table\n",
>>> + __func__);
>>> + ret = -ENOMEM;
>>> + goto err_boost;
>>> + }
>>> + of_property_read_u32_array(dev->of_node, "boost-frequency",
>>> + boost_freqs, len / sizeof(u32));
>>> + }
>>> +
>>> list_for_each_entry(opp, &dev_opp->opp_list, node) {
>>> if (opp->available) {
>>> freq_table[i].driver_data = i;
>>> freq_table[i].frequency = opp->rate / 1000;
>>> + for (j = 0; j < len / sizeof(u32) && boost_freqs; j++) {
>>> + if (boost_freqs[j] == freq_table[i].frequency) {
>>> + freq_table[i].driver_data =
>>> + CPUFREQ_BOOST_FREQ;
>>> + break;
>>> + }
>>> + }
>>
>> What if any one of the boost_freqs are not contained in the enabled frequencies?
>
> It is not used as a boost frequency because its corresponding voltage
> is not known. If required a warning can be printed out for the same.
yes - that would be good, as it helps debug if there are developer
errors in dts.
--
Regards,
Nishanth Menon
^ permalink raw reply
* [PATCH v3 06/15] dt: binding: add binding for ImgTec IR block
From: James Hogan @ 2014-02-07 15:49 UTC (permalink / raw)
To: Mauro Carvalho Chehab, linux-media, Rob Herring
Cc: James Hogan, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
devicetree, Rob Landley, linux-doc, Tomasz Figa
In-Reply-To: <CAL_JsqLL6MbwajCUAm+NJk=ofL5OHq8b0zwO3LFb-TKY6UtVMQ@mail.gmail.com>
Add device tree binding for ImgTec Consumer Infrared block, specifically
major revision 1 of the hardware.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
Cc: linux-media@vger.kernel.org
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: devicetree@vger.kernel.org
Cc: Rob Landley <rob@landley.net>
Cc: linux-doc@vger.kernel.org
Cc: Tomasz Figa <tomasz.figa@gmail.com>
---
v3:
- Rename compatible string to "img,ir-rev1" (Rob Herring).
- Specify ordering of clocks explicitly (Rob Herring).
v2:
- Future proof compatible string from "img,ir" to "img,ir1", where the 1
corresponds to the major revision number of the hardware (Tomasz
Figa).
- Added clock-names property and three specific clock names described in
the manual, only one of which is used by the current driver (Tomasz
Figa).
---
.../devicetree/bindings/media/img-ir-rev1.txt | 34 ++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/img-ir-rev1.txt
diff --git a/Documentation/devicetree/bindings/media/img-ir-rev1.txt b/Documentation/devicetree/bindings/media/img-ir-rev1.txt
new file mode 100644
index 000000000000..5434ce61b925
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/img-ir-rev1.txt
@@ -0,0 +1,34 @@
+* ImgTec Infrared (IR) decoder version 1
+
+This binding is for Imagination Technologies' Infrared decoder block,
+specifically major revision 1.
+
+Required properties:
+- compatible: Should be "img,ir-rev1"
+- reg: Physical base address of the controller and length of
+ memory mapped region.
+- interrupts: The interrupt specifier to the cpu.
+
+Optional properties:
+- clocks: List of clock specifiers as described in standard
+ clock bindings.
+ Up to 3 clocks may be specified in the following order:
+ 1st: Core clock (defaults to 32.768KHz if omitted).
+ 2nd: System side (fast) clock.
+ 3rd: Power modulation clock.
+- clock-names: List of clock names corresponding to the clocks
+ specified in the clocks property.
+ Accepted clock names are:
+ "core": Core clock.
+ "sys": System clock.
+ "mod": Power modulation clock.
+
+Example:
+
+ ir@02006200 {
+ compatible = "img,ir-rev1";
+ reg = <0x02006200 0x100>;
+ interrupts = <29 4>;
+ clocks = <&clk_32khz>;
+ clock-names = "core";
+ };
--
1.8.1.2
^ permalink raw reply related
* Re: [PATCH] ARM: at91/dt: fix sama5d3 ohci hclk clock reference
From: Nicolas Ferre @ 2014-02-07 15:46 UTC (permalink / raw)
To: Boris BREZILLON
Cc: Jean-Christophe Plagniol-Villard, Russell King, linux-arm-kernel,
devicetree, linux-kernel
In-Reply-To: <1389885934-26119-1-git-send-email-b.brezillon@overkiz.com>
On 16/01/2014 16:25, Boris BREZILLON :
> The hclk clock of the ohci node is referencing udphs_clk instead of
> uhphs_clk.
>
> Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Added to at91-3.14-fixes
Thanks,
> ---
> arch/arm/boot/dts/sama5d3.dtsi | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi
> index e398e5d..31356e2 100644
> --- a/arch/arm/boot/dts/sama5d3.dtsi
> +++ b/arch/arm/boot/dts/sama5d3.dtsi
> @@ -1195,7 +1195,7 @@
> compatible = "atmel,at91rm9200-ohci", "usb-ohci";
> reg = <0x00600000 0x100000>;
> interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>;
> - clocks = <&usb>, <&uhphs_clk>, <&udphs_clk>,
> + clocks = <&usb>, <&uhphs_clk>, <&uhphs_clk>,
> <&uhpck>;
> clock-names = "usb_clk", "ohci_clk", "hclk", "uhpck";
> status = "disabled";
>
--
Nicolas Ferre
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox