* [PATH v2 0/2] spi: Add Qualcomm QUP SPI controller support
From: Ivan T. Ivanov @ 2014-02-13 16:21 UTC (permalink / raw)
To: Mark Brown, Grant Likely, Rob Herring
Cc: Ivan T. Ivanov, linux-spi, linux-arm-msm, linux-kernel,
devicetree
From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
Hi,
Following two patches are adding initial support for SPI controller
available in Qualcomm SoC's.
Controller initialization is based on spi_qsd driver available in
CAF repository.
Controller supports SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP modes,
up to 4 CS's and from 4 to 32 bits per word. SPI_LOOP mode is limited
to input FIFO buffer size.
Currently driver support only PIO mode, I am hopping to add also DMA
mode support with dmaengine patches developed by Andy.
Changes since first version:
- Replace master::transfer_one_message with master::transfer_one and
master::set_cs.
- Use full controller version for compatible string
"qcom,spi-qup-v2.1.1" and "qcom,spi-qup-v2.2.1".
- Ensure that controller internal state is changed only if it is in
valid state.
- Ensure that resources shared between interrupt and thread context
are properly protected.
- Use controller auto clock gating for run time power management,
instead full clock stop. This should have the same net result, right?
- Simplify a bit read and write FIFO routines.
- Several useless print messages removed.
Ivan T. Ivanov (2):
spi: qup: Add device tree bindings information
spi: Add Qualcomm QUP SPI controller support
.../devicetree/bindings/spi/qcom,spi-qup.txt | 85 ++
drivers/spi/Kconfig | 13 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-qup.c | 837 ++++++++++++++++++++
4 files changed, 936 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
create mode 100644 drivers/spi/spi-qup.c
--
1.7.9.5
^ permalink raw reply
* Re: [PATCH 1/3] mmc: add support for power-on sequencing through DT
From: Arnd Bergmann @ 2014-02-13 16:13 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: linux-arm-kernel, mark.rutland, devicetree, Ulf Hansson,
Pawel Moll, Ian Campbell, Tomasz Figa, linux-mmc, Tomasz Figa,
Chris Ball, robh+dt, Kumar Gala, Olof Johansson, Fabio Estevam,
Sascha Hauer
In-Reply-To: <20140213144106.GW26684@n2100.arm.linux.org.uk>
On Thursday 13 February 2014 14:41:06 Russell King - ARM Linux wrote:
> On Thu, Feb 13, 2014 at 01:48:55PM +0100, Arnd Bergmann wrote:
> > On Thursday 13 February 2014 10:42:48 Russell King - ARM Linux wrote:
> > >
> > > What if we have a platform where things subtly change, like for instance,
> > > the wiring on the SD slot to fix a problem with UHS-1 cards, which means
> > > you don't have UHS-1 support for some platforms but do for others.
> > >
> > > What if you have a platform which uses a brcm4329 chip for Wifi, but then
> > > later in the production run switch to using a different Wifi chipset?
> >
> > As far as I can tell, the power sequencing is normally really
> > dependent on the device. If someone has an on-board brcm4329
> > that requires a specific set of clocks, resets, voltages etc
> > to be routed to the chip and enabled in the correct order to
> > allow probing, it seems unlikely that changing the wifi chipset
> > to something else would keep the exact same requirements.
>
> That's your assertion - however, do we /know/ whether there's a situation
> where Olof's solution doesn't work because the sequencing is wrong?
>
> I see nothing unreasonable about the sequence:
>
> 1. hold reset at low level
> 2. apply power
> 3. turn clock on
> 4. apply reset
> 5. release reset
I was thinking of cases where you may need a more complex sequence:
- wait for a device specific time between some of the steps
(the cw1200 driver seems to need that, but we could probably
get away with waiting long enough for everyone)
- have more than one of each, and turn them on in the right order.
cw1200 seems to need two voltages, two gpio resets ("reset"
and "powerup").
Again, we could specify a larger number of clocks that can be
provided to the host, but if we make it a device specific
property, we already know how many we need.
I can't think of anything that would require significant changes
to the procedure though, just refinements as we run into problems.
> The points being:
> * never set a signal to a high level before power is applied, otherwise
> we can end up supplying power through that signal (which may cause
> damage.) That goes for the reset and clock.
> * devices normally want clocks running to complete their reset sequencing.
>
> This is actually the sequence which MMC/SD cards do (except without the
> reset) anyway - it's spec'd that on the MMC/SD bus, power will be applied
> and will be stable before the clock signal is applied, and then the clock
> signal runs for a certain number of cycles before you even start talking
> to the card.
It may be dangerous to refer to the spec, since we are talking
specifically about devices that require something beyond what the
spec says ;-) For instance in SD/MMC cards I'd assume the device clock
to be derived from the bus clock. However we can expect that clock
to work already (any working mmc host driver would provide that),
but we may need to drive the device clock. It still sounds reasonable
to assume that the sequencing is the same as for the bus clock.
> That all said, we do have the problem that once we decide, we need to
> support it because it becomes part of DT - this is one of the things I
> hate about DT, it requires over-design.
Yes, I agree. It is a problem that we have to face all the time.
We have in the past defined bindings of both types, overdesigned
and not thought through enough.
> Your point is "Olof's solution
> may break if we have a device which requires a different sequence" which
> is a valid point which has to be considered from the DT perspective and
> addressed whether or not we actually have a device which meets that
> criteria. I don't see an easy solution to this.
I think either one will work. With Olof's suggestion that may mean we
have to keep adding support for increasingly complex cases when we
run into them, or it may all be easy. With my suggestion, we give
more room for function drivers to mess things up, but at least we
can keep the complexity in the places that need them and only need
to change the core once.
Aside from the power-on problem, my suggestion would at the same
time solve the second problem of having a place to stick arbitrary
DT properties for the sdio function. Again looking at the cw1200
example, they may require passing an IRQ descriptor, a MAC address,
the device clock rate, and two flags for things that are not
detectable by looking at the device ID (whether a 5GHz antenna is
connected and something about odd block size transfers).
This is probably the main difference between the two approaches.
Arnd
^ permalink raw reply
* [RESEND][PATCH v4] gpio: davinci: reuse for keystone soc
From: Grygorii Strashko @ 2014-02-13 15:58 UTC (permalink / raw)
To: Linus Walleij, santosh.shilimkar-l0cyMroinI0
Cc: Alexandre Courbot,
davinci-linux-open-source-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <52FBB6BC.2060801-l0cyMroinI0@public.gmane.org>
The similar GPIO HW block is used by keystone SoCs as
in Davinci SoCs.
Hence, reuse Davinci GPIO driver for Keystone taking into
account that Keystone contains ARM GIC IRQ controller which
is implemented using IRQ Chip.
Documentation:
http://www.ti.com/lit/ug/sprugv1/sprugv1.pdf
Acked-by: Santosh Shilimkar <santosh.shilimkar-l0cyMroinI0@public.gmane.org>
Acked-by: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Signed-off-by: Grygorii Strashko <grygorii.strashko-l0cyMroinI0@public.gmane.org>
---
- rebased on top of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git
branch: devel
top commit: ef70bbe gpio: make gpiod_direction_output take a logical value
.../devicetree/bindings/gpio/gpio-davinci.txt | 4 +-
drivers/gpio/gpio-davinci.c | 48 ++++++++++++++++----
2 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/Documentation/devicetree/bindings/gpio/gpio-davinci.txt b/Documentation/devicetree/bindings/gpio/gpio-davinci.txt
index a2e839d..4ce9862 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-davinci.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-davinci.txt
@@ -1,7 +1,7 @@
-Davinci GPIO controller bindings
+Davinci/Keystone GPIO controller bindings
Required Properties:
-- compatible: should be "ti,dm6441-gpio"
+- compatible: should be "ti,dm6441-gpio", "ti,keystone-gpio"
- reg: Physical base address of the controller and the size of memory mapped
registers.
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index 7629b4f..d0f135d 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -37,6 +37,8 @@ struct davinci_gpio_regs {
u32 intstat;
};
+typedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq);
+
#define BINTEN 0x8 /* GPIO Interrupt Per-Bank Enable Register */
#define chip2controller(chip) \
@@ -413,6 +415,26 @@ static const struct irq_domain_ops davinci_gpio_irq_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
+static struct irq_chip *davinci_gpio_get_irq_chip(unsigned int irq)
+{
+ static struct irq_chip_type gpio_unbanked;
+
+ gpio_unbanked = *container_of(irq_get_chip(irq),
+ struct irq_chip_type, chip);
+
+ return &gpio_unbanked.chip;
+};
+
+static struct irq_chip *keystone_gpio_get_irq_chip(unsigned int irq)
+{
+ static struct irq_chip gpio_unbanked;
+
+ gpio_unbanked = *irq_get_chip(irq);
+ return &gpio_unbanked;
+};
+
+static const struct of_device_id davinci_gpio_ids[];
+
/*
* NOTE: for suspend/resume, probably best to make a platform_device with
* suspend_late/resume_resume calls hooking into results of the set_wake()
@@ -433,6 +455,18 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
struct davinci_gpio_platform_data *pdata = dev->platform_data;
struct davinci_gpio_regs __iomem *g;
struct irq_domain *irq_domain = NULL;
+ const struct of_device_id *match;
+ struct irq_chip *irq_chip;
+ gpio_get_irq_chip_cb_t gpio_get_irq_chip;
+
+ /*
+ * Use davinci_gpio_get_irq_chip by default to handle non DT cases
+ */
+ gpio_get_irq_chip = davinci_gpio_get_irq_chip;
+ match = of_match_device(of_match_ptr(davinci_gpio_ids),
+ dev);
+ if (match)
+ gpio_get_irq_chip = (gpio_get_irq_chip_cb_t)match->data;
ngpio = pdata->ngpio;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -489,8 +523,6 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
* IRQ mux conflicts; gpio_irq_type_unbanked() is only for GPIOs.
*/
if (pdata->gpio_unbanked) {
- static struct irq_chip_type gpio_unbanked;
-
/* pass "bank 0" GPIO IRQs to AINTC */
chips[0].chip.to_irq = gpio_to_irq_unbanked;
chips[0].gpio_irq = bank_irq;
@@ -499,10 +531,9 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
/* AINTC handles mask/unmask; GPIO handles triggering */
irq = bank_irq;
- gpio_unbanked = *container_of(irq_get_chip(irq),
- struct irq_chip_type, chip);
- gpio_unbanked.chip.name = "GPIO-AINTC";
- gpio_unbanked.chip.irq_set_type = gpio_irq_type_unbanked;
+ irq_chip = gpio_get_irq_chip(irq);
+ irq_chip->name = "GPIO-AINTC";
+ irq_chip->irq_set_type = gpio_irq_type_unbanked;
/* default trigger: both edges */
g = gpio2regs(0);
@@ -511,7 +542,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
/* set the direct IRQs up to use that irqchip */
for (gpio = 0; gpio < pdata->gpio_unbanked; gpio++, irq++) {
- irq_set_chip(irq, &gpio_unbanked.chip);
+ irq_set_chip(irq, irq_chip);
irq_set_handler_data(irq, &chips[gpio / 32]);
irq_set_status_flags(irq, IRQ_TYPE_EDGE_BOTH);
}
@@ -554,7 +585,8 @@ done:
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id davinci_gpio_ids[] = {
- { .compatible = "ti,dm6441-gpio", },
+ { .compatible = "ti,keystone-gpio", keystone_gpio_get_irq_chip},
+ { .compatible = "ti,dm6441-gpio", davinci_gpio_get_irq_chip},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, davinci_gpio_ids);
--
1.7.9.5
^ permalink raw reply related
* [PATCH] [RESEND] ARM/dts: hdmi-codec: panda/es dt entries
From: Paolo Pisati @ 2014-02-13 15:58 UTC (permalink / raw)
To: bcousson, tony; +Cc: devicetree, linux-omap
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
---
arch/arm/boot/dts/omap4-panda-common.dtsi | 9 ++++++++-
arch/arm/boot/dts/omap4-panda-es.dts | 3 ++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi
index 88c6a05..f4aeaa1 100644
--- a/arch/arm/boot/dts/omap4-panda-common.dtsi
+++ b/arch/arm/boot/dts/omap4-panda-common.dtsi
@@ -36,9 +36,15 @@
};
};
+ hdmi_audio: hdmi_audio@0 {
+ compatible = "linux,hdmi-audio";
+ status = "okay";
+ };
+
sound: sound {
compatible = "ti,abe-twl6040";
ti,model = "PandaBoard";
+ ti,audio-codec = <&hdmi_audio>;
ti,mclk-freq = <38400000>;
@@ -57,7 +63,8 @@
"HSMIC", "Headset Mic",
"Headset Mic", "Headset Mic Bias",
"AFML", "Line In",
- "AFMR", "Line In";
+ "AFMR", "Line In",
+ "HDMI Out", "TX";
};
/* HS USB Port 1 Power */
diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 816d1c9..70152d6 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -23,7 +23,8 @@
"Line Out", "AUXL",
"Line Out", "AUXR",
"AFML", "Line In",
- "AFMR", "Line In";
+ "AFMR", "Line In",
+ "HDMI Out", "TX";
};
/* PandaboardES has external pullups on SCL & SDA */
--
1.7.9.5
^ permalink raw reply related
* [PATCH] ARM/dts: hdmi-codec: panda/es dt entries
From: Paolo Pisati @ 2014-02-13 15:25 UTC (permalink / raw)
To: devicetree; +Cc: linux-omap
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
---
arch/arm/boot/dts/omap4-panda-common.dtsi | 9 ++++++++-
arch/arm/boot/dts/omap4-panda-es.dts | 3 ++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi
index 88c6a05..f4aeaa1 100644
--- a/arch/arm/boot/dts/omap4-panda-common.dtsi
+++ b/arch/arm/boot/dts/omap4-panda-common.dtsi
@@ -36,9 +36,15 @@
};
};
+ hdmi_audio: hdmi_audio@0 {
+ compatible = "linux,hdmi-audio";
+ status = "okay";
+ };
+
sound: sound {
compatible = "ti,abe-twl6040";
ti,model = "PandaBoard";
+ ti,audio-codec = <&hdmi_audio>;
ti,mclk-freq = <38400000>;
@@ -57,7 +63,8 @@
"HSMIC", "Headset Mic",
"Headset Mic", "Headset Mic Bias",
"AFML", "Line In",
- "AFMR", "Line In";
+ "AFMR", "Line In",
+ "HDMI Out", "TX";
};
/* HS USB Port 1 Power */
diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 816d1c9..70152d6 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -23,7 +23,8 @@
"Line Out", "AUXL",
"Line Out", "AUXR",
"AFML", "Line In",
- "AFMR", "Line In";
+ "AFMR", "Line In",
+ "HDMI Out", "TX";
};
/* PandaboardES has external pullups on SCL & SDA */
--
1.7.9.5
^ permalink raw reply related
* Good Day!
From: Mark Reyes Guus @ 2014-02-13 14:56 UTC (permalink / raw)
Good day. I am Mark Reyes Guus, I work with Abn Amro Bank as an
auditor. I have a proposition to discuss with you. Should you be
interested, please e-mail back to me.
Private Email: markreyesguus-cUNmAtK3PYUqdlJmJB21zg@public.gmane.org OR markguus.reyes01-/k+kKI0dE6M@public.gmane.org
Yours Sincerely,
Mark Reyes Guus.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 10/14] Documentation: mfd: s2mps11: Document support for S2MPS14
From: Tomasz Figa @ 2014-02-13 14:55 UTC (permalink / raw)
To: Krzysztof Kozlowski, Sangbeom Kim, Samuel Ortiz, Lee Jones,
linux-kernel, linux-samsung-soc
Cc: Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
Mark Brown, Liam Girdwood, devicetree, Rob Herring, Pawel Moll,
Mark Rutland, Ian Campbell, Kumar Gala
In-Reply-To: <1392282847-25444-11-git-send-email-k.kozlowski@samsung.com>
Hi,
On 13.02.2014 10:14, Krzysztof Kozlowski wrote:
> Add bindings documentation for S2MPS14 device to the s2mps11 driver.
>
> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
> Cc: Mark Brown <broonie@kernel.org>
> Cc: Liam Girdwood <lgirdwood@gmail.com>
> Cc: Tomasz Figa <t.figa@samsung.com>
> Cc: devicetree@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>
> ---
> Documentation/devicetree/bindings/mfd/s2mps11.txt | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/mfd/s2mps11.txt b/Documentation/devicetree/bindings/mfd/s2mps11.txt
> index 15ee89c3cc7b..f69bec294f02 100644
> --- a/Documentation/devicetree/bindings/mfd/s2mps11.txt
> +++ b/Documentation/devicetree/bindings/mfd/s2mps11.txt
> @@ -1,5 +1,5 @@
>
> -* Samsung S2MPS11 Voltage and Current Regulator
> +* Samsung S2MPS11 and S2MPS14 Voltage and Current Regulator
>
> The Samsung S2MPS11 is a multi-function device which includes voltage and
> current regulators, RTC, charger controller and other sub-blocks. It is
> @@ -7,7 +7,7 @@ interfaced to the host controller using an I2C interface. Each sub-block is
> addressed by the host system using different I2C slave addresses.
>
> Required properties:
> -- compatible: Should be "samsung,s2mps11-pmic".
> +- compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps14-pmic".
> - reg: Specifies the I2C slave address of the pmic block. It should be 0x66.
>
> Optional properties:
> @@ -59,10 +59,14 @@ supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number
> as per the datasheet of s2mps11.
>
> - LDOn
> - - valid values for n are 1 to 38
> + - valid values for n are:
> + - S2MPS11: 1 to 38
> + - S2MPS14: 1 to 25
> - Example: LDO1, LD02, LDO28
> - BUCKn
> - - valid values for n are 1 to 10.
> + - valid values for n are:
> + - S2MPS11: 1 to 10
> + - S2MPS14: 1 to 5
> - Example: BUCK1, BUCK2, BUCK9
>
> Example:
>
Acked-by: Tomasz Figa <t.figa@samsung.com>
Best regards,
Tomasz
^ permalink raw reply
* Re: [PATCH 1/3] mmc: add support for power-on sequencing through DT
From: Russell King - ARM Linux @ 2014-02-13 14:41 UTC (permalink / raw)
To: Arnd Bergmann
Cc: linux-arm-kernel, mark.rutland, devicetree, Ulf Hansson,
Pawel Moll, Ian Campbell, Tomasz Figa, linux-mmc, Tomasz Figa,
Chris Ball, robh+dt, Kumar Gala, Olof Johansson, Fabio Estevam,
Sascha Hauer
In-Reply-To: <2874395.Ul86gYA57i@wuerfel>
On Thu, Feb 13, 2014 at 01:48:55PM +0100, Arnd Bergmann wrote:
> On Thursday 13 February 2014 10:42:48 Russell King - ARM Linux wrote:
> >
> > What if we have a platform where things subtly change, like for instance,
> > the wiring on the SD slot to fix a problem with UHS-1 cards, which means
> > you don't have UHS-1 support for some platforms but do for others.
> >
> > What if you have a platform which uses a brcm4329 chip for Wifi, but then
> > later in the production run switch to using a different Wifi chipset?
>
> As far as I can tell, the power sequencing is normally really
> dependent on the device. If someone has an on-board brcm4329
> that requires a specific set of clocks, resets, voltages etc
> to be routed to the chip and enabled in the correct order to
> allow probing, it seems unlikely that changing the wifi chipset
> to something else would keep the exact same requirements.
That's your assertion - however, do we /know/ whether there's a situation
where Olof's solution doesn't work because the sequencing is wrong?
I see nothing unreasonable about the sequence:
1. hold reset at low level
2. apply power
3. turn clock on
4. apply reset
5. release reset
The points being:
* never set a signal to a high level before power is applied, otherwise
we can end up supplying power through that signal (which may cause
damage.) That goes for the reset and clock.
* devices normally want clocks running to complete their reset sequencing.
This is actually the sequence which MMC/SD cards do (except without the
reset) anyway - it's spec'd that on the MMC/SD bus, power will be applied
and will be stable before the clock signal is applied, and then the clock
signal runs for a certain number of cycles before you even start talking
to the card.
That all said, we do have the problem that once we decide, we need to
support it because it becomes part of DT - this is one of the things I
hate about DT, it requires over-design. Your point is "Olof's solution
may break if we have a device which requires a different sequence" which
is a valid point which has to be considered from the DT perspective and
addressed whether or not we actually have a device which meets that
criteria. I don't see an easy solution to this.
--
FTTC broadband for 0.8mile line: 5.8Mbps down 500kbps up. Estimation
in database were 13.1 to 19Mbit for a good line, about 7.5+ for a bad.
Estimate before purchase was "up to 13.2Mbit".
^ permalink raw reply
* [PATCH v2 7/7] ARM: dts: imx6sl: Add power-domain information to gpc node
From: Philipp Zabel @ 2014-02-13 14:39 UTC (permalink / raw)
To: Shawn Guo
Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392302350-11729-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
The PGC that is part of GPC controls isolation and power sequencing of the
power domains. The PU power domain will be handled by the generic pm domain
framework and needs a phandle to the PU regulator to turn off power when
the domain is disabled.
Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
arch/arm/boot/dts/imx6sl.dtsi | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi
index 28558f1..774e1fb 100644
--- a/arch/arm/boot/dts/imx6sl.dtsi
+++ b/arch/arm/boot/dts/imx6sl.dtsi
@@ -529,9 +529,27 @@
};
gpc: gpc@020dc000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
compatible = "fsl,imx6sl-gpc", "fsl,imx6q-gpc";
reg = <0x020dc000 0x4000>;
interrupts = <0 89 0x04>;
+ pu-supply = <®_pu>;
+
+ pd_display: display-power-domain@020dc240 {
+ compatible = "fsl,imx6q-power-domain";
+ reg = <0x020dc240 0x10>;
+ };
+
+ pd_pu: pu-power-domain@020dc260 {
+ compatible = "fsl,imx6q-power-domain";
+ reg = <0x020dc260 0x10>;
+ };
+
+ pd_arm: cpu-power-domain@020dc2a0 {
+ compatible = "fsl,imx6q-power-domain";
+ reg = <0x020dc2a0 0x10>;
+ };
};
gpr: iomuxc-gpr@020e0000 {
--
1.8.5.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 6/7] ARM: dts: imx6qdl: Add power-domain information to gpc node
From: Philipp Zabel @ 2014-02-13 14:39 UTC (permalink / raw)
To: Shawn Guo
Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392302350-11729-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
The PGC that is part of GPC controls isolation and power sequencing of the
power domains. The PU power domain will be handled by the generic pm domain
framework and needs a phandle to the PU regulator to turn off power when
the domain is disabled.
Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
Changes since v1:
- Renamed power-domain to pu-power-domain and added
cpu-power-domain
---
arch/arm/boot/dts/imx6qdl.dtsi | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 253d82c..fd1be55 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -598,9 +598,22 @@
};
gpc: gpc@020dc000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
compatible = "fsl,imx6q-gpc";
reg = <0x020dc000 0x4000>;
interrupts = <0 89 0x04 0 90 0x04>;
+ pu-supply = <®_pu>;
+
+ pd_pu: pu-power-domain@020dc260 {
+ compatible = "fsl,imx6q-power-domain";
+ reg = <0x020dc260 0x10>;
+ };
+
+ pd_arm: cpu-power-domain@020dc2a0 {
+ compatible = "fsl,imx6q-power-domain";
+ reg = <0x020dc2a0 0x10>;
+ };
};
gpr: iomuxc-gpr@020e0000 {
--
1.8.5.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 5/7] ARM: dts: imx6qdl: Allow disabling the PU regulator, add a enable ramp delay
From: Philipp Zabel @ 2014-02-13 14:39 UTC (permalink / raw)
To: Shawn Guo
Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392302350-11729-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
The PU regulator is enabled during boot, but not necessarily always-on.
It can be disabled by the generic pm domain framework when the PU power
domain is shut down. The ramp delay of 150 us might be a bit conservative,
the value is taken from the Freescale kernel.
Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
arch/arm/boot/dts/imx6qdl.dtsi | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index fb28b2e..253d82c 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -515,7 +515,8 @@
regulator-name = "vddpu";
regulator-min-microvolt = <725000>;
regulator-max-microvolt = <1450000>;
- regulator-always-on;
+ regulator-enable-ramp-delay = <150>;
+ regulator-boot-on;
anatop-reg-offset = <0x140>;
anatop-vol-bit-shift = <9>;
anatop-vol-bit-width = <5>;
--
1.8.5.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 4/7] ARM: imx6: gpc: Add observed worst case latencies
From: Philipp Zabel @ 2014-02-13 14:39 UTC (permalink / raw)
To: Shawn Guo
Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392302350-11729-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
This avoids the "... latency exceeded, new value ..." warnings
emitted by the power domain framework code whenever the PU domain
is enabled or disabled.
Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
arch/arm/mach-imx/gpc.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 607a6ea..cbac099 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -225,6 +225,8 @@ static struct generic_pm_domain imx6q_pu_domain = {
.name = "PU",
.power_off = imx6q_pm_pu_power_off,
.power_on = imx6q_pm_pu_power_on,
+ .power_off_latency_ns = 25000,
+ .power_on_latency_ns = 2000000,
};
int imx6q_pm_clk_add(struct device *dev)
@@ -289,6 +291,13 @@ int imx6q_pm_clk_remove(struct device *dev)
return 0;
}
+static struct gpd_timing_data pu_timing_data = {
+ .stop_latency_ns = 2000,
+ .start_latency_ns = 2000,
+ .save_state_latency_ns = 5000,
+ .restore_state_latency_ns = 20000000, /* VPU firmware reload */
+};
+
static int imx6q_pm_notifier_call(struct notifier_block *nb,
unsigned long event, void *data)
{
@@ -303,7 +312,7 @@ static int imx6q_pm_notifier_call(struct notifier_block *nb,
if (!np || np != imx6q_pu_domain.of_node)
return NOTIFY_DONE;
- ret = pm_genpd_of_add_device(np, dev);
+ ret = __pm_genpd_of_add_device(np, dev, &pu_timing_data);
if (ret)
dev_err(dev, "failed to add to power domain: %d\n",
ret);
--
1.8.5.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 3/7] ARM: imx6: gpc: Add pm clock support to PU power domain
From: Philipp Zabel @ 2014-02-13 14:39 UTC (permalink / raw)
To: Shawn Guo
Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392302350-11729-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Drivers still handle clocks themselves, we only enable pm clocks of the
GPU and VPU devices in the PU power domain temporarily during powerup
so that the reset machinery can work.
Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
arch/arm/mach-imx/gpc.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 74 insertions(+)
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 1dc4301..607a6ea 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -18,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
#include <linux/pm_domain.h>
#include <linux/regulator/consumer.h>
#include <linux/irqchip/arm-gic.h>
@@ -182,6 +183,7 @@ static int imx6q_pm_pu_power_off(struct generic_pm_domain *genpd)
static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
{
+ struct pm_domain_data *pdd;
int ret;
u32 val;
int sw, sw2iso;
@@ -192,6 +194,10 @@ static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
return ret;
}
+ /* Enable PM clocks for all devices in the PU domain */
+ list_for_each_entry(pdd, &genpd->dev_list, list_node)
+ pm_clk_resume(pdd->dev);
+
/* Gate off PU domain when GPU/VPU when powered down */
writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);
@@ -208,6 +214,10 @@ static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
/* Wait ISO + ISO2SW IPG clock cycles */
ndelay((sw + sw2iso) * 1000 / 66);
+ /* Disable PM clocks for all devices in the PU domain */
+ list_for_each_entry(pdd, &genpd->dev_list, list_node)
+ pm_clk_suspend(pdd->dev);
+
return 0;
}
@@ -217,6 +227,68 @@ static struct generic_pm_domain imx6q_pu_domain = {
.power_on = imx6q_pm_pu_power_on,
};
+int imx6q_pm_clk_add(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ const char *con_id;
+ struct clk *clk;
+ int i = 0;
+
+ /* Add and prepare named clocks */
+ while (!of_property_read_string_index(np, "clock-names", i, &con_id)) {
+ pm_clk_add(dev, con_id);
+ clk = of_clk_get(np, i);
+ if (!IS_ERR(clk)) {
+ clk_prepare(clk);
+ clk_put(clk);
+ }
+ i++;
+ }
+
+ /* If no named clocks are given, add and prepare unnamed clock */
+ if (i == 1 && of_find_property(dev->of_node, "clocks", NULL)) {
+ pm_clk_add(dev, NULL);
+ clk = of_clk_get(np, 0);
+ if (!IS_ERR(clk)) {
+ clk_prepare(clk);
+ clk_put(clk);
+ }
+ }
+
+ return 0;
+}
+
+int imx6q_pm_clk_remove(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ const char *con_id;
+ struct clk *clk;
+ int i = 0;
+
+ /* Remove and unprepare named clocks */
+ while (!of_property_read_string_index(np, "clock-names", i, &con_id)) {
+ pm_clk_remove(dev, con_id);
+ clk = of_clk_get(np, i);
+ if (!IS_ERR(clk)) {
+ clk_unprepare(clk);
+ clk_put(clk);
+ }
+ i++;
+ }
+
+ /* If no named clocks are given, remove and unprepare unnamed clock */
+ if (i == 1 && of_find_property(dev->of_node, "clocks", NULL)) {
+ pm_clk_remove(dev, NULL);
+ clk = of_clk_get(np, 0);
+ if (!IS_ERR(clk)) {
+ clk_unprepare(clk);
+ clk_put(clk);
+ }
+ }
+
+ return 0;
+}
+
static int imx6q_pm_notifier_call(struct notifier_block *nb,
unsigned long event, void *data)
{
@@ -235,6 +307,7 @@ static int imx6q_pm_notifier_call(struct notifier_block *nb,
if (ret)
dev_err(dev, "failed to add to power domain: %d\n",
ret);
+ imx6q_pm_clk_add(dev);
break;
case BUS_NOTIFY_UNBOUND_DRIVER:
genpd = dev_to_genpd(dev);
@@ -245,6 +318,7 @@ static int imx6q_pm_notifier_call(struct notifier_block *nb,
if (ret)
dev_err(dev, "failed to remove from power domain: %d\n",
ret);
+ imx6q_pm_clk_remove(dev);
break;
}
--
1.8.5.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 2/7] ARM: imx6: gpc: Add PU power domain for GPU/VPU
From: Philipp Zabel @ 2014-02-13 14:39 UTC (permalink / raw)
To: Shawn Guo
Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392302350-11729-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
When generic pm domain support is enabled, the PGC can be used
to completely gate power to the PU power domain containing GPU3D,
GPU2D, and VPU cores.
This code triggers the PGC powerdown sequence to disable the GPU/VPU
isolation cells and gate power and then disables the PU regulator.
To reenable, the reverse powerup sequence is triggered after the PU
regulaotor is enabled again.
Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
Changes since v1:
- Removed superfluous comment
- Link PU domain with its DT node, check for PU domain in bus notifier
- Turn off the PU power domain on boot
---
arch/arm/mach-imx/Kconfig | 2 +
arch/arm/mach-imx/gpc.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 172 insertions(+)
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 33567aa..3c58f2e 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -808,6 +808,7 @@ config SOC_IMX6Q
select PL310_ERRATA_727915 if CACHE_PL310
select PL310_ERRATA_769419 if CACHE_PL310
select PM_OPP if PM
+ select PM_GENERIC_DOMAINS if PM
help
This enables support for Freescale i.MX6 Quad processor.
@@ -827,6 +828,7 @@ config SOC_IMX6SL
select PL310_ERRATA_588369 if CACHE_PL310
select PL310_ERRATA_727915 if CACHE_PL310
select PL310_ERRATA_769419 if CACHE_PL310
+ select PM_GENERIC_DOMAINS if PM
help
This enables support for Freescale i.MX6 SoloLite processor.
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 586e017..1dc4301 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -10,19 +10,32 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regulator/consumer.h>
#include <linux/irqchip/arm-gic.h>
#include "common.h"
+#include "hardware.h"
+#define GPC_CNTR 0x000
#define GPC_IMR1 0x008
+#define GPC_PGC_GPU_PDN 0x260
+#define GPC_PGC_GPU_PUPSCR 0x264
+#define GPC_PGC_GPU_PDNSCR 0x268
#define GPC_PGC_CPU_PDN 0x2a0
#define IMR_NUM 4
+#define GPU_VPU_PUP_REQ BIT(1)
+#define GPU_VPU_PDN_REQ BIT(0)
+
static void __iomem *gpc_base;
static u32 gpc_wake_irqs[IMR_NUM];
static u32 gpc_saved_imrs[IMR_NUM];
@@ -138,3 +151,160 @@ void __init imx_gpc_init(void)
gic_arch_extn.irq_unmask = imx_gpc_irq_unmask;
gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake;
}
+
+static struct regulator *pu_reg;
+
+static int imx6q_pm_pu_power_off(struct generic_pm_domain *genpd)
+{
+ u32 val;
+ int iso, iso2sw;
+
+ /* Read ISO and ISO2SW power down delays */
+ val = readl_relaxed(gpc_base + GPC_PGC_GPU_PDNSCR);
+ iso = val & 0x3f;
+ iso2sw = (val >> 8) & 0x3f;
+
+ /* Gate off PU domain when GPU/VPU when powered down */
+ writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);
+
+ /* Request GPC to power down GPU/VPU */
+ val = readl_relaxed(gpc_base + GPC_CNTR);
+ val |= GPU_VPU_PDN_REQ;
+ writel_relaxed(val, gpc_base + GPC_CNTR);
+
+ /* Wait ISO + ISO2SW IPG clock cycles */
+ ndelay((iso + iso2sw) * 1000 / 66);
+
+ regulator_disable(pu_reg);
+
+ return 0;
+}
+
+static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
+{
+ int ret;
+ u32 val;
+ int sw, sw2iso;
+
+ ret = regulator_enable(pu_reg);
+ if (ret) {
+ pr_err("%s: failed to enable regulator: %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* Gate off PU domain when GPU/VPU when powered down */
+ writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);
+
+ /* Read ISO and ISO2SW power down delays */
+ val = readl_relaxed(gpc_base + GPC_PGC_GPU_PUPSCR);
+ sw = val & 0x3f;
+ sw2iso = (val >> 8) & 0x3f;
+
+ /* Request GPC to power up GPU/VPU */
+ val = readl_relaxed(gpc_base + GPC_CNTR);
+ val |= GPU_VPU_PUP_REQ;
+ writel_relaxed(val, gpc_base + GPC_CNTR);
+
+ /* Wait ISO + ISO2SW IPG clock cycles */
+ ndelay((sw + sw2iso) * 1000 / 66);
+
+ return 0;
+}
+
+static struct generic_pm_domain imx6q_pu_domain = {
+ .name = "PU",
+ .power_off = imx6q_pm_pu_power_off,
+ .power_on = imx6q_pm_pu_power_on,
+};
+
+static int imx6q_pm_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct generic_pm_domain *genpd;
+ struct device *dev = data;
+ struct device_node *np;
+ int ret;
+
+ switch (event) {
+ case BUS_NOTIFY_BIND_DRIVER:
+ np = of_parse_phandle(dev->of_node, "power-domain", 0);
+ if (!np || np != imx6q_pu_domain.of_node)
+ return NOTIFY_DONE;
+
+ ret = pm_genpd_of_add_device(np, dev);
+ if (ret)
+ dev_err(dev, "failed to add to power domain: %d\n",
+ ret);
+ break;
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd) || genpd != &imx6q_pu_domain)
+ return NOTIFY_DONE;
+
+ ret = pm_genpd_remove_device(genpd, dev);
+ if (ret)
+ dev_err(dev, "failed to remove from power domain: %d\n",
+ ret);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block imx6q_platform_nb = {
+ .notifier_call = imx6q_pm_notifier_call,
+};
+
+static int imx_gpc_probe(struct platform_device *pdev)
+{
+ struct device_node *np;
+ int ret;
+
+ np = of_get_child_by_name(pdev->dev.of_node, "pu-power-domain");
+ if (!np) {
+ dev_err(&pdev->dev, "missing pu-power-domain node\n");
+ return -EINVAL;
+ }
+ imx6q_pu_domain.of_node = np;
+
+ pu_reg = devm_regulator_get(&pdev->dev, "pu");
+ if (IS_ERR(pu_reg)) {
+ ret = PTR_ERR(pu_reg);
+ dev_err(&pdev->dev, "failed to get pu regulator: %d\n", ret);
+ return ret;
+ }
+
+ /* The regulator is initially enabled */
+ ret = regulator_enable(pu_reg);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable pu regulator: %d\n", ret);
+ return ret;
+ }
+
+ imx6q_pu_domain.of_node = np;
+ imx6q_pm_pu_power_off(&imx6q_pu_domain);
+ pm_genpd_init(&imx6q_pu_domain, NULL, true);
+ bus_register_notifier(&platform_bus_type, &imx6q_platform_nb);
+
+ return 0;
+}
+
+static struct of_device_id imx_gpc_dt_ids[] = {
+ { .compatible = "fsl,imx6q-gpc" },
+ { }
+};
+
+static struct platform_driver imx_gpc_driver = {
+ .driver = {
+ .name = "imx-gpc",
+ .owner = THIS_MODULE,
+ .of_match_table = imx_gpc_dt_ids,
+ },
+ .probe = imx_gpc_probe,
+};
+
+static int __init imx_pgc_init(void)
+{
+ return platform_driver_register(&imx_gpc_driver);
+}
+subsys_initcall(imx_pgc_init);
--
1.8.5.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 1/7] Documentation: Add device tree bindings for Freescale i.MX GPC
From: Philipp Zabel @ 2014-02-13 14:39 UTC (permalink / raw)
To: Shawn Guo
Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392302350-11729-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
The i.MX6 contains a power controller that controls power gating and
sequencing for the SoC's power domains.
Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
.../devicetree/bindings/power/fsl,imx-gpc.txt | 61 ++++++++++++++++++++++
1 file changed, 61 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/fsl,imx-gpc.txt
diff --git a/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt b/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt
new file mode 100644
index 0000000..3ec8c0e
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt
@@ -0,0 +1,61 @@
+Freescale i.MX General Power Controller
+=======================================
+
+The i.MX6Q General Power Control (GPC) block contains DVFS load tracking
+counters and Power Gating Control (PGC) for the CPU and PU (GPU/VPU) power
+domains.
+
+Required properties:
+- compatible: Should be "fsl,imx6q-gpc"
+- reg: should be register base and length as documented in the
+ datasheet
+- interrupts: Should contain GPC interrupt request 1
+- pu-supply: Link to the LDO regulator powering the PU power domain
+- #address-cells, #size-cells: Should be <1>
+
+The gpc node should contain 'power-domain' subnodes for each power domain.
+These serve as phandle targets for devices belonging to the power domain:
+
+Power domains controlled by a PGC register set
+==============================================
+
+Required properties:
+- compatible: Should be "fsl,imx6q-power-domain"
+- reg: should be register base and length as documented in the
+ datasheet
+
+Specifying power domain for IP modules
+======================================
+
+IP cores belonging to a power domain should contain a 'power-domain' property
+that is a phandle pointing to the power-domain subnode of the gpc device node.
+
+Required properties:
+- power-domain: A phandle pointing to the power-domain device tree node
+
+
+Example:
+
+ gpc: gpc@020dc000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,imx6q-gpc";
+ reg = <0x020dc000 0x4000>;
+ interrupts = <0 89 0x04 0 90 0x04>;
+ pu-supply = <®_pu>;
+
+ pd_pu: power-domain@020dc260 {
+ compatible = "fsl,imx6q-power-domain";
+ reg = <0x020dc260 0x10>;
+ };
+ };
+
+Example of a device that is part of a power domain:
+
+ vpu: vpu@02040000 {
+ reg = <0x02040000 0x3c000>;
+ /* ... */
+ power-domain = <&pd_pu>;
+ /* ... */
+ };
+
--
1.8.5.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 0/7] i.MX6 PU power domain support
From: Philipp Zabel @ 2014-02-13 14:39 UTC (permalink / raw)
To: Shawn Guo
Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
The i.MX6Q can gate off the CPU and PU (GPU/VPU) power domains using the
Power Gating Controller (PGC) in the GPC register space. The CPU power
domain is already handled by wait state code, but the PU power domain can
be controlled using the generic power domain framework and power off the PU
supply regulator if all devices in the power domain are (runtime) suspended.
This patchset adds a GPC platform device initialized at subsys_initcall time
(after anatop regulators) that binds to the gpc device tree node and sets up
the PU power domain:
gpc: gpc@020dc000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "fsl,imx6q-gpc";
reg = <0x020dc000 0x4000>;
interrupts = <0 89 0x04 0 90 0x04>;
pu-supply = <®_pu>;
pd_pu: pu-power-domain@020dc260 {
compatible = "fsl,imx6q-power-domain";
reg = <0x020dc260 0x10>;
};
pd_arm: cpu-power-domain@020dc2a0 {
compatible = "fsl,imx6q-power-domain";
reg = <0x020dc2a0 0x10>;
};
};
The cpu-power-domain node is included for completeness' sake, it is not
currently used by the code.
It registers a platform bus notifier so that it can add GPU and VPU devices
to the power domain when they are bound. If finds devices to be added to the
power domain by scanning the device tree for nodes that contain a
power-domain = <&pd_pu>;
property.
For i.MX6QDL there is only one power domain that can be disabled at runtime,
on i.MX6SL there is an additional DISPLAY power domain, which is not yet
handled by the code.
Changes since v1:
- Added device tree binding documentation
- Removed a superfluous comment in gpc.c
- Changed the power domain compatible to be less generic
- Link PU domain with its DT node, check for PU domain in bus notifier
- Turn off the PU power domain on boot
- Renamed power-domain node to pu-power-domain and added cpu-power-domain
for i.MX6QDL
- Added dts patch for i.MX6SL
regards
Philipp
Philipp Zabel (7):
Documentation: Add device tree bindings for Freescale i.MX GPC
ARM: imx6: gpc: Add PU power domain for GPU/VPU
ARM: imx6: gpc: Add pm clock support to PU power domain
ARM: imx6: gpc: Add observed worst case latencies
ARM: dts: imx6qdl: Allow disabling the PU regulator, add a enable ramp
delay
ARM: dts: imx6qdl: Add power-domain information to gpc node
ARM: dts: imx6sl: Add power-domain information to gpc node
.../devicetree/bindings/power/fsl,imx-gpc.txt | 61 +++++
arch/arm/boot/dts/imx6qdl.dtsi | 16 +-
arch/arm/boot/dts/imx6sl.dtsi | 18 ++
arch/arm/mach-imx/Kconfig | 2 +
arch/arm/mach-imx/gpc.c | 253 +++++++++++++++++++++
5 files changed, 349 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/power/fsl,imx-gpc.txt
--
1.8.5.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v3 3/3] net:phy:dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
This patch adds configuration of the periodic output and external timestamp
pins available on the dp83640 family of PHYs. It also configures the
master/slave relationship in a group of PHYs on the same MDIO bus and the pins
used for clock calibration in the group.
The configuration is retrieved from DT through the properties
dp83640,slave
dp83640,calibrate-pin
dp83640,perout-pins
dp83640,extts-pins
The configuration module parameters are retained as fallback for the non-DT
case.
Since the pin configuration is now stored for each clock device, groups of
devices on different mdio busses can now have different pin configurations.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
Documentation/devicetree/bindings/net/dp83640.txt | 29 +++++
drivers/net/phy/dp83640.c | 139 ++++++++++++++++++----
2 files changed, 143 insertions(+), 25 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/dp83640.txt
diff --git a/Documentation/devicetree/bindings/net/dp83640.txt b/Documentation/devicetree/bindings/net/dp83640.txt
new file mode 100644
index 0000000..b9a57c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dp83640.txt
@@ -0,0 +1,29 @@
+Required properties for the National DP83640 ethernet phy:
+
+- compatible : Must contain "national,dp83640"
+
+Optional properties:
+
+- dp83640,slave: If present, this phy will be slave to another dp83640
+ on the same mdio bus.
+- dp83640,perout-pins : List of the pin pins used for periodic output
+ triggers.
+- dp83640,extts-pins : List of the pin pins used for external event
+ timestamping.
+- dp83640,calibrate-pin : The pin used for master/slave calibration.
+
+Example:
+
+ ethernet-phy@1 {
+ compatible = "national,dp83640", "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ dp83640,perout-pins = <2>;
+ dp83640,extts-pins = <3 4 8 9 10 11>;
+ dp83640,calibrate-pin = <1>;
+ };
+
+ ethernet-phy@2 {
+ compatible = "national,dp83640", "ethernet-phy-ieee802.3-c22";
+ reg = <2>;
+ dp83640,slave;
+ };
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 28a6e1d..077bdc2 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -30,6 +30,7 @@
#include <linux/phy.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
+#include <linux/of_device.h>
#include "dp83640_reg.h"
@@ -120,6 +121,8 @@ struct dp83640_private {
/* queues of incoming and outgoing packets */
struct sk_buff_head rx_queue;
struct sk_buff_head tx_queue;
+ /* is this phyter a slave */
+ bool slave;
};
struct dp83640_clock {
@@ -141,6 +144,7 @@ struct dp83640_clock {
struct list_head phylist;
/* reference to our PTP hardware clock */
struct ptp_clock *ptp_clock;
+ u32 perout_pins[N_EXT], extts_pins[N_EXT], calibrate_pin;
};
@@ -264,7 +268,7 @@ static void periodic_output(struct dp83640_clock *clock,
u32 sec, nsec, period;
u16 gpio, ptp_trig, val;
- gpio = on ? perout_pins[trigger] : 0;
+ gpio = on ? clock->perout_pins[trigger] : 0;
ptp_trig = TRIG_WR |
(trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
@@ -435,12 +439,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
index = rq->extts.index;
- if (index < 0 || index >= n_ext_ts)
+ if (index < 0 || index >= clock->caps.n_ext_ts)
return -EINVAL;
event_num = index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- gpio_num = extts_pins[index];
+ gpio_num = clock->extts_pins[index];
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
if (rq->extts.flags & PTP_FALLING_EDGE)
evnt |= EVNT_FALL;
@@ -452,7 +456,7 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
case PTP_CLK_REQ_PEROUT:
index = rq->perout.index;
- if (index < 0 || index >= n_per_out)
+ if (index < 0 || index >= clock->caps.n_per_out)
return -EINVAL;
periodic_output(clock, rq, index, on);
return 0;
@@ -573,7 +577,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE;
evnt |= (trigger & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
- evnt |= (calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+ evnt |= (clock->calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
list_for_each(this, &clock->phylist) {
tmp = list_entry(this, struct dp83640_private, list);
@@ -586,7 +590,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE;
ptp_trig |= (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT;
- ptp_trig |= (calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
+ ptp_trig |= (clock->calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
ext_write(0, master, PAGE5, PTP_TRIG, ptp_trig);
/* load trigger */
@@ -691,7 +695,7 @@ static int decode_evnt(struct dp83640_private *dp83640,
event.type = PTP_CLOCK_EXTTS;
event.timestamp = phy2txts(&dp83640->edata);
- for (i = 0; i < n_ext_ts; i++) {
+ for (i = 0; i < dp83640->clock->caps.n_ext_ts; i++) {
if (ext_status & exts_chan_to_edata(i)) {
event.index = i;
ptp_clock_event(dp83640->clock->ptp_clock, &event);
@@ -893,12 +897,11 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
mutex_init(&clock->extreg_lock);
mutex_init(&clock->clock_lock);
INIT_LIST_HEAD(&clock->phylist);
+ clock->calibrate_pin = -1;
clock->caps.owner = THIS_MODULE;
sprintf(clock->caps.name, "dp83640 timer");
clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0;
- clock->caps.n_ext_ts = n_ext_ts;
- clock->caps.n_per_out = n_per_out;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
@@ -911,18 +914,6 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
get_device(&bus->dev);
}
-static int choose_this_phy(struct dp83640_clock *clock,
- struct phy_device *phydev)
-{
- if (chosen_phy == -1 && !clock->chosen)
- return 1;
-
- if (chosen_phy == phydev->addr)
- return 1;
-
- return 0;
-}
-
static struct dp83640_clock *dp83640_clock_get(struct dp83640_clock *clock)
{
if (clock)
@@ -968,6 +959,86 @@ static void dp83640_clock_put(struct dp83640_clock *clock)
mutex_unlock(&clock->clock_lock);
}
+#ifdef CONFIG_OF
+static int dp83640_probe_dt(struct device_node *node,
+ struct dp83640_private *dp83640)
+{
+ struct dp83640_clock *clock = dp83640->clock;
+ struct property *prop;
+ int err, proplen, n_cal = 0;
+
+ dp83640->slave = of_property_read_bool(node, "dp83640,slave");
+ if (!dp83640->slave && clock->chosen) {
+ pr_err("More then one dp83640 master on the same bus");
+ return -EINVAL;
+ }
+
+ prop = of_find_property(node, "dp83640,calibrate-pin", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have calibrate pin");
+ return -EINVAL;
+ }
+ of_property_read_u32(node, "dp83640,calibrate-pin",
+ &clock->calibrate_pin);
+ n_cal = 1;
+ }
+
+ prop = of_find_property(node, "dp83640,perout-pins", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have perout pins");
+ return -EINVAL;
+ }
+
+ clock->caps.n_per_out = proplen / sizeof(u32);
+ if (clock->caps.n_per_out + n_cal > N_EXT) {
+ pr_err("Too many dp83640,perout-pins");
+ return -EINVAL;
+ }
+ err = of_property_read_u32_array(node, "dp83640,perout-pins",
+ clock->perout_pins,
+ clock->caps.n_per_out);
+ if (err < 0)
+ return err;
+ }
+
+ prop = of_find_property(node, "dp83640,extts-pins", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have extts pins");
+ return -EINVAL;
+ }
+
+ clock->caps.n_ext_ts = proplen / sizeof(u32);
+ if (clock->caps.n_ext_ts + n_cal > N_EXT) {
+ pr_err("Too many dp83640,extts-pins");
+ return -EINVAL;
+ }
+ err = of_property_read_u32_array(node, "dp83640,extts-pins",
+ clock->extts_pins,
+ clock->caps.n_ext_ts);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id dp83640_of_match_table[] = {
+ { .compatible = "national,dp83640", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, dp83640_of_match_table);
+#else
+
+static inline int dp83640_probe_dt(struct device_node *node,
+ struct dp83640_private *dp83640)
+{
+ return 0;
+}
+#endif
+
static int dp83640_probe(struct phy_device *phydev)
{
struct dp83640_clock *clock;
@@ -985,7 +1056,24 @@ static int dp83640_probe(struct phy_device *phydev)
if (!dp83640)
goto no_memory;
+ dp83640->clock = clock;
dp83640->phydev = phydev;
+
+ if (phydev->dev.of_node) {
+ err = dp83640_probe_dt(phydev->dev.of_node, dp83640);
+ if (err)
+ return err;
+ } else {
+ clock->calibrate_pin = calibrate_pin;
+ memcpy(clock->perout_pins, perout_pins,
+ sizeof(clock->perout_pins));
+ memcpy(clock->extts_pins, extts_pins,
+ sizeof(clock->extts_pins));
+ if (clock->chosen ||
+ (chosen_phy != -1 && phydev->addr != chosen_phy))
+ dp83640->slave = true;
+ }
+
INIT_WORK(&dp83640->ts_work, rx_timestamp_work);
INIT_LIST_HEAD(&dp83640->rxts);
@@ -999,9 +1087,7 @@ static int dp83640_probe(struct phy_device *phydev)
skb_queue_head_init(&dp83640->rx_queue);
skb_queue_head_init(&dp83640->tx_queue);
- dp83640->clock = clock;
-
- if (choose_this_phy(clock, phydev)) {
+ if (!dp83640->slave) {
clock->chosen = dp83640;
clock->ptp_clock = ptp_clock_register(&clock->caps, &phydev->dev);
if (IS_ERR(clock->ptp_clock)) {
@@ -1353,7 +1439,10 @@ static struct phy_driver dp83640_driver = {
.hwtstamp = dp83640_hwtstamp,
.rxtstamp = dp83640_rxtstamp,
.txtstamp = dp83640_txtstamp,
- .driver = {.owner = THIS_MODULE,}
+ .driver = {
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(dp83640_of_match_table),
+ }
};
static int __init dp83640_init(void)
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 2/3] net:phy:dp83640: Support a configurable number of periodic outputs
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
The driver is currently limited to a single periodic output. This patch makes
the number of peridodic output dynamic by dropping the gpio_tab module
parameter and adding calibrate_pin, perout_pins, and extts_pins parameters.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
drivers/net/phy/dp83640.c | 73 ++++++++++++++++++++---------------------------
1 file changed, 31 insertions(+), 42 deletions(-)
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index a370814..28a6e1d 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -38,15 +38,12 @@
#define LAYER4 0x02
#define LAYER2 0x01
#define MAX_RXTS 64
-#define N_EXT_TS 6
+#define N_EXT 8
#define PSF_PTPVER 2
#define PSF_EVNT 0x4000
#define PSF_RX 0x2000
#define PSF_TX 0x1000
-#define EXT_EVENT 1
-#define CAL_EVENT 7
#define CAL_TRIGGER 7
-#define PER_TRIGGER 6
#define MII_DP83640_MICR 0x11
#define MII_DP83640_MISR 0x12
@@ -146,32 +143,24 @@ struct dp83640_clock {
struct ptp_clock *ptp_clock;
};
-/* globals */
-
-enum {
- CALIBRATE_GPIO,
- PEROUT_GPIO,
- EXTTS0_GPIO,
- EXTTS1_GPIO,
- EXTTS2_GPIO,
- EXTTS3_GPIO,
- EXTTS4_GPIO,
- EXTTS5_GPIO,
- GPIO_TABLE_SIZE
-};
static int chosen_phy = -1;
-static ushort gpio_tab[GPIO_TABLE_SIZE] = {
- 1, 2, 3, 4, 8, 9, 10, 11
-};
+static int calibrate_pin = 1;
+static int perout_pins[N_EXT] = {2};
+static int n_per_out = 1;
+static int extts_pins[N_EXT] = {3, 4, 8, 9, 10, 11};
+static int n_ext_ts = 6;
module_param(chosen_phy, int, 0444);
-module_param_array(gpio_tab, ushort, NULL, 0444);
+module_param(calibrate_pin, int, 0444);
+module_param_array(perout_pins, int, &n_per_out, 0444);
+module_param_array(extts_pins, int, &n_ext_ts, 0444);
MODULE_PARM_DESC(chosen_phy, \
"The address of the PHY to use for the ancillary clock features");
-MODULE_PARM_DESC(gpio_tab, \
- "Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
+MODULE_PARM_DESC(calibrate_pin, "Which pin to use for calibration");
+MODULE_PARM_DESC(perout_pins, "Which pins to use for periodic output");
+MODULE_PARM_DESC(extts_pins, "Which pins to use for external timestamping");
/* a list of clocks and a mutex to protect it */
static LIST_HEAD(phyter_clocks);
@@ -267,15 +256,15 @@ static u64 phy2txts(struct phy_txts *p)
}
static void periodic_output(struct dp83640_clock *clock,
- struct ptp_clock_request *clkreq, bool on)
+ struct ptp_clock_request *clkreq, int trigger,
+ bool on)
{
struct dp83640_private *dp83640 = clock->chosen;
struct phy_device *phydev = dp83640->phydev;
u32 sec, nsec, period;
- u16 gpio, ptp_trig, trigger, val;
+ u16 gpio, ptp_trig, val;
- gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
- trigger = PER_TRIGGER;
+ gpio = on ? perout_pins[trigger] : 0;
ptp_trig = TRIG_WR |
(trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
@@ -446,12 +435,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
index = rq->extts.index;
- if (index < 0 || index >= N_EXT_TS)
+ if (index < 0 || index >= n_ext_ts)
return -EINVAL;
- event_num = EXT_EVENT + index;
+ event_num = index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- gpio_num = gpio_tab[EXTTS0_GPIO + index];
+ gpio_num = extts_pins[index];
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
if (rq->extts.flags & PTP_FALLING_EDGE)
evnt |= EVNT_FALL;
@@ -462,9 +451,10 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
return 0;
case PTP_CLK_REQ_PEROUT:
- if (rq->perout.index != 0)
+ index = rq->perout.index;
+ if (index < 0 || index >= n_per_out)
return -EINVAL;
- periodic_output(clock, rq, on);
+ periodic_output(clock, rq, index, on);
return 0;
default:
@@ -557,10 +547,9 @@ static void recalibrate(struct dp83640_clock *clock)
struct list_head *this;
struct dp83640_private *tmp;
struct phy_device *master = clock->chosen->phydev;
- u16 cal_gpio, cfg0, evnt, ptp_trig, trigger, val;
+ u16 cfg0, evnt, ptp_trig, trigger, val;
trigger = CAL_TRIGGER;
- cal_gpio = gpio_tab[CALIBRATE_GPIO];
mutex_lock(&clock->extreg_lock);
@@ -583,8 +572,8 @@ static void recalibrate(struct dp83640_clock *clock)
* enable an event timestamp
*/
evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE;
- evnt |= (CAL_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
- evnt |= (cal_gpio & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+ evnt |= (trigger & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
+ evnt |= (calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
list_for_each(this, &clock->phylist) {
tmp = list_entry(this, struct dp83640_private, list);
@@ -597,7 +586,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE;
ptp_trig |= (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT;
- ptp_trig |= (cal_gpio & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
+ ptp_trig |= (calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
ext_write(0, master, PAGE5, PTP_TRIG, ptp_trig);
/* load trigger */
@@ -661,7 +650,7 @@ static void recalibrate(struct dp83640_clock *clock)
static inline u16 exts_chan_to_edata(int ch)
{
- return 1 << ((ch + EXT_EVENT) * 2);
+ return 1 << ((ch) * 2);
}
static int decode_evnt(struct dp83640_private *dp83640,
@@ -695,14 +684,14 @@ static int decode_evnt(struct dp83640_private *dp83640,
parsed = words + 2;
} else {
parsed = words + 1;
- i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK) - EXT_EVENT;
+ i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK);
ext_status = exts_chan_to_edata(i);
}
event.type = PTP_CLOCK_EXTTS;
event.timestamp = phy2txts(&dp83640->edata);
- for (i = 0; i < N_EXT_TS; i++) {
+ for (i = 0; i < n_ext_ts; i++) {
if (ext_status & exts_chan_to_edata(i)) {
event.index = i;
ptp_clock_event(dp83640->clock->ptp_clock, &event);
@@ -908,8 +897,8 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
sprintf(clock->caps.name, "dp83640 timer");
clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0;
- clock->caps.n_ext_ts = N_EXT_TS;
- clock->caps.n_per_out = 1;
+ clock->caps.n_ext_ts = n_ext_ts;
+ clock->caps.n_per_out = n_per_out;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 1/3] net:phy:dp83640: Program pulsewidth2 values of perout triggers 0 and 1
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
Periodic output triggers 0 and 1 of the dp83640 has a programmable
duty-cycle which is controlled by the Pulsewidth2 field of the trigger
data register. This field is not documented in the datasheet, but it
is described in the "PHYTER Software Development Guide" section
3.1.4.1. Failing to set the field can result in unstable/no trigger
output.
This patch add programming of the Pulsewidth2 field, setting it to the
same value as the Pulsewidth field for a 50% duty cycle.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
drivers/net/phy/dp83640.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 5ff221d..a370814 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -312,6 +312,11 @@ static void periodic_output(struct dp83640_clock *clock,
ext_write(0, phydev, PAGE4, PTP_TDR, sec >> 16); /* sec[31:16] */
ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff); /* ns[15:0] */
ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16); /* ns[31:16] */
+ /* Triggers 0 and 1 has programmable pulsewidth2 */
+ if (trigger == 0 || trigger == 1) {
+ ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff);
+ ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16);
+ }
/*enable trigger*/
val &= ~TRIG_LOAD;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 0/3] dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
This patch series add DT configuration to the DP83640 PHY driver and makes
the configuration of periodic output pins dynamic.
Changes since v2:
- Add patch to properly configure perout triggers 0+1
- Keep extts and perout numbers separate
- Shorten pr_err lines
Changes since v1:
- Add bindings documentation
- Keep module parameters
- Rename gpio->pin
- Split patch into DT part and dynamic periodic output support
Stefan Sørensen (3):
net:phy:dp83640: Program pulsewidth2 values of perout triggers 0 and 1
net:phy:dp83640: Support a configurable number of periodic outputs
net:phy:dp83640: Get pin and master/slave configuration from DT
Documentation/devicetree/bindings/net/dp83640.txt | 29 ++++
drivers/net/phy/dp83640.c | 199 +++++++++++++++-------
2 files changed, 170 insertions(+), 58 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/dp83640.txt
--
1.8.5.3
^ permalink raw reply
* [PATCH v3 3/3] net:phy:dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
This patch adds configuration of the periodic output and external timestamp
pins available on the dp83640 family of PHYs. It also configures the
master/slave relationship in a group of PHYs on the same MDIO bus and the pins
used for clock calibration in the group.
The configuration is retrieved from DT through the properties
dp83640,slave
dp83640,calibrate-pin
dp83640,perout-pins
dp83640,extts-pins
The configuration module parameters are retained as fallback for the non-DT
case.
Since the pin configuration is now stored for each clock device, groups of
devices on different mdio busses can now have different pin configurations.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
Documentation/devicetree/bindings/net/dp83640.txt | 29 +++++
drivers/net/phy/dp83640.c | 139 ++++++++++++++++++----
2 files changed, 143 insertions(+), 25 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/dp83640.txt
diff --git a/Documentation/devicetree/bindings/net/dp83640.txt b/Documentation/devicetree/bindings/net/dp83640.txt
new file mode 100644
index 0000000..b9a57c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dp83640.txt
@@ -0,0 +1,29 @@
+Required properties for the National DP83640 ethernet phy:
+
+- compatible : Must contain "national,dp83640"
+
+Optional properties:
+
+- dp83640,slave: If present, this phy will be slave to another dp83640
+ on the same mdio bus.
+- dp83640,perout-pins : List of the pin pins used for periodic output
+ triggers.
+- dp83640,extts-pins : List of the pin pins used for external event
+ timestamping.
+- dp83640,calibrate-pin : The pin used for master/slave calibration.
+
+Example:
+
+ ethernet-phy@1 {
+ compatible = "national,dp83640", "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ dp83640,perout-pins = <2>;
+ dp83640,extts-pins = <3 4 8 9 10 11>;
+ dp83640,calibrate-pin = <1>;
+ };
+
+ ethernet-phy@2 {
+ compatible = "national,dp83640", "ethernet-phy-ieee802.3-c22";
+ reg = <2>;
+ dp83640,slave;
+ };
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 28a6e1d..077bdc2 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -30,6 +30,7 @@
#include <linux/phy.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
+#include <linux/of_device.h>
#include "dp83640_reg.h"
@@ -120,6 +121,8 @@ struct dp83640_private {
/* queues of incoming and outgoing packets */
struct sk_buff_head rx_queue;
struct sk_buff_head tx_queue;
+ /* is this phyter a slave */
+ bool slave;
};
struct dp83640_clock {
@@ -141,6 +144,7 @@ struct dp83640_clock {
struct list_head phylist;
/* reference to our PTP hardware clock */
struct ptp_clock *ptp_clock;
+ u32 perout_pins[N_EXT], extts_pins[N_EXT], calibrate_pin;
};
@@ -264,7 +268,7 @@ static void periodic_output(struct dp83640_clock *clock,
u32 sec, nsec, period;
u16 gpio, ptp_trig, val;
- gpio = on ? perout_pins[trigger] : 0;
+ gpio = on ? clock->perout_pins[trigger] : 0;
ptp_trig = TRIG_WR |
(trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
@@ -435,12 +439,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
index = rq->extts.index;
- if (index < 0 || index >= n_ext_ts)
+ if (index < 0 || index >= clock->caps.n_ext_ts)
return -EINVAL;
event_num = index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- gpio_num = extts_pins[index];
+ gpio_num = clock->extts_pins[index];
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
if (rq->extts.flags & PTP_FALLING_EDGE)
evnt |= EVNT_FALL;
@@ -452,7 +456,7 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
case PTP_CLK_REQ_PEROUT:
index = rq->perout.index;
- if (index < 0 || index >= n_per_out)
+ if (index < 0 || index >= clock->caps.n_per_out)
return -EINVAL;
periodic_output(clock, rq, index, on);
return 0;
@@ -573,7 +577,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE;
evnt |= (trigger & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
- evnt |= (calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+ evnt |= (clock->calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
list_for_each(this, &clock->phylist) {
tmp = list_entry(this, struct dp83640_private, list);
@@ -586,7 +590,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE;
ptp_trig |= (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT;
- ptp_trig |= (calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
+ ptp_trig |= (clock->calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
ext_write(0, master, PAGE5, PTP_TRIG, ptp_trig);
/* load trigger */
@@ -691,7 +695,7 @@ static int decode_evnt(struct dp83640_private *dp83640,
event.type = PTP_CLOCK_EXTTS;
event.timestamp = phy2txts(&dp83640->edata);
- for (i = 0; i < n_ext_ts; i++) {
+ for (i = 0; i < dp83640->clock->caps.n_ext_ts; i++) {
if (ext_status & exts_chan_to_edata(i)) {
event.index = i;
ptp_clock_event(dp83640->clock->ptp_clock, &event);
@@ -893,12 +897,11 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
mutex_init(&clock->extreg_lock);
mutex_init(&clock->clock_lock);
INIT_LIST_HEAD(&clock->phylist);
+ clock->calibrate_pin = -1;
clock->caps.owner = THIS_MODULE;
sprintf(clock->caps.name, "dp83640 timer");
clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0;
- clock->caps.n_ext_ts = n_ext_ts;
- clock->caps.n_per_out = n_per_out;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
@@ -911,18 +914,6 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
get_device(&bus->dev);
}
-static int choose_this_phy(struct dp83640_clock *clock,
- struct phy_device *phydev)
-{
- if (chosen_phy == -1 && !clock->chosen)
- return 1;
-
- if (chosen_phy == phydev->addr)
- return 1;
-
- return 0;
-}
-
static struct dp83640_clock *dp83640_clock_get(struct dp83640_clock *clock)
{
if (clock)
@@ -968,6 +959,86 @@ static void dp83640_clock_put(struct dp83640_clock *clock)
mutex_unlock(&clock->clock_lock);
}
+#ifdef CONFIG_OF
+static int dp83640_probe_dt(struct device_node *node,
+ struct dp83640_private *dp83640)
+{
+ struct dp83640_clock *clock = dp83640->clock;
+ struct property *prop;
+ int err, proplen, n_cal = 0;
+
+ dp83640->slave = of_property_read_bool(node, "dp83640,slave");
+ if (!dp83640->slave && clock->chosen) {
+ pr_err("More then one dp83640 master on the same bus");
+ return -EINVAL;
+ }
+
+ prop = of_find_property(node, "dp83640,calibrate-pin", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have calibrate pin");
+ return -EINVAL;
+ }
+ of_property_read_u32(node, "dp83640,calibrate-pin",
+ &clock->calibrate_pin);
+ n_cal = 1;
+ }
+
+ prop = of_find_property(node, "dp83640,perout-pins", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have perout pins");
+ return -EINVAL;
+ }
+
+ clock->caps.n_per_out = proplen / sizeof(u32);
+ if (clock->caps.n_per_out + n_cal > N_EXT) {
+ pr_err("Too many dp83640,perout-pins");
+ return -EINVAL;
+ }
+ err = of_property_read_u32_array(node, "dp83640,perout-pins",
+ clock->perout_pins,
+ clock->caps.n_per_out);
+ if (err < 0)
+ return err;
+ }
+
+ prop = of_find_property(node, "dp83640,extts-pins", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have extts pins");
+ return -EINVAL;
+ }
+
+ clock->caps.n_ext_ts = proplen / sizeof(u32);
+ if (clock->caps.n_ext_ts + n_cal > N_EXT) {
+ pr_err("Too many dp83640,extts-pins");
+ return -EINVAL;
+ }
+ err = of_property_read_u32_array(node, "dp83640,extts-pins",
+ clock->extts_pins,
+ clock->caps.n_ext_ts);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id dp83640_of_match_table[] = {
+ { .compatible = "national,dp83640", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, dp83640_of_match_table);
+#else
+
+static inline int dp83640_probe_dt(struct device_node *node,
+ struct dp83640_private *dp83640)
+{
+ return 0;
+}
+#endif
+
static int dp83640_probe(struct phy_device *phydev)
{
struct dp83640_clock *clock;
@@ -985,7 +1056,24 @@ static int dp83640_probe(struct phy_device *phydev)
if (!dp83640)
goto no_memory;
+ dp83640->clock = clock;
dp83640->phydev = phydev;
+
+ if (phydev->dev.of_node) {
+ err = dp83640_probe_dt(phydev->dev.of_node, dp83640);
+ if (err)
+ return err;
+ } else {
+ clock->calibrate_pin = calibrate_pin;
+ memcpy(clock->perout_pins, perout_pins,
+ sizeof(clock->perout_pins));
+ memcpy(clock->extts_pins, extts_pins,
+ sizeof(clock->extts_pins));
+ if (clock->chosen ||
+ (chosen_phy != -1 && phydev->addr != chosen_phy))
+ dp83640->slave = true;
+ }
+
INIT_WORK(&dp83640->ts_work, rx_timestamp_work);
INIT_LIST_HEAD(&dp83640->rxts);
@@ -999,9 +1087,7 @@ static int dp83640_probe(struct phy_device *phydev)
skb_queue_head_init(&dp83640->rx_queue);
skb_queue_head_init(&dp83640->tx_queue);
- dp83640->clock = clock;
-
- if (choose_this_phy(clock, phydev)) {
+ if (!dp83640->slave) {
clock->chosen = dp83640;
clock->ptp_clock = ptp_clock_register(&clock->caps, &phydev->dev);
if (IS_ERR(clock->ptp_clock)) {
@@ -1353,7 +1439,10 @@ static struct phy_driver dp83640_driver = {
.hwtstamp = dp83640_hwtstamp,
.rxtstamp = dp83640_rxtstamp,
.txtstamp = dp83640_txtstamp,
- .driver = {.owner = THIS_MODULE,}
+ .driver = {
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(dp83640_of_match_table),
+ }
};
static int __init dp83640_init(void)
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 2/3] net:phy:dp83640: Support a configurable number of periodic outputs
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
The driver is currently limited to a single periodic output. This patch makes
the number of peridodic output dynamic by dropping the gpio_tab module
parameter and adding calibrate_pin, perout_pins, and extts_pins parameters.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
drivers/net/phy/dp83640.c | 73 ++++++++++++++++++++---------------------------
1 file changed, 31 insertions(+), 42 deletions(-)
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index a370814..28a6e1d 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -38,15 +38,12 @@
#define LAYER4 0x02
#define LAYER2 0x01
#define MAX_RXTS 64
-#define N_EXT_TS 6
+#define N_EXT 8
#define PSF_PTPVER 2
#define PSF_EVNT 0x4000
#define PSF_RX 0x2000
#define PSF_TX 0x1000
-#define EXT_EVENT 1
-#define CAL_EVENT 7
#define CAL_TRIGGER 7
-#define PER_TRIGGER 6
#define MII_DP83640_MICR 0x11
#define MII_DP83640_MISR 0x12
@@ -146,32 +143,24 @@ struct dp83640_clock {
struct ptp_clock *ptp_clock;
};
-/* globals */
-
-enum {
- CALIBRATE_GPIO,
- PEROUT_GPIO,
- EXTTS0_GPIO,
- EXTTS1_GPIO,
- EXTTS2_GPIO,
- EXTTS3_GPIO,
- EXTTS4_GPIO,
- EXTTS5_GPIO,
- GPIO_TABLE_SIZE
-};
static int chosen_phy = -1;
-static ushort gpio_tab[GPIO_TABLE_SIZE] = {
- 1, 2, 3, 4, 8, 9, 10, 11
-};
+static int calibrate_pin = 1;
+static int perout_pins[N_EXT] = {2};
+static int n_per_out = 1;
+static int extts_pins[N_EXT] = {3, 4, 8, 9, 10, 11};
+static int n_ext_ts = 6;
module_param(chosen_phy, int, 0444);
-module_param_array(gpio_tab, ushort, NULL, 0444);
+module_param(calibrate_pin, int, 0444);
+module_param_array(perout_pins, int, &n_per_out, 0444);
+module_param_array(extts_pins, int, &n_ext_ts, 0444);
MODULE_PARM_DESC(chosen_phy, \
"The address of the PHY to use for the ancillary clock features");
-MODULE_PARM_DESC(gpio_tab, \
- "Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
+MODULE_PARM_DESC(calibrate_pin, "Which pin to use for calibration");
+MODULE_PARM_DESC(perout_pins, "Which pins to use for periodic output");
+MODULE_PARM_DESC(extts_pins, "Which pins to use for external timestamping");
/* a list of clocks and a mutex to protect it */
static LIST_HEAD(phyter_clocks);
@@ -267,15 +256,15 @@ static u64 phy2txts(struct phy_txts *p)
}
static void periodic_output(struct dp83640_clock *clock,
- struct ptp_clock_request *clkreq, bool on)
+ struct ptp_clock_request *clkreq, int trigger,
+ bool on)
{
struct dp83640_private *dp83640 = clock->chosen;
struct phy_device *phydev = dp83640->phydev;
u32 sec, nsec, period;
- u16 gpio, ptp_trig, trigger, val;
+ u16 gpio, ptp_trig, val;
- gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
- trigger = PER_TRIGGER;
+ gpio = on ? perout_pins[trigger] : 0;
ptp_trig = TRIG_WR |
(trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
@@ -446,12 +435,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
index = rq->extts.index;
- if (index < 0 || index >= N_EXT_TS)
+ if (index < 0 || index >= n_ext_ts)
return -EINVAL;
- event_num = EXT_EVENT + index;
+ event_num = index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- gpio_num = gpio_tab[EXTTS0_GPIO + index];
+ gpio_num = extts_pins[index];
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
if (rq->extts.flags & PTP_FALLING_EDGE)
evnt |= EVNT_FALL;
@@ -462,9 +451,10 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
return 0;
case PTP_CLK_REQ_PEROUT:
- if (rq->perout.index != 0)
+ index = rq->perout.index;
+ if (index < 0 || index >= n_per_out)
return -EINVAL;
- periodic_output(clock, rq, on);
+ periodic_output(clock, rq, index, on);
return 0;
default:
@@ -557,10 +547,9 @@ static void recalibrate(struct dp83640_clock *clock)
struct list_head *this;
struct dp83640_private *tmp;
struct phy_device *master = clock->chosen->phydev;
- u16 cal_gpio, cfg0, evnt, ptp_trig, trigger, val;
+ u16 cfg0, evnt, ptp_trig, trigger, val;
trigger = CAL_TRIGGER;
- cal_gpio = gpio_tab[CALIBRATE_GPIO];
mutex_lock(&clock->extreg_lock);
@@ -583,8 +572,8 @@ static void recalibrate(struct dp83640_clock *clock)
* enable an event timestamp
*/
evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE;
- evnt |= (CAL_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
- evnt |= (cal_gpio & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+ evnt |= (trigger & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
+ evnt |= (calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
list_for_each(this, &clock->phylist) {
tmp = list_entry(this, struct dp83640_private, list);
@@ -597,7 +586,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE;
ptp_trig |= (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT;
- ptp_trig |= (cal_gpio & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
+ ptp_trig |= (calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
ext_write(0, master, PAGE5, PTP_TRIG, ptp_trig);
/* load trigger */
@@ -661,7 +650,7 @@ static void recalibrate(struct dp83640_clock *clock)
static inline u16 exts_chan_to_edata(int ch)
{
- return 1 << ((ch + EXT_EVENT) * 2);
+ return 1 << ((ch) * 2);
}
static int decode_evnt(struct dp83640_private *dp83640,
@@ -695,14 +684,14 @@ static int decode_evnt(struct dp83640_private *dp83640,
parsed = words + 2;
} else {
parsed = words + 1;
- i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK) - EXT_EVENT;
+ i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK);
ext_status = exts_chan_to_edata(i);
}
event.type = PTP_CLOCK_EXTTS;
event.timestamp = phy2txts(&dp83640->edata);
- for (i = 0; i < N_EXT_TS; i++) {
+ for (i = 0; i < n_ext_ts; i++) {
if (ext_status & exts_chan_to_edata(i)) {
event.index = i;
ptp_clock_event(dp83640->clock->ptp_clock, &event);
@@ -908,8 +897,8 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
sprintf(clock->caps.name, "dp83640 timer");
clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0;
- clock->caps.n_ext_ts = N_EXT_TS;
- clock->caps.n_per_out = 1;
+ clock->caps.n_ext_ts = n_ext_ts;
+ clock->caps.n_per_out = n_per_out;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 1/3] net:phy:dp83640: Program pulsewidth2 values of perout triggers 0 and 1
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
Periodic output triggers 0 and 1 of the dp83640 has a programmable
duty-cycle which is controlled by the Pulsewidth2 field of the trigger
data register. This field is not documented in the datasheet, but it
is described in the "PHYTER Software Development Guide" section
3.1.4.1. Failing to set the field can result in unstable/no trigger
output.
This patch add programming of the Pulsewidth2 field, setting it to the
same value as the Pulsewidth field for a 50% duty cycle.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
drivers/net/phy/dp83640.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 5ff221d..a370814 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -312,6 +312,11 @@ static void periodic_output(struct dp83640_clock *clock,
ext_write(0, phydev, PAGE4, PTP_TDR, sec >> 16); /* sec[31:16] */
ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff); /* ns[15:0] */
ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16); /* ns[31:16] */
+ /* Triggers 0 and 1 has programmable pulsewidth2 */
+ if (trigger == 0 || trigger == 1) {
+ ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff);
+ ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16);
+ }
/*enable trigger*/
val &= ~TRIG_LOAD;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 0/3] dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
This patch series add DT configuration to the DP83640 PHY driver and makes
the configuration of periodic output pins dynamic.
Changes since v2:
- Add patch to properly configure perout triggers 0+1
- Keep extts and perout numbers separate
- Shorten pr_err lines
Changes since v1:
- Add bindings documentation
- Keep module parameters
- Rename gpio->pin
- Split patch into DT part and dynamic periodic output support
Stefan Sørensen (3):
net:phy:dp83640: Program pulsewidth2 values of perout triggers 0 and 1
net:phy:dp83640: Support a configurable number of periodic outputs
net:phy:dp83640: Get pin and master/slave configuration from DT
Documentation/devicetree/bindings/net/dp83640.txt | 29 ++++
drivers/net/phy/dp83640.c | 199 +++++++++++++++-------
2 files changed, 170 insertions(+), 58 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/dp83640.txt
--
1.8.5.3
^ permalink raw reply
* Re: [PATCH v2 2/2] dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:23 UTC (permalink / raw)
To: Richard Cochran
Cc: grant.likely, robh+dt, mark.rutland, netdev, linux-kernel,
devicetree
In-Reply-To: <20140211201917.GB4254@netboy>
On Tue, 2014-02-11 at 21:19 +0100, Richard Cochran wrote:
> > +- dp83640,slave: If present, this phy will be slave to another dp83640
> > + on the same mdio bus.
>
> Wouldn't it be more natural to have one "dp83640,master" property
> rather than multiple slave properties?
I wanted to keep the common case of a single phy simple, i.e. no need to
specify any master/slave properties.
> Most of these pr_err lines are a bit _way_ too long for coding style.
I will fix that in the next version.
Stefan
^ 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