* [PATCH v2 1/6] usb: dwc2: add support for Meson8b and GXBB SoCs
From: Kevin Hilman @ 2016-09-14 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <0d587b46-4c6e-c2ec-a62e-5d0c5a5bd902@synopsys.com>
On Wed, Sep 14, 2016 at 11:12 AM, John Youn <John.Youn@synopsys.com> wrote:
> On 9/14/2016 9:11 AM, Kevin Hilman wrote:
>> Hi John,
>>
>> Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes:
>>
>>> From: Jerome Brunet <jbrunet@baylibre.com>
>>>
>>> Add compatible strings for amlogic Meson8b and GXBB SoCs with the
>>> corresponding configuration parameters.
>>>
>>> Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
>>> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>>
>> Are you OK with adding this platform for v4.9? I know you mentioned
>> you're working on new bindings to replace the current way, but since
>> that hasn't been posted AFAICT, it would be nice to get this merged now
>> and we can help test the new bindings when you're ready.
>>
>> If you're OK with that, and with your Ack, I can take merge the driver
>> changes through the arm-soc tree along with the rest of the DT patches.
>>
>
> Sure. Unfortunately, I wasn't able to complete it for 4.9.
>
> You can add my acked-by on this:
>
> Acked-by: John Youn <johnyoun@synopsys.com>
Great, thanks! I assume you're OK with PATCH 2/2 for the bindings as
well (at least for now until you rework them?)
Kevin
^ permalink raw reply
* [PATCH 1/4] clk: gxbb: expose i2c clocks
From: Stephen Boyd @ 2016-09-14 18:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473847568-20163-2-git-send-email-jbrunet@baylibre.com>
On 09/14, Jerome Brunet wrote:
> I2C and AO_I2C clocks are needed for the i2c driver, expose to DT
> (and comment out in clk driver)
>
> Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
> ---
Acked-by: Stephen Boyd <sboyd@codeaurora.org>
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH v2 1/6] usb: dwc2: add support for Meson8b and GXBB SoCs
From: John Youn @ 2016-09-14 18:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOi56cW+zJ8_Gf6xKMyKNk7YB1+MpWHrtmv=+Xxs0PZy9rAXqQ@mail.gmail.com>
On 9/14/2016 11:17 AM, Kevin Hilman wrote:
> On Wed, Sep 14, 2016 at 11:12 AM, John Youn <John.Youn@synopsys.com> wrote:
>> On 9/14/2016 9:11 AM, Kevin Hilman wrote:
>>> Hi John,
>>>
>>> Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes:
>>>
>>>> From: Jerome Brunet <jbrunet@baylibre.com>
>>>>
>>>> Add compatible strings for amlogic Meson8b and GXBB SoCs with the
>>>> corresponding configuration parameters.
>>>>
>>>> Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
>>>> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>>>
>>> Are you OK with adding this platform for v4.9? I know you mentioned
>>> you're working on new bindings to replace the current way, but since
>>> that hasn't been posted AFAICT, it would be nice to get this merged now
>>> and we can help test the new bindings when you're ready.
>>>
>>> If you're OK with that, and with your Ack, I can take merge the driver
>>> changes through the arm-soc tree along with the rest of the DT patches.
>>>
>>
>> Sure. Unfortunately, I wasn't able to complete it for 4.9.
>>
>> You can add my acked-by on this:
>>
>> Acked-by: John Youn <johnyoun@synopsys.com>
>
> Great, thanks! I assume you're OK with PATCH 2/2 for the bindings as
> well (at least for now until you rework them?)
>
> Kevin
>
Do you mean PATCH 2/6 from this series?
It doesn't seem to add or remove any bindings in dwc2 so I'm fine with
it.
Regards,
John
^ permalink raw reply
* [PATCH 1/2] ARM: clk-imx35: fix name for ckil clk
From: Stephen Boyd @ 2016-09-14 18:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473327022-8065-1-git-send-email-u.kleine-koenig@pengutronix.de>
On 09/08, Uwe Kleine-K?nig wrote:
> This fixes
> [ 0.000000] i.MX clk 82: register failed with -17
> because the name is duplicated.
>
> Signed-off-by: Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>
> ---
Applied to clk-next
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH 2/2] ARM: clk-imx35: annotate clk enum with number values
From: Stephen Boyd @ 2016-09-14 18:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473327022-8065-2-git-send-email-u.kleine-koenig@pengutronix.de>
On 09/08, Uwe Kleine-K?nig wrote:
> This helps to decode error messages like:
>
> [ 0.000000] i.MX clk 82: register failed with -17
>
> Signed-off-by: Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>
> ---
Applied to clk-next
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH v1 2/3] ARM: dts: imx6q-apalis-ixora: Remove use of pwm-leds
From: Marcel Ziswiler @ 2016-09-14 18:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5eee36b0a60c41a05ca2a161b8ef3eb7@agner.ch>
On Wed, 2016-09-14 at 08:58 -0700, Stefan Agner wrote:
> On 2016-09-13 23:35, Sanchayan Maity wrote:
> >
> > Remove use of pwm-leds and use the standard /sys/class/pwm
> > interface from PWM subsystem.
> Don't we have actual leds on the Ixora?
Yes, sure, but not really PWM ones:
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/ar
ch/arm/boot/dts/imx6q-apalis-ixora.dts?id=refs/tags/v4.8-rc6#n124
^ permalink raw reply
* [PATCH] ARM: s3c24xx: Add dma_slave_map for s3c2440 devices
From: Sam Van Den Berge @ 2016-09-14 18:29 UTC (permalink / raw)
To: linux-arm-kernel
This patch updates the s3c24xx dma driver to be able to pass a
dma_slave_map array via the platform data. This is needed to
be able to use the new, simpler dmaengine API [1].
I used the virtual DMA channels as a parameter for the dma_filter
function. By doing that, I could reuse the existing filter function in
drivers/dma/s3c24xx-dma.c.
I have tested this on my mini2440 board with the audio driver.
(I first applied the audio fixes from Sylwester Nawrocki [2])
According to my observations, dma_request_slave_channel in the
function dmaengine_pcm_new in the file
sound/soc/soc-generic-dmaengine-pcm.c now returns a valid DMA channel
whereas before no DMA channel was returned at that point.
Entries for DMACH_XD0, DMACH_XD1 and DMACH_TIMER are missing because I
don't realy know which driver to use for these.
[1]
http://lists.infradead.org/pipermail/linux-arm-kernel/2015-December/393635.html
[2] http://www.spinics.net/lists/arm-kernel/msg521918.html
Signed-off-by: Sam Van Den Berge <sam.van.den.berge@telenet.be>
---
arch/arm/mach-s3c24xx/common.c | 36 +++++++++++++++++++++++++++++++
drivers/dma/s3c24xx-dma.c | 3 +++
include/linux/platform_data/dma-s3c24xx.h | 6 ++++++
3 files changed, 45 insertions(+)
diff --git a/arch/arm/mach-s3c24xx/common.c b/arch/arm/mach-s3c24xx/common.c
index fe7485d..d5cadef 100644
--- a/arch/arm/mach-s3c24xx/common.c
+++ b/arch/arm/mach-s3c24xx/common.c
@@ -33,6 +33,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/platform_data/dma-s3c24xx.h>
+#include <linux/dmaengine.h>
#include <mach/hardware.h>
#include <mach/regs-clock.h>
@@ -445,10 +446,45 @@ static struct s3c24xx_dma_channel s3c2440_dma_channels[DMACH_MAX] = {
[DMACH_USB_EP4] = { S3C24XX_DMA_APB, true, S3C24XX_DMA_CHANREQ(4, 3), },
};
+static const struct dma_slave_map s3c2440_dma_slave_map[] = {
+ /* TODO: DMACH_XD0 */
+ /* TODO: DMACH_XD1 */
+ { "3c2440-sdi", "rx", (void *)DMACH_SDI },
+ { "3c2440-sdi", "tx", (void *)DMACH_SDI },
+ { "s3c2410-spi.0", "rx", (void *)DMACH_SPI0 },
+ { "s3c2410-spi.0", "tx", (void *)DMACH_SPI0 },
+ { "s3c2410-spi.1", "rx", (void *)DMACH_SPI1 },
+ { "s3c2410-spi.1", "tx", (void *)DMACH_SPI1 },
+ { "s3c2440-uart.0", "rx", (void *)DMACH_UART0 },
+ { "s3c2440-uart.0", "tx", (void *)DMACH_UART0 },
+ { "s3c2440-uart.1", "rx", (void *)DMACH_UART1 },
+ { "s3c2440-uart.1", "tx", (void *)DMACH_UART1 },
+ { "s3c2440-uart.2", "rx", (void *)DMACH_UART2 },
+ { "s3c2440-uart.2", "tx", (void *)DMACH_UART2 },
+ { "s3c2440-uart.3", "rx", (void *)DMACH_UART3 },
+ { "s3c2440-uart.3", "tx", (void *)DMACH_UART3 },
+ /* TODO: DMACH_TIMER */
+ { "s3c24xx-iis", "rx", (void *)DMACH_I2S_IN },
+ { "s3c24xx-iis", "tx", (void *)DMACH_I2S_OUT },
+ { "samsung-ac97", "rx", (void *)DMACH_PCM_IN },
+ { "samsung-ac97", "tx", (void *)DMACH_PCM_OUT },
+ { "samsung-ac97", "rx", (void *)DMACH_MIC_IN },
+ { "s3c-hsudc", "rx0", (void *)DMACH_USB_EP1 },
+ { "s3c-hsudc", "rx1", (void *)DMACH_USB_EP2 },
+ { "s3c-hsudc", "rx2", (void *)DMACH_USB_EP3 },
+ { "s3c-hsudc", "rx3", (void *)DMACH_USB_EP4 },
+ { "s3c-hsudc", "tx0", (void *)DMACH_USB_EP1 },
+ { "s3c-hsudc", "tx1", (void *)DMACH_USB_EP2 },
+ { "s3c-hsudc", "tx2", (void *)DMACH_USB_EP3 },
+ { "s3c-hsudc", "tx3", (void *)DMACH_USB_EP4 }
+};
+
static struct s3c24xx_dma_platdata s3c2440_dma_platdata = {
.num_phy_channels = 4,
.channels = s3c2440_dma_channels,
.num_channels = DMACH_MAX,
+ .slave_map = s3c2440_dma_slave_map,
+ .slavecnt = ARRAY_SIZE(s3c2440_dma_slave_map),
};
struct platform_device s3c2440_device_dma = {
diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c
index ce67075..d5c85e7 100644
--- a/drivers/dma/s3c24xx-dma.c
+++ b/drivers/dma/s3c24xx-dma.c
@@ -1301,6 +1301,9 @@ static int s3c24xx_dma_probe(struct platform_device *pdev)
s3cdma->slave.device_prep_dma_cyclic = s3c24xx_dma_prep_dma_cyclic;
s3cdma->slave.device_config = s3c24xx_dma_set_runtime_config;
s3cdma->slave.device_terminate_all = s3c24xx_dma_terminate_all;
+ s3cdma->slave.filter.map = pdata->slave_map;
+ s3cdma->slave.filter.mapcnt = pdata->slavecnt;
+ s3cdma->slave.filter.fn = s3c24xx_dma_filter;
/* Register as many memcpy channels as there are physical channels */
ret = s3c24xx_dma_init_virtual_channels(s3cdma, &s3cdma->memcpy,
diff --git a/include/linux/platform_data/dma-s3c24xx.h b/include/linux/platform_data/dma-s3c24xx.h
index 89ba1b0..4f9aba4 100644
--- a/include/linux/platform_data/dma-s3c24xx.h
+++ b/include/linux/platform_data/dma-s3c24xx.h
@@ -30,16 +30,22 @@ struct s3c24xx_dma_channel {
u16 chansel;
};
+struct dma_slave_map;
+
/**
* struct s3c24xx_dma_platdata - platform specific settings
* @num_phy_channels: number of physical channels
* @channels: array of virtual channel descriptions
* @num_channels: number of virtual channels
+ * @slave_map: dma slave map matching table
+ * @slavecnt: number of elements in slave_map
*/
struct s3c24xx_dma_platdata {
int num_phy_channels;
struct s3c24xx_dma_channel *channels;
int num_channels;
+ const struct dma_slave_map *slave_map;
+ int slavecnt;
};
struct dma_chan;
--
1.9.1
^ permalink raw reply related
* [GIT PULL] Greybus driver subsystem for 4.9-rc1
From: Greg KH @ 2016-09-14 18:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160914180754.GA16053@kroah.com>
On Wed, Sep 14, 2016 at 08:07:54PM +0200, Greg KH wrote:
> On Wed, Sep 14, 2016 at 06:36:26PM +0100, Mark Rutland wrote:
> > Hi Greg,
> >
> > On Wed, Sep 14, 2016 at 12:09:49PM +0200, Greg KH wrote:
> > > Given that it's never a good idea to keep subsystems out of the mainline
> > > kernel, I've put together this pull request that adds the greybus driver
> > > layer to drivers/greybus/. Because this was 2 1/2 years of work, with
> > > many many developers contributing, I didn't want to flatten all of their
> > > effort into a few small patches, as that wouldn't be very fair. So I've
> > > built a git tree with all of the changes going back to the first commit,
> > > and merged it into the kernel tree, just like btrfs was merged into the
> > > kernel.
> >
> > > Unless people point out some major problems with this, I'd like to get
> > > it merged into 4.9-rc1.
> >
> > I'm extremely concerned that these patches have *never* seen upstream
> > review, and this pull request gives no real opportunity for people to
> > make a judgement regarding the code, as many relevant parties have not
> > been Cc'd.
>
> As I said, I will send a set of simple patches, I wanted to get this out
> as soon as possible and other things came up today. Will do it in the
> morning, sorry.
Here's the timesync code pulled out into a simple patch if you want to
see it.
Bryan, any explanations you want to provide that would help in
clarifying Mark's issues?
thanks,
greg k-h
---
drivers/greybus/timesync.c | 1357 ++++++++++++++++++++++++++++++++++++
drivers/greybus/timesync.h | 45 +
drivers/greybus/timesync_platform.c | 77 ++
3 files changed, 1479 insertions(+)
--- /dev/null
+++ b/drivers/greybus/timesync.c
@@ -0,0 +1,1357 @@
+/*
+ * TimeSync API driver.
+ *
+ * Copyright 2016 Google Inc.
+ * Copyright 2016 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+#include <linux/debugfs.h>
+#include <linux/hrtimer.h>
+#include "greybus.h"
+#include "timesync.h"
+#include "greybus_trace.h"
+
+/*
+ * Minimum inter-strobe value of one millisecond is chosen because it
+ * just-about fits the common definition of a jiffy.
+ *
+ * Maximum value OTOH is constrained by the number of bits the SVC can fit
+ * into a 16 bit up-counter. The SVC configures the timer in microseconds
+ * so the maximum allowable value is 65535 microseconds. We clip that value
+ * to 10000 microseconds for the sake of using nice round base 10 numbers
+ * and since right-now there's no imaginable use-case requiring anything
+ * other than a one millisecond inter-strobe time, let alone something
+ * higher than ten milliseconds.
+ */
+#define GB_TIMESYNC_STROBE_DELAY_US 1000
+#define GB_TIMESYNC_DEFAULT_OFFSET_US 1000
+
+/* Work queue timers long, short and SVC strobe timeout */
+#define GB_TIMESYNC_DELAYED_WORK_LONG msecs_to_jiffies(10)
+#define GB_TIMESYNC_DELAYED_WORK_SHORT msecs_to_jiffies(1)
+#define GB_TIMESYNC_MAX_WAIT_SVC msecs_to_jiffies(5000)
+#define GB_TIMESYNC_KTIME_UPDATE msecs_to_jiffies(1000)
+#define GB_TIMESYNC_MAX_KTIME_CONVERSION 15
+
+/* Maximum number of times we'll retry a failed synchronous sync */
+#define GB_TIMESYNC_MAX_RETRIES 5
+
+/* Reported nanoseconds/femtoseconds per clock */
+static u64 gb_timesync_ns_per_clock;
+static u64 gb_timesync_fs_per_clock;
+
+/* Maximum difference we will accept converting FrameTime to ktime */
+static u32 gb_timesync_max_ktime_diff;
+
+/* Reported clock rate */
+static unsigned long gb_timesync_clock_rate;
+
+/* Workqueue */
+static void gb_timesync_worker(struct work_struct *work);
+
+/* List of SVCs with one FrameTime per SVC */
+static LIST_HEAD(gb_timesync_svc_list);
+
+/* Synchronize parallel contexts accessing a valid timesync_svc pointer */
+static DEFINE_MUTEX(gb_timesync_svc_list_mutex);
+
+/* Structure to convert from FrameTime to timespec/ktime */
+struct gb_timesync_frame_time_data {
+ u64 frame_time;
+ struct timespec ts;
+};
+
+struct gb_timesync_svc {
+ struct list_head list;
+ struct list_head interface_list;
+ struct gb_svc *svc;
+ struct gb_timesync_host_device *timesync_hd;
+
+ spinlock_t spinlock; /* Per SVC spinlock to sync with ISR */
+ struct mutex mutex; /* Per SVC mutex for regular synchronization */
+
+ struct dentry *frame_time_dentry;
+ struct dentry *frame_ktime_dentry;
+ struct workqueue_struct *work_queue;
+ wait_queue_head_t wait_queue;
+ struct delayed_work delayed_work;
+ struct timer_list ktime_timer;
+
+ /* The current local FrameTime */
+ u64 frame_time_offset;
+ struct gb_timesync_frame_time_data strobe_data[GB_TIMESYNC_MAX_STROBES];
+ struct gb_timesync_frame_time_data ktime_data;
+
+ /* The SVC FrameTime and relative AP FrameTime @ last TIMESYNC_PING */
+ u64 svc_ping_frame_time;
+ u64 ap_ping_frame_time;
+
+ /* Transitory settings */
+ u32 strobe_mask;
+ bool offset_down;
+ bool print_ping;
+ bool capture_ping;
+ int strobe;
+
+ /* Current state */
+ int state;
+};
+
+struct gb_timesync_host_device {
+ struct list_head list;
+ struct gb_host_device *hd;
+ u64 ping_frame_time;
+};
+
+struct gb_timesync_interface {
+ struct list_head list;
+ struct gb_interface *interface;
+ u64 ping_frame_time;
+};
+
+enum gb_timesync_state {
+ GB_TIMESYNC_STATE_INVALID = 0,
+ GB_TIMESYNC_STATE_INACTIVE = 1,
+ GB_TIMESYNC_STATE_INIT = 2,
+ GB_TIMESYNC_STATE_WAIT_SVC = 3,
+ GB_TIMESYNC_STATE_AUTHORITATIVE = 4,
+ GB_TIMESYNC_STATE_PING = 5,
+ GB_TIMESYNC_STATE_ACTIVE = 6,
+};
+
+static void gb_timesync_ktime_timer_fn(unsigned long data);
+
+static u64 gb_timesync_adjust_count(struct gb_timesync_svc *timesync_svc,
+ u64 counts)
+{
+ if (timesync_svc->offset_down)
+ return counts - timesync_svc->frame_time_offset;
+ else
+ return counts + timesync_svc->frame_time_offset;
+}
+
+/*
+ * This function provides the authoritative FrameTime to a calling function. It
+ * is designed to be lockless and should remain that way the caller is assumed
+ * to be state-aware.
+ */
+static u64 __gb_timesync_get_frame_time(struct gb_timesync_svc *timesync_svc)
+{
+ u64 clocks = gb_timesync_platform_get_counter();
+
+ return gb_timesync_adjust_count(timesync_svc, clocks);
+}
+
+static void gb_timesync_schedule_svc_timeout(struct gb_timesync_svc
+ *timesync_svc)
+{
+ queue_delayed_work(timesync_svc->work_queue,
+ ×ync_svc->delayed_work,
+ GB_TIMESYNC_MAX_WAIT_SVC);
+}
+
+static void gb_timesync_set_state(struct gb_timesync_svc *timesync_svc,
+ int state)
+{
+ switch (state) {
+ case GB_TIMESYNC_STATE_INVALID:
+ timesync_svc->state = state;
+ wake_up(×ync_svc->wait_queue);
+ break;
+ case GB_TIMESYNC_STATE_INACTIVE:
+ timesync_svc->state = state;
+ wake_up(×ync_svc->wait_queue);
+ break;
+ case GB_TIMESYNC_STATE_INIT:
+ if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) {
+ timesync_svc->strobe = 0;
+ timesync_svc->frame_time_offset = 0;
+ timesync_svc->state = state;
+ cancel_delayed_work(×ync_svc->delayed_work);
+ queue_delayed_work(timesync_svc->work_queue,
+ ×ync_svc->delayed_work,
+ GB_TIMESYNC_DELAYED_WORK_LONG);
+ }
+ break;
+ case GB_TIMESYNC_STATE_WAIT_SVC:
+ if (timesync_svc->state == GB_TIMESYNC_STATE_INIT)
+ timesync_svc->state = state;
+ break;
+ case GB_TIMESYNC_STATE_AUTHORITATIVE:
+ if (timesync_svc->state == GB_TIMESYNC_STATE_WAIT_SVC) {
+ timesync_svc->state = state;
+ cancel_delayed_work(×ync_svc->delayed_work);
+ queue_delayed_work(timesync_svc->work_queue,
+ ×ync_svc->delayed_work, 0);
+ }
+ break;
+ case GB_TIMESYNC_STATE_PING:
+ if (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE) {
+ timesync_svc->state = state;
+ queue_delayed_work(timesync_svc->work_queue,
+ ×ync_svc->delayed_work,
+ GB_TIMESYNC_DELAYED_WORK_SHORT);
+ }
+ break;
+ case GB_TIMESYNC_STATE_ACTIVE:
+ if (timesync_svc->state == GB_TIMESYNC_STATE_AUTHORITATIVE ||
+ timesync_svc->state == GB_TIMESYNC_STATE_PING) {
+ timesync_svc->state = state;
+ wake_up(×ync_svc->wait_queue);
+ }
+ break;
+ }
+
+ if (WARN_ON(timesync_svc->state != state)) {
+ pr_err("Invalid state transition %d=>%d\n",
+ timesync_svc->state, state);
+ }
+}
+
+static void gb_timesync_set_state_atomic(struct gb_timesync_svc *timesync_svc,
+ int state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(×ync_svc->spinlock, flags);
+ gb_timesync_set_state(timesync_svc, state);
+ spin_unlock_irqrestore(×ync_svc->spinlock, flags);
+}
+
+static u64 gb_timesync_diff(u64 x, u64 y)
+{
+ if (x > y)
+ return x - y;
+ else
+ return y - x;
+}
+
+static void gb_timesync_adjust_to_svc(struct gb_timesync_svc *svc,
+ u64 svc_frame_time, u64 ap_frame_time)
+{
+ if (svc_frame_time > ap_frame_time) {
+ svc->frame_time_offset = svc_frame_time - ap_frame_time;
+ svc->offset_down = false;
+ } else {
+ svc->frame_time_offset = ap_frame_time - svc_frame_time;
+ svc->offset_down = true;
+ }
+}
+
+/*
+ * Associate a FrameTime with a ktime timestamp represented as struct timespec
+ * Requires the calling context to hold timesync_svc->mutex
+ */
+static void gb_timesync_store_ktime(struct gb_timesync_svc *timesync_svc,
+ struct timespec ts, u64 frame_time)
+{
+ timesync_svc->ktime_data.ts = ts;
+ timesync_svc->ktime_data.frame_time = frame_time;
+}
+
+/*
+ * Find the two pulses that best-match our expected inter-strobe gap and
+ * then calculate the difference between the SVC time at the second pulse
+ * to the local time at the second pulse.
+ */
+static void gb_timesync_collate_frame_time(struct gb_timesync_svc *timesync_svc,
+ u64 *frame_time)
+{
+ int i = 0;
+ u64 delta, ap_frame_time;
+ u64 strobe_delay_ns = GB_TIMESYNC_STROBE_DELAY_US * NSEC_PER_USEC;
+ u64 least = 0;
+
+ for (i = 1; i < GB_TIMESYNC_MAX_STROBES; i++) {
+ delta = timesync_svc->strobe_data[i].frame_time -
+ timesync_svc->strobe_data[i - 1].frame_time;
+ delta *= gb_timesync_ns_per_clock;
+ delta = gb_timesync_diff(delta, strobe_delay_ns);
+
+ if (!least || delta < least) {
+ least = delta;
+ gb_timesync_adjust_to_svc(timesync_svc, frame_time[i],
+ timesync_svc->strobe_data[i].frame_time);
+
+ ap_frame_time = timesync_svc->strobe_data[i].frame_time;
+ ap_frame_time = gb_timesync_adjust_count(timesync_svc,
+ ap_frame_time);
+ gb_timesync_store_ktime(timesync_svc,
+ timesync_svc->strobe_data[i].ts,
+ ap_frame_time);
+
+ pr_debug("adjust %s local %llu svc %llu delta %llu\n",
+ timesync_svc->offset_down ? "down" : "up",
+ timesync_svc->strobe_data[i].frame_time,
+ frame_time[i], delta);
+ }
+ }
+}
+
+static void gb_timesync_teardown(struct gb_timesync_svc *timesync_svc)
+{
+ struct gb_timesync_interface *timesync_interface;
+ struct gb_svc *svc = timesync_svc->svc;
+ struct gb_interface *interface;
+ struct gb_host_device *hd;
+ int ret;
+
+ list_for_each_entry(timesync_interface,
+ ×ync_svc->interface_list, list) {
+ interface = timesync_interface->interface;
+ ret = gb_interface_timesync_disable(interface);
+ if (ret) {
+ dev_err(&interface->dev,
+ "interface timesync_disable %d\n", ret);
+ }
+ }
+
+ hd = timesync_svc->timesync_hd->hd;
+ ret = hd->driver->timesync_disable(hd);
+ if (ret < 0) {
+ dev_err(&hd->dev, "host timesync_disable %d\n",
+ ret);
+ }
+
+ gb_svc_timesync_wake_pins_release(svc);
+ gb_svc_timesync_disable(svc);
+ gb_timesync_platform_unlock_bus();
+
+ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INACTIVE);
+}
+
+static void gb_timesync_platform_lock_bus_fail(struct gb_timesync_svc
+ *timesync_svc, int ret)
+{
+ if (ret == -EAGAIN) {
+ gb_timesync_set_state(timesync_svc, timesync_svc->state);
+ } else {
+ pr_err("Failed to lock timesync bus %d\n", ret);
+ gb_timesync_set_state(timesync_svc, GB_TIMESYNC_STATE_INACTIVE);
+ }
+}
+
+static void gb_timesync_enable(struct gb_timesync_svc *timesync_svc)
+{
+ struct gb_svc *svc = timesync_svc->svc;
+ struct gb_host_device *hd;
+ struct gb_timesync_interface *timesync_interface;
+ struct gb_interface *interface;
+ u64 init_frame_time;
+ unsigned long clock_rate = gb_timesync_clock_rate;
+ int ret;
+
+ /*
+ * Get access to the wake pins in the AP and SVC
+ * Release these pins either in gb_timesync_teardown() or in
+ * gb_timesync_authoritative()
+ */
+ ret = gb_timesync_platform_lock_bus(timesync_svc);
+ if (ret < 0) {
+ gb_timesync_platform_lock_bus_fail(timesync_svc, ret);
+ return;
+ }
+ ret = gb_svc_timesync_wake_pins_acquire(svc, timesync_svc->strobe_mask);
+ if (ret) {
+ dev_err(&svc->dev,
+ "gb_svc_timesync_wake_pins_acquire %d\n", ret);
+ gb_timesync_teardown(timesync_svc);
+ return;
+ }
+
+ /* Choose an initial time in the future */
+ init_frame_time = __gb_timesync_get_frame_time(timesync_svc) + 100000UL;
+
+ /* Send enable command to all relevant participants */
+ list_for_each_entry(timesync_interface, ×ync_svc->interface_list,
+ list) {
+ interface = timesync_interface->interface;
+ ret = gb_interface_timesync_enable(interface,
+ GB_TIMESYNC_MAX_STROBES,
+ init_frame_time,
+ GB_TIMESYNC_STROBE_DELAY_US,
+ clock_rate);
+ if (ret) {
+ dev_err(&interface->dev,
+ "interface timesync_enable %d\n", ret);
+ }
+ }
+
+ hd = timesync_svc->timesync_hd->hd;
+ ret = hd->driver->timesync_enable(hd, GB_TIMESYNC_MAX_STROBES,
+ init_frame_time,
+ GB_TIMESYNC_STROBE_DELAY_US,
+ clock_rate);
+ if (ret < 0) {
+ dev_err(&hd->dev, "host timesync_enable %d\n",
+ ret);
+ }
+
+ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_WAIT_SVC);
+ ret = gb_svc_timesync_enable(svc, GB_TIMESYNC_MAX_STROBES,
+ init_frame_time,
+ GB_TIMESYNC_STROBE_DELAY_US,
+ clock_rate);
+ if (ret) {
+ dev_err(&svc->dev,
+ "gb_svc_timesync_enable %d\n", ret);
+ gb_timesync_teardown(timesync_svc);
+ return;
+ }
+
+ /* Schedule a timeout waiting for SVC to complete strobing */
+ gb_timesync_schedule_svc_timeout(timesync_svc);
+}
+
+static void gb_timesync_authoritative(struct gb_timesync_svc *timesync_svc)
+{
+ struct gb_svc *svc = timesync_svc->svc;
+ struct gb_host_device *hd;
+ struct gb_timesync_interface *timesync_interface;
+ struct gb_interface *interface;
+ u64 svc_frame_time[GB_TIMESYNC_MAX_STROBES];
+ int ret;
+
+ /* Get authoritative time from SVC and adjust local clock */
+ ret = gb_svc_timesync_authoritative(svc, svc_frame_time);
+ if (ret) {
+ dev_err(&svc->dev,
+ "gb_svc_timesync_authoritative %d\n", ret);
+ gb_timesync_teardown(timesync_svc);
+ return;
+ }
+ gb_timesync_collate_frame_time(timesync_svc, svc_frame_time);
+
+ /* Transmit authoritative time to downstream slaves */
+ hd = timesync_svc->timesync_hd->hd;
+ ret = hd->driver->timesync_authoritative(hd, svc_frame_time);
+ if (ret < 0)
+ dev_err(&hd->dev, "host timesync_authoritative %d\n", ret);
+
+ list_for_each_entry(timesync_interface,
+ ×ync_svc->interface_list, list) {
+ interface = timesync_interface->interface;
+ ret = gb_interface_timesync_authoritative(
+ interface,
+ svc_frame_time);
+ if (ret) {
+ dev_err(&interface->dev,
+ "interface timesync_authoritative %d\n", ret);
+ }
+ }
+
+ /* Release wake pins */
+ gb_svc_timesync_wake_pins_release(svc);
+ gb_timesync_platform_unlock_bus();
+
+ /* Transition to state ACTIVE */
+ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_ACTIVE);
+
+ /* Schedule a ping to verify the synchronized system time */
+ timesync_svc->print_ping = true;
+ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_PING);
+}
+
+static int __gb_timesync_get_status(struct gb_timesync_svc *timesync_svc)
+{
+ int ret = -EINVAL;
+
+ switch (timesync_svc->state) {
+ case GB_TIMESYNC_STATE_INVALID:
+ case GB_TIMESYNC_STATE_INACTIVE:
+ ret = -ENODEV;
+ break;
+ case GB_TIMESYNC_STATE_INIT:
+ case GB_TIMESYNC_STATE_WAIT_SVC:
+ case GB_TIMESYNC_STATE_AUTHORITATIVE:
+ ret = -EAGAIN;
+ break;
+ case GB_TIMESYNC_STATE_PING:
+ case GB_TIMESYNC_STATE_ACTIVE:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * This routine takes a FrameTime and derives the difference with-respect
+ * to a reference FrameTime/ktime pair. It then returns the calculated
+ * ktime based on the difference between the supplied FrameTime and
+ * the reference FrameTime.
+ *
+ * The time difference is calculated to six decimal places. Taking 19.2MHz
+ * as an example this means we have 52.083333~ nanoseconds per clock or
+ * 52083333~ femtoseconds per clock.
+ *
+ * Naively taking the count difference and converting to
+ * seconds/nanoseconds would quickly see the 0.0833 component produce
+ * noticeable errors. For example a time difference of one second would
+ * loose 19200000 * 0.08333x nanoseconds or 1.59 seconds.
+ *
+ * In contrast calculating in femtoseconds the same example of 19200000 *
+ * 0.000000083333x nanoseconds per count of error is just 1.59 nanoseconds!
+ *
+ * Continuing the example of 19.2 MHz we cap the maximum error difference
+ * at a worst-case 0.3 microseconds over a potential calculation window of
+ * abount 15 seconds, meaning you can convert a FrameTime that is <= 15
+ * seconds older/younger than the reference time with a maximum error of
+ * 0.2385 useconds. Note 19.2MHz is an example frequency not a requirement.
+ */
+static int gb_timesync_to_timespec(struct gb_timesync_svc *timesync_svc,
+ u64 frame_time, struct timespec *ts)
+{
+ unsigned long flags;
+ u64 delta_fs, counts, sec, nsec;
+ bool add;
+ int ret = 0;
+
+ memset(ts, 0x00, sizeof(*ts));
+ mutex_lock(×ync_svc->mutex);
+ spin_lock_irqsave(×ync_svc->spinlock, flags);
+
+ ret = __gb_timesync_get_status(timesync_svc);
+ if (ret)
+ goto done;
+
+ /* Support calculating ktime upwards or downwards from the reference */
+ if (frame_time < timesync_svc->ktime_data.frame_time) {
+ add = false;
+ counts = timesync_svc->ktime_data.frame_time - frame_time;
+ } else {
+ add = true;
+ counts = frame_time - timesync_svc->ktime_data.frame_time;
+ }
+
+ /* Enforce the .23 of a usecond boundary @ 19.2MHz */
+ if (counts > gb_timesync_max_ktime_diff) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Determine the time difference in femtoseconds */
+ delta_fs = counts * gb_timesync_fs_per_clock;
+
+ /* Convert to seconds */
+ sec = delta_fs;
+ do_div(sec, NSEC_PER_SEC);
+ do_div(sec, 1000000UL);
+
+ /* Get the nanosecond remainder */
+ nsec = do_div(delta_fs, sec);
+ do_div(nsec, 1000000UL);
+
+ if (add) {
+ /* Add the calculated offset - overflow nanoseconds upwards */
+ ts->tv_sec = timesync_svc->ktime_data.ts.tv_sec + sec;
+ ts->tv_nsec = timesync_svc->ktime_data.ts.tv_nsec + nsec;
+ if (ts->tv_nsec >= NSEC_PER_SEC) {
+ ts->tv_sec++;
+ ts->tv_nsec -= NSEC_PER_SEC;
+ }
+ } else {
+ /* Subtract the difference over/underflow as necessary */
+ if (nsec > timesync_svc->ktime_data.ts.tv_nsec) {
+ sec++;
+ nsec = nsec + timesync_svc->ktime_data.ts.tv_nsec;
+ nsec = do_div(nsec, NSEC_PER_SEC);
+ } else {
+ nsec = timesync_svc->ktime_data.ts.tv_nsec - nsec;
+ }
+ /* Cannot return a negative second value */
+ if (sec > timesync_svc->ktime_data.ts.tv_sec) {
+ ret = -EINVAL;
+ goto done;
+ }
+ ts->tv_sec = timesync_svc->ktime_data.ts.tv_sec - sec;
+ ts->tv_nsec = nsec;
+ }
+done:
+ spin_unlock_irqrestore(×ync_svc->spinlock, flags);
+ mutex_unlock(×ync_svc->mutex);
+ return ret;
+}
+
+static size_t gb_timesync_log_frame_time(struct gb_timesync_svc *timesync_svc,
+ char *buf, size_t buflen)
+{
+ struct gb_svc *svc = timesync_svc->svc;
+ struct gb_host_device *hd;
+ struct gb_timesync_interface *timesync_interface;
+ struct gb_interface *interface;
+ unsigned int len;
+ size_t off;
+
+ /* AP/SVC */
+ off = snprintf(buf, buflen, "%s frametime: ap=%llu %s=%llu ",
+ greybus_bus_type.name,
+ timesync_svc->ap_ping_frame_time, dev_name(&svc->dev),
+ timesync_svc->svc_ping_frame_time);
+ len = buflen - off;
+
+ /* APB/GPB */
+ if (len < buflen) {
+ hd = timesync_svc->timesync_hd->hd;
+ off += snprintf(&buf[off], len, "%s=%llu ", dev_name(&hd->dev),
+ timesync_svc->timesync_hd->ping_frame_time);
+ len = buflen - off;
+ }
+
+ list_for_each_entry(timesync_interface,
+ ×ync_svc->interface_list, list) {
+ if (len < buflen) {
+ interface = timesync_interface->interface;
+ off += snprintf(&buf[off], len, "%s=%llu ",
+ dev_name(&interface->dev),
+ timesync_interface->ping_frame_time);
+ len = buflen - off;
+ }
+ }
+ if (len < buflen)
+ off += snprintf(&buf[off], len, "\n");
+ return off;
+}
+
+static size_t gb_timesync_log_frame_ktime(struct gb_timesync_svc *timesync_svc,
+ char *buf, size_t buflen)
+{
+ struct gb_svc *svc = timesync_svc->svc;
+ struct gb_host_device *hd;
+ struct gb_timesync_interface *timesync_interface;
+ struct gb_interface *interface;
+ struct timespec ts;
+ unsigned int len;
+ size_t off;
+
+ /* AP */
+ gb_timesync_to_timespec(timesync_svc, timesync_svc->ap_ping_frame_time,
+ &ts);
+ off = snprintf(buf, buflen, "%s frametime: ap=%lu.%lu ",
+ greybus_bus_type.name, ts.tv_sec, ts.tv_nsec);
+ len = buflen - off;
+ if (len >= buflen)
+ goto done;
+
+ /* SVC */
+ gb_timesync_to_timespec(timesync_svc, timesync_svc->svc_ping_frame_time,
+ &ts);
+ off += snprintf(&buf[off], len, "%s=%lu.%lu ", dev_name(&svc->dev),
+ ts.tv_sec, ts.tv_nsec);
+ len = buflen - off;
+ if (len >= buflen)
+ goto done;
+
+ /* APB/GPB */
+ hd = timesync_svc->timesync_hd->hd;
+ gb_timesync_to_timespec(timesync_svc,
+ timesync_svc->timesync_hd->ping_frame_time,
+ &ts);
+ off += snprintf(&buf[off], len, "%s=%lu.%lu ",
+ dev_name(&hd->dev),
+ ts.tv_sec, ts.tv_nsec);
+ len = buflen - off;
+ if (len >= buflen)
+ goto done;
+
+ list_for_each_entry(timesync_interface,
+ ×ync_svc->interface_list, list) {
+ interface = timesync_interface->interface;
+ gb_timesync_to_timespec(timesync_svc,
+ timesync_interface->ping_frame_time,
+ &ts);
+ off += snprintf(&buf[off], len, "%s=%lu.%lu ",
+ dev_name(&interface->dev),
+ ts.tv_sec, ts.tv_nsec);
+ len = buflen - off;
+ if (len >= buflen)
+ goto done;
+ }
+ off += snprintf(&buf[off], len, "\n");
+done:
+ return off;
+}
+
+/*
+ * Send an SVC initiated wake 'ping' to each TimeSync participant.
+ * Get the FrameTime from each participant associated with the wake
+ * ping.
+ */
+static void gb_timesync_ping(struct gb_timesync_svc *timesync_svc)
+{
+ struct gb_svc *svc = timesync_svc->svc;
+ struct gb_host_device *hd;
+ struct gb_timesync_interface *timesync_interface;
+ struct gb_control *control;
+ u64 *ping_frame_time;
+ int ret;
+
+ /* Get access to the wake pins in the AP and SVC */
+ ret = gb_timesync_platform_lock_bus(timesync_svc);
+ if (ret < 0) {
+ gb_timesync_platform_lock_bus_fail(timesync_svc, ret);
+ return;
+ }
+ ret = gb_svc_timesync_wake_pins_acquire(svc, timesync_svc->strobe_mask);
+ if (ret) {
+ dev_err(&svc->dev,
+ "gb_svc_timesync_wake_pins_acquire %d\n", ret);
+ gb_timesync_teardown(timesync_svc);
+ return;
+ }
+
+ /* Have SVC generate a timesync ping */
+ timesync_svc->capture_ping = true;
+ timesync_svc->svc_ping_frame_time = 0;
+ ret = gb_svc_timesync_ping(svc, ×ync_svc->svc_ping_frame_time);
+ timesync_svc->capture_ping = false;
+ if (ret) {
+ dev_err(&svc->dev,
+ "gb_svc_timesync_ping %d\n", ret);
+ gb_timesync_teardown(timesync_svc);
+ return;
+ }
+
+ /* Get the ping FrameTime from each APB/GPB */
+ hd = timesync_svc->timesync_hd->hd;
+ timesync_svc->timesync_hd->ping_frame_time = 0;
+ ret = hd->driver->timesync_get_last_event(hd,
+ ×ync_svc->timesync_hd->ping_frame_time);
+ if (ret)
+ dev_err(&hd->dev, "host timesync_get_last_event %d\n", ret);
+
+ list_for_each_entry(timesync_interface,
+ ×ync_svc->interface_list, list) {
+ control = timesync_interface->interface->control;
+ timesync_interface->ping_frame_time = 0;
+ ping_frame_time = ×ync_interface->ping_frame_time;
+ ret = gb_control_timesync_get_last_event(control,
+ ping_frame_time);
+ if (ret) {
+ dev_err(×ync_interface->interface->dev,
+ "gb_control_timesync_get_last_event %d\n", ret);
+ }
+ }
+
+ /* Ping success - move to timesync active */
+ gb_svc_timesync_wake_pins_release(svc);
+ gb_timesync_platform_unlock_bus();
+ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_ACTIVE);
+}
+
+static void gb_timesync_log_ping_time(struct gb_timesync_svc *timesync_svc)
+{
+ char *buf;
+
+ if (!timesync_svc->print_ping)
+ return;
+
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (buf) {
+ gb_timesync_log_frame_time(timesync_svc, buf, PAGE_SIZE);
+ dev_dbg(×ync_svc->svc->dev, "%s", buf);
+ kfree(buf);
+ }
+}
+
+/*
+ * Perform the actual work of scheduled TimeSync logic.
+ */
+static void gb_timesync_worker(struct work_struct *work)
+{
+ struct delayed_work *delayed_work = to_delayed_work(work);
+ struct gb_timesync_svc *timesync_svc =
+ container_of(delayed_work, struct gb_timesync_svc, delayed_work);
+
+ mutex_lock(×ync_svc->mutex);
+
+ switch (timesync_svc->state) {
+ case GB_TIMESYNC_STATE_INIT:
+ gb_timesync_enable(timesync_svc);
+ break;
+
+ case GB_TIMESYNC_STATE_WAIT_SVC:
+ dev_err(×ync_svc->svc->dev,
+ "timeout SVC strobe completion %d/%d\n",
+ timesync_svc->strobe, GB_TIMESYNC_MAX_STROBES);
+ gb_timesync_teardown(timesync_svc);
+ break;
+
+ case GB_TIMESYNC_STATE_AUTHORITATIVE:
+ gb_timesync_authoritative(timesync_svc);
+ break;
+
+ case GB_TIMESYNC_STATE_PING:
+ gb_timesync_ping(timesync_svc);
+ gb_timesync_log_ping_time(timesync_svc);
+ break;
+
+ default:
+ pr_err("Invalid state %d for delayed work\n",
+ timesync_svc->state);
+ break;
+ }
+
+ mutex_unlock(×ync_svc->mutex);
+}
+
+/*
+ * Schedule a new TimeSync INIT or PING operation serialized w/r to
+ * gb_timesync_worker().
+ */
+static int gb_timesync_schedule(struct gb_timesync_svc *timesync_svc, int state)
+{
+ int ret = 0;
+
+ if (state != GB_TIMESYNC_STATE_INIT && state != GB_TIMESYNC_STATE_PING)
+ return -EINVAL;
+
+ mutex_lock(×ync_svc->mutex);
+ if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) {
+ gb_timesync_set_state_atomic(timesync_svc, state);
+ } else {
+ ret = -ENODEV;
+ }
+ mutex_unlock(×ync_svc->mutex);
+ return ret;
+}
+
+static int __gb_timesync_schedule_synchronous(
+ struct gb_timesync_svc *timesync_svc, int state)
+{
+ unsigned long flags;
+ int ret;
+
+ ret = gb_timesync_schedule(timesync_svc, state);
+ if (ret)
+ return ret;
+
+ ret = wait_event_interruptible(timesync_svc->wait_queue,
+ (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE ||
+ timesync_svc->state == GB_TIMESYNC_STATE_INACTIVE ||
+ timesync_svc->state == GB_TIMESYNC_STATE_INVALID));
+ if (ret)
+ return ret;
+
+ mutex_lock(×ync_svc->mutex);
+ spin_lock_irqsave(×ync_svc->spinlock, flags);
+
+ ret = __gb_timesync_get_status(timesync_svc);
+
+ spin_unlock_irqrestore(×ync_svc->spinlock, flags);
+ mutex_unlock(×ync_svc->mutex);
+
+ return ret;
+}
+
+static struct gb_timesync_svc *gb_timesync_find_timesync_svc(
+ struct gb_host_device *hd)
+{
+ struct gb_timesync_svc *timesync_svc;
+
+ list_for_each_entry(timesync_svc, &gb_timesync_svc_list, list) {
+ if (timesync_svc->svc == hd->svc)
+ return timesync_svc;
+ }
+ return NULL;
+}
+
+static struct gb_timesync_interface *gb_timesync_find_timesync_interface(
+ struct gb_timesync_svc *timesync_svc,
+ struct gb_interface *interface)
+{
+ struct gb_timesync_interface *timesync_interface;
+
+ list_for_each_entry(timesync_interface, ×ync_svc->interface_list, list) {
+ if (timesync_interface->interface == interface)
+ return timesync_interface;
+ }
+ return NULL;
+}
+
+int gb_timesync_schedule_synchronous(struct gb_interface *interface)
+{
+ int ret;
+ struct gb_timesync_svc *timesync_svc;
+ int retries;
+
+ if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC))
+ return 0;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ for (retries = 0; retries < GB_TIMESYNC_MAX_RETRIES; retries++) {
+ timesync_svc = gb_timesync_find_timesync_svc(interface->hd);
+ if (!timesync_svc) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = __gb_timesync_schedule_synchronous(timesync_svc,
+ GB_TIMESYNC_STATE_INIT);
+ if (!ret)
+ break;
+ }
+ if (ret && retries == GB_TIMESYNC_MAX_RETRIES)
+ ret = -ETIMEDOUT;
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gb_timesync_schedule_synchronous);
+
+void gb_timesync_schedule_asynchronous(struct gb_interface *interface)
+{
+ struct gb_timesync_svc *timesync_svc;
+
+ if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC))
+ return;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ timesync_svc = gb_timesync_find_timesync_svc(interface->hd);
+ if (!timesync_svc)
+ goto done;
+
+ gb_timesync_schedule(timesync_svc, GB_TIMESYNC_STATE_INIT);
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+ return;
+}
+EXPORT_SYMBOL_GPL(gb_timesync_schedule_asynchronous);
+
+static ssize_t gb_timesync_ping_read(struct file *file, char __user *ubuf,
+ size_t len, loff_t *offset, bool ktime)
+{
+ struct gb_timesync_svc *timesync_svc = file->f_inode->i_private;
+ char *buf;
+ ssize_t ret = 0;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ mutex_lock(×ync_svc->mutex);
+ if (list_empty(×ync_svc->interface_list))
+ ret = -ENODEV;
+ timesync_svc->print_ping = false;
+ mutex_unlock(×ync_svc->mutex);
+ if (ret)
+ goto done;
+
+ ret = __gb_timesync_schedule_synchronous(timesync_svc,
+ GB_TIMESYNC_STATE_PING);
+ if (ret)
+ goto done;
+
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (ktime)
+ ret = gb_timesync_log_frame_ktime(timesync_svc, buf, PAGE_SIZE);
+ else
+ ret = gb_timesync_log_frame_time(timesync_svc, buf, PAGE_SIZE);
+ if (ret > 0)
+ ret = simple_read_from_buffer(ubuf, len, offset, buf, ret);
+ kfree(buf);
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+ return ret;
+}
+
+static ssize_t gb_timesync_ping_read_frame_time(struct file *file,
+ char __user *buf,
+ size_t len, loff_t *offset)
+{
+ return gb_timesync_ping_read(file, buf, len, offset, false);
+}
+
+static ssize_t gb_timesync_ping_read_frame_ktime(struct file *file,
+ char __user *buf,
+ size_t len, loff_t *offset)
+{
+ return gb_timesync_ping_read(file, buf, len, offset, true);
+}
+
+static const struct file_operations gb_timesync_debugfs_frame_time_ops = {
+ .read = gb_timesync_ping_read_frame_time,
+};
+
+static const struct file_operations gb_timesync_debugfs_frame_ktime_ops = {
+ .read = gb_timesync_ping_read_frame_ktime,
+};
+
+static int gb_timesync_hd_add(struct gb_timesync_svc *timesync_svc,
+ struct gb_host_device *hd)
+{
+ struct gb_timesync_host_device *timesync_hd;
+
+ timesync_hd = kzalloc(sizeof(*timesync_hd), GFP_KERNEL);
+ if (!timesync_hd)
+ return -ENOMEM;
+
+ WARN_ON(timesync_svc->timesync_hd);
+ timesync_hd->hd = hd;
+ timesync_svc->timesync_hd = timesync_hd;
+
+ return 0;
+}
+
+static void gb_timesync_hd_remove(struct gb_timesync_svc *timesync_svc,
+ struct gb_host_device *hd)
+{
+ if (timesync_svc->timesync_hd->hd == hd) {
+ kfree(timesync_svc->timesync_hd);
+ timesync_svc->timesync_hd = NULL;
+ return;
+ }
+ WARN_ON(1);
+}
+
+int gb_timesync_svc_add(struct gb_svc *svc)
+{
+ struct gb_timesync_svc *timesync_svc;
+ int ret;
+
+ timesync_svc = kzalloc(sizeof(*timesync_svc), GFP_KERNEL);
+ if (!timesync_svc)
+ return -ENOMEM;
+
+ timesync_svc->work_queue =
+ create_singlethread_workqueue("gb-timesync-work_queue");
+
+ if (!timesync_svc->work_queue) {
+ kfree(timesync_svc);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ INIT_LIST_HEAD(×ync_svc->interface_list);
+ INIT_DELAYED_WORK(×ync_svc->delayed_work, gb_timesync_worker);
+ mutex_init(×ync_svc->mutex);
+ spin_lock_init(×ync_svc->spinlock);
+ init_waitqueue_head(×ync_svc->wait_queue);
+
+ timesync_svc->svc = svc;
+ timesync_svc->frame_time_offset = 0;
+ timesync_svc->capture_ping = false;
+ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INACTIVE);
+
+ timesync_svc->frame_time_dentry =
+ debugfs_create_file("frame-time", S_IRUGO, svc->debugfs_dentry,
+ timesync_svc,
+ &gb_timesync_debugfs_frame_time_ops);
+ timesync_svc->frame_ktime_dentry =
+ debugfs_create_file("frame-ktime", S_IRUGO, svc->debugfs_dentry,
+ timesync_svc,
+ &gb_timesync_debugfs_frame_ktime_ops);
+
+ list_add(×ync_svc->list, &gb_timesync_svc_list);
+ ret = gb_timesync_hd_add(timesync_svc, svc->hd);
+ if (ret) {
+ list_del(×ync_svc->list);
+ debugfs_remove(timesync_svc->frame_ktime_dentry);
+ debugfs_remove(timesync_svc->frame_time_dentry);
+ destroy_workqueue(timesync_svc->work_queue);
+ kfree(timesync_svc);
+ goto done;
+ }
+
+ init_timer(×ync_svc->ktime_timer);
+ timesync_svc->ktime_timer.function = gb_timesync_ktime_timer_fn;
+ timesync_svc->ktime_timer.expires = jiffies + GB_TIMESYNC_KTIME_UPDATE;
+ timesync_svc->ktime_timer.data = (unsigned long)timesync_svc;
+ add_timer(×ync_svc->ktime_timer);
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gb_timesync_svc_add);
+
+void gb_timesync_svc_remove(struct gb_svc *svc)
+{
+ struct gb_timesync_svc *timesync_svc;
+ struct gb_timesync_interface *timesync_interface;
+ struct gb_timesync_interface *next;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ timesync_svc = gb_timesync_find_timesync_svc(svc->hd);
+ if (!timesync_svc)
+ goto done;
+
+ cancel_delayed_work_sync(×ync_svc->delayed_work);
+
+ mutex_lock(×ync_svc->mutex);
+
+ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INVALID);
+ del_timer_sync(×ync_svc->ktime_timer);
+ gb_timesync_teardown(timesync_svc);
+
+ gb_timesync_hd_remove(timesync_svc, svc->hd);
+ list_for_each_entry_safe(timesync_interface, next,
+ ×ync_svc->interface_list, list) {
+ list_del(×ync_interface->list);
+ kfree(timesync_interface);
+ }
+ debugfs_remove(timesync_svc->frame_ktime_dentry);
+ debugfs_remove(timesync_svc->frame_time_dentry);
+ destroy_workqueue(timesync_svc->work_queue);
+ list_del(×ync_svc->list);
+
+ mutex_unlock(×ync_svc->mutex);
+
+ kfree(timesync_svc);
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+}
+EXPORT_SYMBOL_GPL(gb_timesync_svc_remove);
+
+/*
+ * Add a Greybus Interface to the set of TimeSync Interfaces.
+ */
+int gb_timesync_interface_add(struct gb_interface *interface)
+{
+ struct gb_timesync_svc *timesync_svc;
+ struct gb_timesync_interface *timesync_interface;
+ int ret = 0;
+
+ if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC))
+ return 0;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ timesync_svc = gb_timesync_find_timesync_svc(interface->hd);
+ if (!timesync_svc) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ timesync_interface = kzalloc(sizeof(*timesync_interface), GFP_KERNEL);
+ if (!timesync_interface) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ mutex_lock(×ync_svc->mutex);
+ timesync_interface->interface = interface;
+ list_add(×ync_interface->list, ×ync_svc->interface_list);
+ timesync_svc->strobe_mask |= 1 << interface->interface_id;
+ mutex_unlock(×ync_svc->mutex);
+
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gb_timesync_interface_add);
+
+/*
+ * Remove a Greybus Interface from the set of TimeSync Interfaces.
+ */
+void gb_timesync_interface_remove(struct gb_interface *interface)
+{
+ struct gb_timesync_svc *timesync_svc;
+ struct gb_timesync_interface *timesync_interface;
+
+ if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC))
+ return;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ timesync_svc = gb_timesync_find_timesync_svc(interface->hd);
+ if (!timesync_svc)
+ goto done;
+
+ timesync_interface = gb_timesync_find_timesync_interface(timesync_svc,
+ interface);
+ if (!timesync_interface)
+ goto done;
+
+ mutex_lock(×ync_svc->mutex);
+ timesync_svc->strobe_mask &= ~(1 << interface->interface_id);
+ list_del(×ync_interface->list);
+ kfree(timesync_interface);
+ mutex_unlock(×ync_svc->mutex);
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+}
+EXPORT_SYMBOL_GPL(gb_timesync_interface_remove);
+
+/*
+ * Give the authoritative FrameTime to the calling function. Returns zero if we
+ * are not in GB_TIMESYNC_STATE_ACTIVE.
+ */
+static u64 gb_timesync_get_frame_time(struct gb_timesync_svc *timesync_svc)
+{
+ unsigned long flags;
+ u64 ret;
+
+ spin_lock_irqsave(×ync_svc->spinlock, flags);
+ if (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE)
+ ret = __gb_timesync_get_frame_time(timesync_svc);
+ else
+ ret = 0;
+ spin_unlock_irqrestore(×ync_svc->spinlock, flags);
+ return ret;
+}
+
+u64 gb_timesync_get_frame_time_by_interface(struct gb_interface *interface)
+{
+ struct gb_timesync_svc *timesync_svc;
+ u64 ret = 0;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ timesync_svc = gb_timesync_find_timesync_svc(interface->hd);
+ if (!timesync_svc)
+ goto done;
+
+ ret = gb_timesync_get_frame_time(timesync_svc);
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gb_timesync_get_frame_time_by_interface);
+
+u64 gb_timesync_get_frame_time_by_svc(struct gb_svc *svc)
+{
+ struct gb_timesync_svc *timesync_svc;
+ u64 ret = 0;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ timesync_svc = gb_timesync_find_timesync_svc(svc->hd);
+ if (!timesync_svc)
+ goto done;
+
+ ret = gb_timesync_get_frame_time(timesync_svc);
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gb_timesync_get_frame_time_by_svc);
+
+/* Incrementally updates the conversion base from FrameTime to ktime */
+static void gb_timesync_ktime_timer_fn(unsigned long data)
+{
+ struct gb_timesync_svc *timesync_svc =
+ (struct gb_timesync_svc *)data;
+ unsigned long flags;
+ u64 frame_time;
+ struct timespec ts;
+
+ spin_lock_irqsave(×ync_svc->spinlock, flags);
+
+ if (timesync_svc->state != GB_TIMESYNC_STATE_ACTIVE)
+ goto done;
+
+ ktime_get_ts(&ts);
+ frame_time = __gb_timesync_get_frame_time(timesync_svc);
+ gb_timesync_store_ktime(timesync_svc, ts, frame_time);
+
+done:
+ spin_unlock_irqrestore(×ync_svc->spinlock, flags);
+ mod_timer(×ync_svc->ktime_timer,
+ jiffies + GB_TIMESYNC_KTIME_UPDATE);
+}
+
+int gb_timesync_to_timespec_by_svc(struct gb_svc *svc, u64 frame_time,
+ struct timespec *ts)
+{
+ struct gb_timesync_svc *timesync_svc;
+ int ret = 0;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ timesync_svc = gb_timesync_find_timesync_svc(svc->hd);
+ if (!timesync_svc) {
+ ret = -ENODEV;
+ goto done;
+ }
+ ret = gb_timesync_to_timespec(timesync_svc, frame_time, ts);
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gb_timesync_to_timespec_by_svc);
+
+int gb_timesync_to_timespec_by_interface(struct gb_interface *interface,
+ u64 frame_time, struct timespec *ts)
+{
+ struct gb_timesync_svc *timesync_svc;
+ int ret = 0;
+
+ mutex_lock(&gb_timesync_svc_list_mutex);
+ timesync_svc = gb_timesync_find_timesync_svc(interface->hd);
+ if (!timesync_svc) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = gb_timesync_to_timespec(timesync_svc, frame_time, ts);
+done:
+ mutex_unlock(&gb_timesync_svc_list_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gb_timesync_to_timespec_by_interface);
+
+void gb_timesync_irq(struct gb_timesync_svc *timesync_svc)
+{
+ unsigned long flags;
+ u64 strobe_time;
+ bool strobe_is_ping = true;
+ struct timespec ts;
+
+ ktime_get_ts(&ts);
+ strobe_time = __gb_timesync_get_frame_time(timesync_svc);
+
+ spin_lock_irqsave(×ync_svc->spinlock, flags);
+
+ if (timesync_svc->state == GB_TIMESYNC_STATE_PING) {
+ if (!timesync_svc->capture_ping)
+ goto done_nolog;
+ timesync_svc->ap_ping_frame_time = strobe_time;
+ goto done_log;
+ } else if (timesync_svc->state != GB_TIMESYNC_STATE_WAIT_SVC) {
+ goto done_nolog;
+ }
+
+ timesync_svc->strobe_data[timesync_svc->strobe].frame_time = strobe_time;
+ timesync_svc->strobe_data[timesync_svc->strobe].ts = ts;
+
+ if (++timesync_svc->strobe == GB_TIMESYNC_MAX_STROBES) {
+ gb_timesync_set_state(timesync_svc,
+ GB_TIMESYNC_STATE_AUTHORITATIVE);
+ }
+ strobe_is_ping = false;
+done_log:
+ trace_gb_timesync_irq(strobe_is_ping, timesync_svc->strobe,
+ GB_TIMESYNC_MAX_STROBES, strobe_time);
+done_nolog:
+ spin_unlock_irqrestore(×ync_svc->spinlock, flags);
+}
+EXPORT_SYMBOL(gb_timesync_irq);
+
+int __init gb_timesync_init(void)
+{
+ int ret = 0;
+
+ ret = gb_timesync_platform_init();
+ if (ret) {
+ pr_err("timesync platform init fail!\n");
+ return ret;
+ }
+
+ gb_timesync_clock_rate = gb_timesync_platform_get_clock_rate();
+
+ /* Calculate nanoseconds and femtoseconds per clock */
+ gb_timesync_fs_per_clock = FSEC_PER_SEC;
+ do_div(gb_timesync_fs_per_clock, gb_timesync_clock_rate);
+ gb_timesync_ns_per_clock = NSEC_PER_SEC;
+ do_div(gb_timesync_ns_per_clock, gb_timesync_clock_rate);
+
+ /* Calculate the maximum number of clocks we will convert to ktime */
+ gb_timesync_max_ktime_diff =
+ GB_TIMESYNC_MAX_KTIME_CONVERSION * gb_timesync_clock_rate;
+
+ pr_info("Time-Sync @ %lu Hz max ktime conversion +/- %d seconds\n",
+ gb_timesync_clock_rate, GB_TIMESYNC_MAX_KTIME_CONVERSION);
+ return 0;
+}
+
+void gb_timesync_exit(void)
+{
+ gb_timesync_platform_exit();
+}
--- /dev/null
+++ b/drivers/greybus/timesync.h
@@ -0,0 +1,45 @@
+/*
+ * TimeSync API driver.
+ *
+ * Copyright 2016 Google Inc.
+ * Copyright 2016 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef __TIMESYNC_H
+#define __TIMESYNC_H
+
+struct gb_svc;
+struct gb_interface;
+struct gb_timesync_svc;
+
+/* Platform */
+u64 gb_timesync_platform_get_counter(void);
+u32 gb_timesync_platform_get_clock_rate(void);
+int gb_timesync_platform_lock_bus(struct gb_timesync_svc *pdata);
+void gb_timesync_platform_unlock_bus(void);
+
+int gb_timesync_platform_init(void);
+void gb_timesync_platform_exit(void);
+
+/* Core API */
+int gb_timesync_interface_add(struct gb_interface *interface);
+void gb_timesync_interface_remove(struct gb_interface *interface);
+int gb_timesync_svc_add(struct gb_svc *svc);
+void gb_timesync_svc_remove(struct gb_svc *svc);
+
+u64 gb_timesync_get_frame_time_by_interface(struct gb_interface *interface);
+u64 gb_timesync_get_frame_time_by_svc(struct gb_svc *svc);
+int gb_timesync_to_timespec_by_svc(struct gb_svc *svc, u64 frame_time,
+ struct timespec *ts);
+int gb_timesync_to_timespec_by_interface(struct gb_interface *interface,
+ u64 frame_time, struct timespec *ts);
+
+int gb_timesync_schedule_synchronous(struct gb_interface *intf);
+void gb_timesync_schedule_asynchronous(struct gb_interface *intf);
+void gb_timesync_irq(struct gb_timesync_svc *timesync_svc);
+int gb_timesync_init(void);
+void gb_timesync_exit(void);
+
+#endif /* __TIMESYNC_H */
--- /dev/null
+++ b/drivers/greybus/timesync_platform.c
@@ -0,0 +1,77 @@
+/*
+ * TimeSync API driver.
+ *
+ * Copyright 2016 Google Inc.
+ * Copyright 2016 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ *
+ * This code reads directly from an ARMv7 memory-mapped timer that lives in
+ * MMIO space. Since this counter lives inside of MMIO space its shared between
+ * cores and that means we don't have to worry about issues like TSC on x86
+ * where each time-stamp-counter (TSC) is local to a particular core.
+ *
+ * Register-level access code is based on
+ * drivers/clocksource/arm_arch_timer.c
+ */
+#include <linux/cpufreq.h>
+#include <linux/of_platform.h>
+
+#include "greybus.h"
+#include "arche_platform.h"
+
+static u32 gb_timesync_clock_frequency;
+int (*arche_platform_change_state_cb)(enum arche_platform_state state,
+ struct gb_timesync_svc *pdata);
+EXPORT_SYMBOL_GPL(arche_platform_change_state_cb);
+
+u64 gb_timesync_platform_get_counter(void)
+{
+ return (u64)get_cycles();
+}
+
+u32 gb_timesync_platform_get_clock_rate(void)
+{
+ if (unlikely(!gb_timesync_clock_frequency))
+ return cpufreq_get(0);
+
+ return gb_timesync_clock_frequency;
+}
+
+int gb_timesync_platform_lock_bus(struct gb_timesync_svc *pdata)
+{
+ return arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_TIME_SYNC,
+ pdata);
+}
+
+void gb_timesync_platform_unlock_bus(void)
+{
+ arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_ACTIVE, NULL);
+}
+
+static const struct of_device_id arch_timer_of_match[] = {
+ { .compatible = "google,greybus-frame-time-counter", },
+ {},
+};
+
+int __init gb_timesync_platform_init(void)
+{
+ struct device_node *np;
+
+ np = of_find_matching_node(NULL, arch_timer_of_match);
+ if (!np) {
+ /* Tolerate not finding to allow BBB etc to continue */
+ pr_warn("Unable to find a compatible ARMv7 timer\n");
+ return 0;
+ }
+
+ if (of_property_read_u32(np, "clock-frequency",
+ &gb_timesync_clock_frequency)) {
+ pr_err("Unable to find timer clock-frequency\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+void gb_timesync_platform_exit(void) {}
^ permalink raw reply
* [PATCH v3 00/14] Clock improvement for video playback
From: Stephen Boyd @ 2016-09-14 18:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1472473626-15398-1-git-send-email-gabriel.fernandez@st.com>
On 08/29, gabriel.fernandez at st.com wrote:
> From: Gabriel Fernandez <gabriel.fernandez@st.com>
>
> v3:
> - Rebase to v4.8-rc1
> - Tipo fix in st,clkgen-pll.txt
> - Add Ack of Peter for the series
> - Add missed patch: "ARM: DT: STiH4xx: Simplify clock binding of STiH4xx platforms"
>
> v2:
> - Simpliflication of clock binding
> remark from Rob https://lkml.org/lkml/2016/5/25/492
> - Suppression of stih415-416 support for the clocks (in order
> to help simplification of clock binding)
> (others patchs for the machine and drivers will come)
>
> This serie allows to increase video resolutions and make audio
> adjustment during a video playback.
>
> Gabriel Fernandez (14):
> drivers: clk: st: Remove stih415-416 clock support
> drivers: clk: st: Simplify clock binding of STiH4xx platforms
> ARM: DT: STiH4xx: Simplify clock binding of STiH4xx platforms
> drivers: clk: st: Add fs660c32 synthesizer algorithm
> drivers: clk: st: Add clock propagation for audio clocks
> drivers: clk: st: Handle clk synchronous mode for video clocks
> ARM: DT: STiH407: Enable clock propagation for audio clocks
> ARM: DT: STiH410: Enable clock propagation for audio clocks
> ARM: DT: STiH418: Enable clock propagation for audio clocks
> ARM: DT: STiH407: Enable synchronous clock mode for video clocks
> ARM: DT: STiH410: Enable synchronous clock mode for video clocks
> ARM: DT: STiH418: Enable synchronous clock mode for video clocks
> ARM: DT: STi: STiH407: clock configuration to address 720p and 1080p
> ARM: DT: STi: STiH410: clock configuration to address 720p and 1080p
The order of patches intermingles clk changes and dts changes.
I'd prefer to not take any patches for dts through the clk tree,
so can those be deferred to an arm-soc PR? Assuming that works,
I'd just pick 1-2, and 4-6 into the clk tree and the rest can go
on top through arm-soc.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH v2 1/6] usb: dwc2: add support for Meson8b and GXBB SoCs
From: Kevin Hilman @ 2016-09-14 18:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <40e49082-3635-5b99-863b-f51f97258864@synopsys.com>
On Wed, Sep 14, 2016 at 11:26 AM, John Youn <John.Youn@synopsys.com> wrote:
> On 9/14/2016 11:17 AM, Kevin Hilman wrote:
>> On Wed, Sep 14, 2016 at 11:12 AM, John Youn <John.Youn@synopsys.com> wrote:
>>> On 9/14/2016 9:11 AM, Kevin Hilman wrote:
>>>> Hi John,
>>>>
>>>> Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes:
>>>>
>>>>> From: Jerome Brunet <jbrunet@baylibre.com>
>>>>>
>>>>> Add compatible strings for amlogic Meson8b and GXBB SoCs with the
>>>>> corresponding configuration parameters.
>>>>>
>>>>> Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
>>>>> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>>>>
>>>> Are you OK with adding this platform for v4.9? I know you mentioned
>>>> you're working on new bindings to replace the current way, but since
>>>> that hasn't been posted AFAICT, it would be nice to get this merged now
>>>> and we can help test the new bindings when you're ready.
>>>>
>>>> If you're OK with that, and with your Ack, I can take merge the driver
>>>> changes through the arm-soc tree along with the rest of the DT patches.
>>>>
>>>
>>> Sure. Unfortunately, I wasn't able to complete it for 4.9.
>>>
>>> You can add my acked-by on this:
>>>
>>> Acked-by: John Youn <johnyoun@synopsys.com>
>>
>> Great, thanks! I assume you're OK with PATCH 2/2 for the bindings as
>> well (at least for now until you rework them?)
>>
>> Kevin
>>
>
> Do you mean PATCH 2/6 from this series?
Yes.
> It doesn't seem to add or remove any bindings in dwc2 so I'm fine with
> it.
Thanks.
Kevin
^ permalink raw reply
* [GIT PULL] Amlogic driver updates for v4.9, 2nd round
From: Kevin Hilman @ 2016-09-14 19:02 UTC (permalink / raw)
To: linux-arm-kernel
Hi Arnd, Olof,
Below is the 2nd round of driver updates for Amlogic SoCs. All patches
have acks from appropriate maintainers.
There is a dependency on the immutable clk-meson-gxbb branch of the
clock tree, which is merged into this branch. The reason is that clock
maintainers wanted me to start taking the simple patcches that just
expose clock IDs for use by DT files.
Due to the dependency, I recommend adding this to next/late so it will
go upstream after the clock dependencies.
I'll also have a round of dt64 changes coming shortly that depend on
this branch (due to the clock IDs.)
Kevin
The following changes since commit ad855eae6caf0d1dd17bce5bcd8e07759adc9903:
nvmem: amlogic: Add Amlogic Meson EFUSE driver (2016-09-01 14:24:21 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic.git tags/amlogic-drivers-2
for you to fetch changes up to dfdd7d4af6ebee027be7bf2636b2314937948da6:
clk: gxbb: expose i2c clocks (2016-09-14 11:24:04 -0700)
----------------------------------------------------------------
Amlogic driver updates for v4.9, 2nd round
- media: update IR support for newer SoCs
- firmware: add secure monitor driver
- net: new stmmac glue driver
- usb: udd DWC2 support for meson-gxbb
- clocks: expose more clock IDs for use by DT
- DT binding updates
----------------------------------------------------------------
Alexander M?ller (6):
clk: meson: Rename meson8b-clkc.c to reflect gxbb naming convention
meson: clk: Move register definitions to meson8b.h
meson: clk: Rename register names according to Amlogic datasheet
clk: meson: Copy meson8b CLKID defines to private header file
gxbb: clk: Adjust MESON_GATE macro to be shared with meson8b
meson: clk: Add support for clock gates
Jerome Brunet (3):
usb: dwc2: add support for Meson8b and GXBB SoCs
clk: gxbb: expose spifc clock
clk: gxbb: expose i2c clocks
Joachim Eastwood (1):
stmmac: introduce get_stmmac_bsp_priv() helper
Kevin Hilman (2):
clk: gxbb: add MMC gate clocks, and expose for DT
Merge branch 'clk-meson-gxbb' of git://git.kernel.org/.../clk/linux into v4.8/drivers
Martin Blumenstingl (6):
net: dt-bindings: Document the new Meson8b and GXBB DWMAC bindings
net: stmmac: add a glue driver for the Amlogic Meson 8b / GXBB DWMAC
net: stmmac: update the module description of the dwmac-meson driver
Documentation: dt-bindings: Add documentation for the Meson USB2 PHYs
clk: gxbb: expose MPLL2 clock for use by DT
clk: gxbb: expose USB clocks
Michael Turquette (1):
Merge remote-tracking branch 'clk/clk-meson-gxbb-ao' into clk-meson-gxbb
Neil Armstrong (3):
dt-bindings: clock: reset: Add GXBB AO Clock and Reset Bindings
clk: meson: Add GXBB AO Clock and Reset controller driver
clk: meson-gxbb: Export PWM related clocks for DT
Wei Yongjun (1):
clk: meson: Fix invalid use of sizeof in gxbb_aoclkc_probe()
Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt | 45 ++++++++++++
Documentation/devicetree/bindings/net/meson-dwmac.txt | 45 +++++++++---
Documentation/devicetree/bindings/phy/meson-usb2-phy.txt | 27 ++++++++
Documentation/devicetree/bindings/usb/dwc2.txt | 2 +
drivers/clk/meson/Makefile | 4 +-
drivers/clk/meson/clkc.h | 2 +-
drivers/clk/meson/gxbb-aoclk.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/meson/gxbb.c | 171 +++++++++++++++++++++++----------------------
drivers/clk/meson/gxbb.h | 31 +++++----
drivers/clk/meson/{meson8b-clkc.c => meson8b.c} | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
drivers/clk/meson/meson8b.h | 151 ++++++++++++++++++++++++++++++++++++++++
drivers/net/ethernet/stmicro/stmmac/Kconfig | 6 +-
drivers/net/ethernet/stmicro/stmmac/Makefile | 2 +-
drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c | 4 +-
drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h | 8 +++
drivers/usb/dwc2/platform.c | 34 +++++++++
include/dt-bindings/clock/gxbb-aoclkc.h | 66 ++++++++++++++++++
include/dt-bindings/clock/gxbb-clkc.h | 16 +++++
include/dt-bindings/clock/meson8b-clkc.h | 2 -
include/dt-bindings/reset/gxbb-aoclkc.h | 66 ++++++++++++++++++
21 files changed, 1346 insertions(+), 144 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt
create mode 100644 Documentation/devicetree/bindings/phy/meson-usb2-phy.txt
create mode 100644 drivers/clk/meson/gxbb-aoclk.c
rename drivers/clk/meson/{meson8b-clkc.c => meson8b.c} (51%)
create mode 100644 drivers/clk/meson/meson8b.h
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
create mode 100644 include/dt-bindings/clock/gxbb-aoclkc.h
create mode 100644 include/dt-bindings/reset/gxbb-aoclkc.h
^ permalink raw reply
* [GIT PULL] Greybus driver subsystem for 4.9-rc1
From: Joe Perches @ 2016-09-14 19:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160914182952.GA21615@kroah.com>
On Wed, 2016-09-14 at 20:29 +0200, Greg KH wrote:
trivial note:
> +static size_t gb_timesync_log_frame_time(struct gb_timesync_svc *timesync_svc,
> + char *buf, size_t buflen)
> +{
> + struct gb_svc *svc = timesync_svc->svc;
> + struct gb_host_device *hd;
> + struct gb_timesync_interface *timesync_interface;
> + struct gb_interface *interface;
> + unsigned int len;
> + size_t off;
> +
> + /* AP/SVC */
> + off = snprintf(buf, buflen, "%s frametime: ap=%llu %s=%llu ",
> + greybus_bus_type.name,
> + timesync_svc->ap_ping_frame_time, dev_name(&svc->dev),
> + timesync_svc->svc_ping_frame_time);
> + len = buflen - off;
> +
> + /* APB/GPB */
> + if (len < buflen) {
> + hd = timesync_svc->timesync_hd->hd;
> + off += snprintf(&buf[off], len, "%s=%llu ", dev_name(&hd->dev),
> + timesync_svc->timesync_hd->ping_frame_time);
> + len = buflen - off;
> + }
> +
> + list_for_each_entry(timesync_interface,
> + ×ync_svc->interface_list, list) {
> + if (len < buflen) {
> + interface = timesync_interface->interface;
> + off += snprintf(&buf[off], len, "%s=%llu ",
> + dev_name(&interface->dev),
> + timesync_interface->ping_frame_time);
> + len = buflen - off;
> + }
> + }
> + if (len < buflen)
> + off += snprintf(&buf[off], len, "\n");
> + return off;
> +}
The unnecessary trailing blank can be avoided by converting
snprintf(, "format ', ...);
for (...)
snprintf(, "format ", ...);
snprintf(, "\n");
to
snprintf(, "format', ...);
for (...)
snprintf(, " format", ...);
snprintf(, "\n");
>
^ permalink raw reply
* [PATCH 1/6] PCI: xilinx: Keep both legacy and MSI interrupt references.
From: Bjorn Helgaas @ 2016-09-14 19:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8520D5D51A55D047800579B094147198258D6418@XAP-PVEXMBX01.xlnx.xilinx.com>
On Wed, Sep 14, 2016 at 03:32:44PM +0000, Bharat Kumar Gogada wrote:
> On Thu, Sep 01, 2016 at 03:44:41PM +0530, Bharat Kumar Gogada wrote:
> > When built with MSI support the legacy domain reference is being
> > overwritten with MSI.
> > Instead creating two separate domains for MSI and legacy interrupts.
> >
> > Signed-off-by: Bharat Kumar Gogada <bharatku@xilinx.com>
>
> Applied all these (except [5/6]) with Michal's ack to pci/host-xilinx for v4.9, thanks!
>
> I already applied Po Liu's patch ("PCI: xilinx: Relax device number checking to allow SR-IOV") to pci/virtualization. This is identical to [5/6], so I dropped [5/6] to avoid a conflict.
>
> Thanks Bjorn.
> Some how I couldn't find Po Liu's patch, can you provide git link of patch that was applied.
Sure, here it is: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=pci/virtualization&id=8e7ca8ca5fd8a3242289105723c429733be8b73b
^ permalink raw reply
* [PATCH v3 1/9] mtd:fsl-quadspi:use the property fields of SPI-NOR
From: Han Xu @ 2016-09-14 19:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471505884-33996-1-git-send-email-B56489@freescale.com>
On Thu, Aug 18, 2016 at 2:37 AM, Yunhui Cui <B56489@freescale.com> wrote:
> We can get the read/write/erase opcode from the spi nor framework
> directly. This patch uses the information stored in the SPI-NOR to
> remove the hardcode in the fsl_qspi_init_lut().
>
> Signed-off-by: Yunhui Cui <B56489@freescale.com>
> Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
> ---
> drivers/mtd/spi-nor/fsl-quadspi.c | 40 ++++++++++++---------------------------
> 1 file changed, 12 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 5c82e4e..5ad6402 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -373,9 +373,13 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> void __iomem *base = q->iobase;
> int rxfifo = q->devtype_data->rxfifo;
> u32 lut_base;
> - u8 cmd, addrlen, dummy;
> int i;
>
> + struct spi_nor *nor = &q->nor[0];
> + u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
> + u8 read_op = nor->read_opcode;
> + u8 read_dm = nor->read_dummy;
> +
> fsl_qspi_unlock_lut(q);
>
> /* Clear all the LUT table */
> @@ -385,20 +389,10 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> /* Quad Read */
> lut_base = SEQID_QUAD_READ * 4;
>
> - if (q->nor_size <= SZ_16M) {
> - cmd = SPINOR_OP_READ_1_1_4;
> - addrlen = ADDR24BIT;
> - dummy = 8;
> - } else {
> - /* use the 4-byte address */
> - cmd = SPINOR_OP_READ_1_1_4;
> - addrlen = ADDR32BIT;
> - dummy = 8;
> - }
> -
> - qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
> + qspi_writel(q, LUT0(CMD, PAD1, read_op) | LUT1(ADDR, PAD1, addrlen),
> base + QUADSPI_LUT(lut_base));
> - qspi_writel(q, LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo),
> + qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) |
> + LUT1(FSL_READ, PAD4, rxfifo),
> base + QUADSPI_LUT(lut_base + 1));
>
> /* Write enable */
> @@ -409,16 +403,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> /* Page Program */
> lut_base = SEQID_PP * 4;
>
> - if (q->nor_size <= SZ_16M) {
> - cmd = SPINOR_OP_PP;
> - addrlen = ADDR24BIT;
> - } else {
> - /* use the 4-byte address */
> - cmd = SPINOR_OP_PP;
> - addrlen = ADDR32BIT;
> - }
> -
> - qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
> + qspi_writel(q, LUT0(CMD, PAD1, nor->program_opcode) |
> + LUT1(ADDR, PAD1, addrlen),
> base + QUADSPI_LUT(lut_base));
> qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0),
> base + QUADSPI_LUT(lut_base + 1));
> @@ -432,10 +418,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> /* Erase a sector */
> lut_base = SEQID_SE * 4;
>
> - cmd = q->nor[0].erase_opcode;
> - addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT;
> -
> - qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
> + qspi_writel(q, LUT0(CMD, PAD1, nor->erase_opcode) |
> + LUT1(ADDR, PAD1, addrlen),
> base + QUADSPI_LUT(lut_base));
>
> /* Erase the whole chip */
> --
> 2.1.0.27.g96db324
>
>
Acked-by: Han xu <han.xu@nxp.com>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Sincerely,
Han XU
^ permalink raw reply
* [PATCH v3 2/9] mtd: fsl-quadspi: Rename SEQID_QUAD_READ to SEQID_READ
From: Han Xu @ 2016-09-14 19:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471505884-33996-2-git-send-email-B56489@freescale.com>
On Thu, Aug 18, 2016 at 2:37 AM, Yunhui Cui <B56489@freescale.com> wrote:
> There are some read modes for flash, such as NORMAL, FAST,
> QUAD, DDR QUAD. These modes will use the identical lut table base
> So rename SEQID_QUAD_READ to SEQID_READ.
>
> Signed-off-by: Yunhui Cui <B56489@freescale.com>
> Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
> ---
> drivers/mtd/spi-nor/fsl-quadspi.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 5ad6402..21c77e8 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -193,7 +193,7 @@
> #define QUADSPI_LUT_NUM 64
>
> /* SEQID -- we can have 16 seqids at most. */
> -#define SEQID_QUAD_READ 0
> +#define SEQID_READ 0
> #define SEQID_WREN 1
> #define SEQID_WRDI 2
> #define SEQID_RDSR 3
> @@ -386,8 +386,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> for (i = 0; i < QUADSPI_LUT_NUM; i++)
> qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4);
>
> - /* Quad Read */
> - lut_base = SEQID_QUAD_READ * 4;
> + /* Read */
> + lut_base = SEQID_READ * 4;
>
> qspi_writel(q, LUT0(CMD, PAD1, read_op) | LUT1(ADDR, PAD1, addrlen),
> base + QUADSPI_LUT(lut_base));
> @@ -468,7 +468,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> {
> switch (cmd) {
> case SPINOR_OP_READ_1_1_4:
> - return SEQID_QUAD_READ;
> + return SEQID_READ;
> case SPINOR_OP_WREN:
> return SEQID_WREN;
> case SPINOR_OP_WRDI:
> --
> 2.1.0.27.g96db324
>
>
Acked-by: Han xu <han.xu@nxp.com>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Sincerely,
Han XU
^ permalink raw reply
* [PATCH v3 3/9] mtd: spi-nor: fsl-quadspi: add fast-read mode support
From: Han Xu @ 2016-09-14 19:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471505884-33996-3-git-send-email-B56489@freescale.com>
On Thu, Aug 18, 2016 at 2:37 AM, Yunhui Cui <B56489@freescale.com> wrote:
> From: Yunhui Cui <yunhui.cui@nxp.com>
>
> The qspi driver add generic fast-read mode for different
> flash venders. There are some different board flash work on
> different mode, such fast-read, quad-mode.
>
> Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
> ---
> drivers/mtd/spi-nor/fsl-quadspi.c | 21 ++++++++++++++++-----
> 1 file changed, 16 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 21c77e8..b0a74b8 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -389,11 +389,21 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> /* Read */
> lut_base = SEQID_READ * 4;
>
> - qspi_writel(q, LUT0(CMD, PAD1, read_op) | LUT1(ADDR, PAD1, addrlen),
> - base + QUADSPI_LUT(lut_base));
> - qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) |
> - LUT1(FSL_READ, PAD4, rxfifo),
> - base + QUADSPI_LUT(lut_base + 1));
> + if (nor->flash_read == SPI_NOR_FAST) {
> + qspi_writel(q, LUT0(CMD, PAD1, read_op) |
> + LUT1(ADDR, PAD1, addrlen),
> + base + QUADSPI_LUT(lut_base));
> + qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) |
> + LUT1(FSL_READ, PAD1, rxfifo),
> + base + QUADSPI_LUT(lut_base + 1));
> + } else if (nor->flash_read == SPI_NOR_QUAD) {
> + qspi_writel(q, LUT0(CMD, PAD1, read_op) |
> + LUT1(ADDR, PAD1, addrlen),
> + base + QUADSPI_LUT(lut_base));
> + qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) |
> + LUT1(FSL_READ, PAD4, rxfifo),
> + base + QUADSPI_LUT(lut_base + 1));
> + }
>
> /* Write enable */
> lut_base = SEQID_WREN * 4;
> @@ -468,6 +478,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> {
> switch (cmd) {
> case SPINOR_OP_READ_1_1_4:
> + case SPINOR_OP_READ_FAST:
> return SEQID_READ;
> case SPINOR_OP_WREN:
> return SEQID_WREN;
> --
> 2.1.0.27.g96db324
>
>
Acked-by: Han xu <han.xu@nxp.com>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Sincerely,
Han XU
^ permalink raw reply
* [PATCH v3 4/9] mtd: spi-nor: fsl-quadspi: extend support for some special requerment.
From: Han Xu @ 2016-09-14 19:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471505884-33996-4-git-send-email-B56489@freescale.com>
On Thu, Aug 18, 2016 at 2:37 AM, Yunhui Cui <B56489@freescale.com> wrote:
> From: Yunhui Cui <yunhui.cui@nxp.com>
>
> Add extra info in LUT table to support some special requerments.
> Spansion S25FS-S family flash need some special operations.
>
> Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
> ---
> drivers/mtd/spi-nor/fsl-quadspi.c | 44 +++++++++++++++++++++++++++++++++++++--
> include/linux/mtd/spi-nor.h | 4 ++++
> 2 files changed, 46 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index b0a74b8..2521370 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -205,6 +205,9 @@
> #define SEQID_RDCR 9
> #define SEQID_EN4B 10
> #define SEQID_BRWR 11
> +#define SEQID_RDAR 12
> +#define SEQID_WRAR 13
> +
>
> #define QUADSPI_MIN_IOMAP SZ_4M
>
> @@ -470,6 +473,28 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR),
> base + QUADSPI_LUT(lut_base));
>
> + /*
> + * Read any device register.
> + * Used for Spansion S25FS-S family flash only.
> + */
> + lut_base = SEQID_RDAR * 4;
> + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_SPANSION_RDAR) |
> + LUT1(ADDR, PAD1, ADDR24BIT),
> + base + QUADSPI_LUT(lut_base));
> + qspi_writel(q, LUT0(DUMMY, PAD1, 8) | LUT1(FSL_READ, PAD1, 1),
> + base + QUADSPI_LUT(lut_base + 1));
> +
> + /*
> + * Write any device register.
> + * Used for Spansion S25FS-S family flash only.
> + */
> + lut_base = SEQID_WRAR * 4;
> + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_SPANSION_WRAR) |
> + LUT1(ADDR, PAD1, ADDR24BIT),
> + base + QUADSPI_LUT(lut_base));
> + qspi_writel(q, LUT0(FSL_WRITE, PAD1, 1),
> + base + QUADSPI_LUT(lut_base + 1));
> +
> fsl_qspi_lock_lut(q);
> }
>
> @@ -477,9 +502,15 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> {
> switch (cmd) {
> + case SPINOR_OP_READ4_1_1_4:
> case SPINOR_OP_READ_1_1_4:
> case SPINOR_OP_READ_FAST:
> + case SPINOR_OP_READ4_FAST:
> return SEQID_READ;
> + case SPINOR_OP_SPANSION_RDAR:
> + return SEQID_RDAR;
> + case SPINOR_OP_SPANSION_WRAR:
> + return SEQID_WRAR;
> case SPINOR_OP_WREN:
> return SEQID_WREN;
> case SPINOR_OP_WRDI:
> @@ -491,6 +522,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> case SPINOR_OP_CHIP_ERASE:
> return SEQID_CHIP_ERASE;
> case SPINOR_OP_PP:
> + case SPINOR_OP_PP_4B:
> return SEQID_PP;
> case SPINOR_OP_RDID:
> return SEQID_RDID;
> @@ -830,8 +862,12 @@ static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
> {
> int ret;
> struct fsl_qspi *q = nor->priv;
> + u32 to = 0;
> +
> + if (opcode == SPINOR_OP_SPANSION_RDAR)
> + memcpy(&to, nor->cmd_buf, 4);
>
> - ret = fsl_qspi_runcmd(q, opcode, 0, len);
> + ret = fsl_qspi_runcmd(q, opcode, to, len);
> if (ret)
> return ret;
>
> @@ -843,9 +879,13 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
> {
> struct fsl_qspi *q = nor->priv;
> int ret;
> + u32 to = 0;
> +
> + if (opcode == SPINOR_OP_SPANSION_WRAR)
> + memcpy(&to, nor->cmd_buf, 4);
>
> if (!buf) {
> - ret = fsl_qspi_runcmd(q, opcode, 0, 1);
> + ret = fsl_qspi_runcmd(q, opcode, to, 1);
> if (ret)
> return ret;
>
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index c425c7b..db3fe42 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -75,6 +75,10 @@
> /* Used for Spansion flashes only. */
> #define SPINOR_OP_BRWR 0x17 /* Bank register write */
>
> +/* Used for Spansion S25FS-S family flash only. */
> +#define SPINOR_OP_SPANSION_RDAR 0x65 /* Read any device register */
> +#define SPINOR_OP_SPANSION_WRAR 0x71 /* Write any device register */
> +
> /* Used for Micron flashes only. */
> #define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
> #define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
> --
> 2.1.0.27.g96db324
>
>
Acked-by: Han xu <han.xu@nxp.com>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Sincerely,
Han XU
^ permalink raw reply
* [PATCH v3 5/9] mtd: spi-nor: fsl-quadspi:Support qspi for ls2080a
From: Han Xu @ 2016-09-14 19:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471505884-33996-5-git-send-email-B56489@freescale.com>
On Thu, Aug 18, 2016 at 2:38 AM, Yunhui Cui <B56489@freescale.com> wrote:
> There is a hardware feature that qspi_amba_base is added
> internally by SOC design on ls2080a. So as to software, the driver
> need support to the feature.
>
> Signed-off-by: Yunhui Cui <B56489@freescale.com>
> Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
> ---
> drivers/mtd/spi-nor/fsl-quadspi.c | 24 ++++++++++++++++++++++--
> 1 file changed, 22 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 2521370..57eed3c 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -41,6 +41,8 @@
> #define QUADSPI_QUIRK_TKT253890 (1 << 2)
> /* Controller cannot wake up from wait mode, TKT245618 */
> #define QUADSPI_QUIRK_TKT245618 (1 << 3)
> +/* QSPI_AMBA_BASE is internally added by SOC design */
> +#define QUADSPI_AMBA_BASE_INTERNAL (0x10000)
>
> /* The registers */
> #define QUADSPI_MCR 0x00
> @@ -217,6 +219,7 @@ enum fsl_qspi_devtype {
> FSL_QUADSPI_IMX7D,
> FSL_QUADSPI_IMX6UL,
> FSL_QUADSPI_LS1021A,
> + FSL_QUADSPI_LS2080A,
> };
>
> struct fsl_qspi_devtype_data {
> @@ -270,6 +273,14 @@ static struct fsl_qspi_devtype_data ls1021a_data = {
> .driver_data = 0,
> };
>
> +static struct fsl_qspi_devtype_data ls2080a_data = {
> + .devtype = FSL_QUADSPI_LS2080A,
> + .rxfifo = 128,
> + .txfifo = 64,
> + .ahb_buf_size = 1024,
> + .driver_data = QUADSPI_AMBA_BASE_INTERNAL,
> +};
> +
> #define FSL_QSPI_MAX_CHIP 4
> struct fsl_qspi {
> struct spi_nor nor[FSL_QSPI_MAX_CHIP];
> @@ -312,6 +323,11 @@ static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
> return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618;
> }
>
> +static inline int has_added_amba_base_internal(struct fsl_qspi *q)
> +{
> + return q->devtype_data->driver_data & QUADSPI_AMBA_BASE_INTERNAL;
> +}
> +
> /*
> * R/W functions for big- or little-endian registers:
> * The qSPI controller's endian is independent of the CPU core's endian.
> @@ -558,8 +574,11 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
> /* save the reg */
> reg = qspi_readl(q, base + QUADSPI_MCR);
>
> - qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr,
> - base + QUADSPI_SFAR);
> + if (has_added_amba_base_internal(q))
> + qspi_writel(q, q->chip_base_addr + addr, base + QUADSPI_SFAR);
> + else
> + qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr,
> + base + QUADSPI_SFAR);
> qspi_writel(q, QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
> base + QUADSPI_RBCT);
> qspi_writel(q, reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR);
> @@ -849,6 +868,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
> { .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, },
> { .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, },
> { .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, },
> + { .compatible = "fsl,ls2080a-qspi", .data = (void *)&ls2080a_data, },
> { /* sentinel */ }
> };
> MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
> --
> 2.1.0.27.g96db324
>
>
Acked-by: Han xu <han.xu@nxp.com>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Sincerely,
Han XU
^ permalink raw reply
* [PATCH v3 6/9] mtd: spi-nor: Support R/W for S25FS-S family flash
From: Han Xu @ 2016-09-14 19:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471505884-33996-6-git-send-email-B56489@freescale.com>
On Thu, Aug 18, 2016 at 2:38 AM, Yunhui Cui <B56489@freescale.com> wrote:
> From: Yunhui Cui <yunhui.cui@nxp.com>
>
> With the physical sectors combination, S25FS-S family flash
> requires some special operations for read/write functions.
>
> Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
> ---
> drivers/mtd/spi-nor/spi-nor.c | 56 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 56 insertions(+)
>
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index d0fc165..495d0bb 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -39,6 +39,10 @@
>
> #define SPI_NOR_MAX_ID_LEN 6
> #define SPI_NOR_MAX_ADDR_WIDTH 4
> +/* Added for S25FS-S family flash */
> +#define SPINOR_CONFIG_REG3_OFFSET 0x800004
> +#define CR3V_4KB_ERASE_UNABLE 0x8
> +#define SPINOR_S25FS_FAMILY_EXT_JEDEC 0x81
>
> struct flash_info {
> char *name;
> @@ -78,6 +82,7 @@ struct flash_info {
> };
>
> #define JEDEC_MFR(info) ((info)->id[0])
> +#define EXT_JEDEC(info) ((info)->id[5])
>
> static const struct flash_info *spi_nor_match_id(const char *name);
>
> @@ -899,6 +904,7 @@ static const struct flash_info spi_nor_ids[] = {
> */
> { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> + { "s25fs256s1", INFO6(0x010219, 0x4d0181, 64 * 1024, 512, 0)},
> { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
> { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> @@ -1036,6 +1042,50 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
> return ERR_PTR(-ENODEV);
> }
>
> +/*
> + * The S25FS-S family physical sectors may be configured as a
> + * hybrid combination of eight 4-kB parameter sectors
> + * at the top or bottom of the address space with all
> + * but one of the remaining sectors being uniform size.
> + * The Parameter Sector Erase commands (20h or 21h) must
> + * be used to erase the 4-kB parameter sectors individually.
> + * The Sector (uniform sector) Erase commands (D8h or DCh)
> + * must be used to erase any of the remaining
> + * sectors, including the portion of highest or lowest address
> + * sector that is not overlaid by the parameter sectors.
> + * The uniform sector erase command has no effect on parameter sectors.
> + */
> +static int spansion_s25fs_disable_4kb_erase(struct spi_nor *nor)
> +{
> + u32 cr3v_addr = SPINOR_CONFIG_REG3_OFFSET;
> + u8 cr3v = 0x0;
> + int ret = 0x0;
> +
> + nor->cmd_buf[2] = cr3v_addr >> 16;
> + nor->cmd_buf[1] = cr3v_addr >> 8;
> + nor->cmd_buf[0] = cr3v_addr >> 0;
> +
> + ret = nor->read_reg(nor, SPINOR_OP_SPANSION_RDAR, &cr3v, 1);
> + if (ret)
> + return ret;
> + if (cr3v & CR3V_4KB_ERASE_UNABLE)
> + return 0;
> + ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
> + if (ret)
> + return ret;
> + cr3v = CR3V_4KB_ERASE_UNABLE;
> + nor->program_opcode = SPINOR_OP_SPANSION_WRAR;
> + nor->write(nor, cr3v_addr, 1, &cr3v);
> +
> + ret = nor->read_reg(nor, SPINOR_OP_SPANSION_RDAR, &cr3v, 1);
> + if (ret)
> + return ret;
> + if (!(cr3v & CR3V_4KB_ERASE_UNABLE))
> + return -EPERM;
> +
> + return 0;
> +}
> +
> static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
> size_t *retlen, u_char *buf)
> {
> @@ -1361,6 +1411,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
> spi_nor_wait_till_ready(nor);
> }
>
> + if (EXT_JEDEC(info) == SPINOR_S25FS_FAMILY_EXT_JEDEC) {
> + ret = spansion_s25fs_disable_4kb_erase(nor);
> + if (ret)
> + return ret;
> + }
> +
> if (!mtd->name)
> mtd->name = dev_name(dev);
> mtd->priv = nor;
> --
> 2.1.0.27.g96db324
>
>
Hi Brian, I will ack this change but still feel it's kind of hacking code.
Acked-by: Han xu <han.xu@nxp.com>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Sincerely,
Han XU
^ permalink raw reply
* [PATCH v3 7/9] mtd: fsl-quadspi: Solve Micron Spansion flash command conflict
From: Han Xu @ 2016-09-14 19:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471505884-33996-7-git-send-email-B56489@freescale.com>
On Thu, Aug 18, 2016 at 2:38 AM, Yunhui Cui <B56489@freescale.com> wrote:
> From: Yunhui Cui <yunhui.cui@nxp.com>
>
> Add some lut_tables to support quad mode for flash n25q128
> on the board ls1021a-twr and solve flash Spansion and Micron
> command conflict.
> In switch {}, The value of command SPINOR_OP_RD_EVCR and
> SPINOR_OP_SPANSION_RDAR is the same. They have to share
> the same seq_id: SEQID_RDAR_OR_RD_EVCR.
>
> Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
> ---
> drivers/mtd/spi-nor/fsl-quadspi.c | 47 ++++++++++++++++++++++++++++++---------
> 1 file changed, 36 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 57eed3c..f9a7d4b 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -207,9 +207,9 @@
> #define SEQID_RDCR 9
> #define SEQID_EN4B 10
> #define SEQID_BRWR 11
> -#define SEQID_RDAR 12
> +#define SEQID_RDAR_OR_RD_EVCR 12
> #define SEQID_WRAR 13
> -
> +#define SEQID_WD_EVCR 14
>
> #define QUADSPI_MIN_IOMAP SZ_4M
>
> @@ -393,6 +393,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> int rxfifo = q->devtype_data->rxfifo;
> u32 lut_base;
> int i;
> + const struct fsl_qspi_devtype_data *devtype_data = q->devtype_data;
>
> struct spi_nor *nor = &q->nor[0];
> u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
> @@ -489,16 +490,26 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR),
> base + QUADSPI_LUT(lut_base));
>
> +
> /*
> - * Read any device register.
> - * Used for Spansion S25FS-S family flash only.
> + * Flash Micron and Spansion command confilict
> + * use the same value 0x65. But it indicates different meaning.
> */
> - lut_base = SEQID_RDAR * 4;
> - qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_SPANSION_RDAR) |
> - LUT1(ADDR, PAD1, ADDR24BIT),
> - base + QUADSPI_LUT(lut_base));
> - qspi_writel(q, LUT0(DUMMY, PAD1, 8) | LUT1(FSL_READ, PAD1, 1),
> - base + QUADSPI_LUT(lut_base + 1));
> + lut_base = SEQID_RDAR_OR_RD_EVCR * 4;
> + if (devtype_data->devtype == FSL_QUADSPI_LS2080A) {
> + /*
> + * Read any device register.
> + * Used for Spansion S25FS-S family flash only.
> + */
> + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_SPANSION_RDAR) |
> + LUT1(ADDR, PAD1, ADDR24BIT),
> + base + QUADSPI_LUT(lut_base));
> + qspi_writel(q, LUT0(DUMMY, PAD1, 8) | LUT1(FSL_READ, PAD1, 1),
> + base + QUADSPI_LUT(lut_base + 1));
> + } else {
> + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RD_EVCR),
> + base + QUADSPI_LUT(lut_base));
> + }
>
> /*
> * Write any device register.
> @@ -511,6 +522,11 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> qspi_writel(q, LUT0(FSL_WRITE, PAD1, 1),
> base + QUADSPI_LUT(lut_base + 1));
>
> + /* Write EVCR register */
> + lut_base = SEQID_WD_EVCR * 4;
> + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WD_EVCR),
> + base + QUADSPI_LUT(lut_base));
> +
> fsl_qspi_lock_lut(q);
> }
>
> @@ -523,8 +539,15 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> case SPINOR_OP_READ_FAST:
> case SPINOR_OP_READ4_FAST:
> return SEQID_READ;
> + /*
> + * Spansion & Micron use the same command value 0x65
> + * Spansion: SPINOR_OP_SPANSION_RDAR, read any register.
> + * Micron: SPINOR_OP_RD_EVCR,
> + * read enhanced volatile configuration register.
> + * case SPINOR_OP_RD_EVCR:
> + */
> case SPINOR_OP_SPANSION_RDAR:
> - return SEQID_RDAR;
> + return SEQID_RDAR_OR_RD_EVCR;
> case SPINOR_OP_SPANSION_WRAR:
> return SEQID_WRAR;
> case SPINOR_OP_WREN:
> @@ -550,6 +573,8 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> return SEQID_EN4B;
> case SPINOR_OP_BRWR:
> return SEQID_BRWR;
> + case SPINOR_OP_WD_EVCR:
> + return SEQID_WD_EVCR;
> default:
> if (cmd == q->nor[0].erase_opcode)
> return SEQID_SE;
> --
> 2.1.0.27.g96db324
>
>
Acked-by: Han xu <han.xu@nxp.com>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Sincerely,
Han XU
^ permalink raw reply
* [PATCH v3 8/9] mtd: fsl-quadspi: disable AHB buffer prefetch
From: Han Xu @ 2016-09-14 19:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471505884-33996-8-git-send-email-B56489@freescale.com>
On Thu, Aug 18, 2016 at 2:38 AM, Yunhui Cui <B56489@freescale.com> wrote:
> From: Yunhui Cui <yunhui.cui@nxp.com>
>
> A-009282: QuadSPI: QuadSPI data pre-fetch can result in incorrect data
> Affects: QuadSPI
> Description: With AHB buffer prefetch enabled, the QuadSPI may return
> incorrect data on the AHB
> interface. The buffer pre-fetch is enabled if the fetch size as
> configured either in the LUT or in
> the BUFxCR register is greater than 8 bytes.
> Impact: Only 64 bit read allowed.
> Workaround: Keep the read data size to 64 bits (8 Bytes), which disables
> the prefetch on the AHB buffer,
> and prevents this issue from occurring.
>
> Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
> ---
> drivers/mtd/spi-nor/fsl-quadspi.c | 33 ++++++++++++++++++++++++---------
> 1 file changed, 24 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index f9a7d4b..193e81b 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -41,6 +41,8 @@
> #define QUADSPI_QUIRK_TKT253890 (1 << 2)
> /* Controller cannot wake up from wait mode, TKT245618 */
> #define QUADSPI_QUIRK_TKT245618 (1 << 3)
> +/*Errata A-009282: disable the AHB buffer prefetch */
> +#define QUADSPI_QUIRK_ADASZ_8BYTE_LIMIT (1 << 4)
> /* QSPI_AMBA_BASE is internally added by SOC design */
> #define QUADSPI_AMBA_BASE_INTERNAL (0x10000)
>
> @@ -270,7 +272,7 @@ static struct fsl_qspi_devtype_data ls1021a_data = {
> .rxfifo = 128,
> .txfifo = 64,
> .ahb_buf_size = 1024,
> - .driver_data = 0,
> + .driver_data = QUADSPI_QUIRK_ADASZ_8BYTE_LIMIT,
> };
>
> static struct fsl_qspi_devtype_data ls2080a_data = {
> @@ -278,7 +280,8 @@ static struct fsl_qspi_devtype_data ls2080a_data = {
> .rxfifo = 128,
> .txfifo = 64,
> .ahb_buf_size = 1024,
> - .driver_data = QUADSPI_AMBA_BASE_INTERNAL,
> + .driver_data = QUADSPI_AMBA_BASE_INTERNAL
> + | QUADSPI_QUIRK_ADASZ_8BYTE_LIMIT,
> };
>
> #define FSL_QSPI_MAX_CHIP 4
> @@ -328,6 +331,11 @@ static inline int has_added_amba_base_internal(struct fsl_qspi *q)
> return q->devtype_data->driver_data & QUADSPI_AMBA_BASE_INTERNAL;
> }
>
> +static inline int needs_disable_ahb_prefetch(struct fsl_qspi *q)
> +{
> + return q->devtype_data->driver_data & QUADSPI_QUIRK_ADASZ_8BYTE_LIMIT;
> +}
> +
> /*
> * R/W functions for big- or little-endian registers:
> * The qSPI controller's endian is independent of the CPU core's endian.
> @@ -757,14 +765,21 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
> qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
> qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
> qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
> - /*
> - * Set ADATSZ with the maximum AHB buffer size to improve the
> - * read performance.
> - */
> - qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK |
> - ((q->devtype_data->ahb_buf_size / 8)
> - << QUADSPI_BUF3CR_ADATSZ_SHIFT),
> +
> + if (needs_disable_ahb_prefetch(q)) {
> + qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK |
> + (1 << QUADSPI_BUF3CR_ADATSZ_SHIFT),
> base + QUADSPI_BUF3CR);
> + } else {
> + /*
> + * Set ADATSZ with the maximum AHB buffer size to improve the
> + * read performance.
> + */
> + qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK |
> + ((q->devtype_data->ahb_buf_size / 8)
> + << QUADSPI_BUF3CR_ADATSZ_SHIFT),
> + base + QUADSPI_BUF3CR);
> + }
>
> /* We only use the buffer3 */
> qspi_writel(q, 0, base + QUADSPI_BUF0IND);
> --
> 2.1.0.27.g96db324
>
>
Acked-by: Han xu <han.xu@nxp.com>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Sincerely,
Han XU
^ permalink raw reply
* [PATCH v3 9/9] mtd: fsl-quadspi: add multi flash chip R/W on ls2080a
From: Han Xu @ 2016-09-14 19:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471505884-33996-9-git-send-email-B56489@freescale.com>
On Thu, Aug 18, 2016 at 2:38 AM, Yunhui Cui <B56489@freescale.com> wrote:
> From: Yunhui Cui <yunhui.cui@nxp.com>
>
> There is a hardware feature that qspi_amba_base is added
> internally by SOC design on ls2080a. so memmap_phy need not
> be added in driver. If memmap_phy is added, the flash A1
> addr space is [0, memmap_phy] which far more than flash size.
> The AMBA memory will be divided into four parts and assign to
> every chipselect. Every channel will has two valid chipselects.
>
> Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
> ---
> drivers/mtd/spi-nor/fsl-quadspi.c | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 193e81b..8c9746c 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -736,11 +736,17 @@ static void fsl_qspi_set_map_addr(struct fsl_qspi *q)
> {
> int nor_size = q->nor_size;
> void __iomem *base = q->iobase;
> + u32 mem_base;
>
> - qspi_writel(q, nor_size + q->memmap_phy, base + QUADSPI_SFA1AD);
> - qspi_writel(q, nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD);
> - qspi_writel(q, nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD);
> - qspi_writel(q, nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD);
> + if (has_added_amba_base_internal(q))
> + mem_base = 0x0;
> + else
> + mem_base = q->memmap_phy;
> +
> + qspi_writel(q, nor_size + mem_base, base + QUADSPI_SFA1AD);
> + qspi_writel(q, nor_size * 2 + mem_base, base + QUADSPI_SFA2AD);
> + qspi_writel(q, nor_size * 3 + mem_base, base + QUADSPI_SFB1AD);
> + qspi_writel(q, nor_size * 4 + mem_base, base + QUADSPI_SFB2AD);
> }
>
> /*
> --
> 2.1.0.27.g96db324
>
>
Acked-by: Han xu <han.xu@nxp.com>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Sincerely,
Han XU
^ permalink raw reply
* next-20160914 build: 2 failures 3 warnings (next-20160914)
From: Lee Jones @ 2016-09-14 19:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160914171137.GF27974@sirena.org.uk>
On Wed, 14 Sep 2016, Mark Brown wrote:
> On Wed, Sep 14, 2016 at 09:18:08AM +0100, Build bot for Mark Brown wrote:
>
> Today's -next fails to build both arm and arm64 allmodconfig due to:
>
> > arm64-allmodconfig
> > ERROR: "irq_set_parent" [drivers/mfd/tps65217.ko] undefined!
> >
> > arm-allmodconfig
> > ERROR: "irq_set_parent" [drivers/mfd/tps65217.ko] undefined!
>
> due to 262d5cc6ceb293 (mfd: tps65217: Add support for IRQs) since
> irq_set_parent() isn't exported.
Arnd is talking to Thomas about a possible solution.
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [GIT PULL] Greybus driver subsystem for 4.9-rc1
From: Rob Herring @ 2016-09-14 20:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160914180754.GA16053@kroah.com>
On Wed, Sep 14, 2016 at 1:07 PM, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Wed, Sep 14, 2016 at 06:36:26PM +0100, Mark Rutland wrote:
>> Hi Greg,
>>
>> On Wed, Sep 14, 2016 at 12:09:49PM +0200, Greg KH wrote:
>> > Given that it's never a good idea to keep subsystems out of the mainline
>> > kernel, I've put together this pull request that adds the greybus driver
>> > layer to drivers/greybus/. Because this was 2 1/2 years of work, with
>> > many many developers contributing, I didn't want to flatten all of their
>> > effort into a few small patches, as that wouldn't be very fair. So I've
>> > built a git tree with all of the changes going back to the first commit,
>> > and merged it into the kernel tree, just like btrfs was merged into the
>> > kernel.
>>
>> > Unless people point out some major problems with this, I'd like to get
>> > it merged into 4.9-rc1.
>>
>> I'm extremely concerned that these patches have *never* seen upstream
>> review, and this pull request gives no real opportunity for people to
>> make a judgement regarding the code, as many relevant parties have not
>> been Cc'd.
>
> As I said, I will send a set of simple patches, I wanted to get this out
> as soon as possible and other things came up today. Will do it in the
> morning, sorry.
>
>> From a quick scan of the git tree, I can see code (that isn't even
>> placed under staging/) for which I have fundamental objections to as a
>> maintainer, and has not been Cc'd to a relevant list.
>>
>> For example, I see commit 5a450477311fbfe2 ("greybus: timesync: Add
>> timesync core driver"). This states that it directly accesses the ARMv7
>> architected timer, though it's unclear as to precisely what it's doing
>> since it introduces an (undocumented) compatible string, and what should
>> be an unnecessary devicetree property.
>>
>> That's never gone to the linux-arm-kernel mainline list, myself or Marc
>> (as maintainers of the arch timer driver), nor has the binding seen any
>> review on the devicetree mailing list.
>
> Hm, odd, I thought we had Rob review all of the device tree bindings,
> but maybe the timesync stuff missed him. And timesync is "odd" to say
> the least, wait until you see the firmware side of it :)
I have not. There weren't any when I was involved (other than SOC
related bindings). Some like USB devices were discussed at least, but
at the time there was no common definition of how to deal with
soldered, on-board USB devices. And that was also what's good enough
to move forward with development, not ready for mainline. There's a
binding now for USB, but what's there for arche predates it IIRC. This
is the first I've heard of timesync having a binding. I can't imagine
why it needs one.
I was going to stay out of this, but now that I'm here...
I'm not all that worried about the quality of the code, but do
question whether this really makes sense to merge at this time. While
I worked on Ara and would like to see all this code be used, I'm
pretty doubtful it will be. Yes, Motorola is using a version of it,
but they forked it some time back and changed who knows what. So what
is upstream won't likely even work with any publicly available device.
And how long that Motorola phone lasts is unknown. Sure it is self
contained, but maintenance to keep it in tree is not 0.
There's also things that never got solved. Like how do you describe
devices on I2C, SPI, UART, etc. behind a greybus device? The plan was
to use DT overlays, but that was never solved and brings a whole set
of problems to solve upstream.
Rob
^ permalink raw reply
* [PATCH v2 0/3] ARM64: amlogic: Add support for GXL SoC Family
From: Neil Armstrong @ 2016-09-14 20:08 UTC (permalink / raw)
To: linux-arm-kernel
The new Amlogic GXL SoCs (S905X and S905D) are part of the Meson GX family and
share some common features that can be described in a common GX dtsi file used
by the Meson GXBB and Meson GXL Family dtsi.
This patchset introduces the common GX dtsi and switches the GXBB to use
the common GX dtsi.
Then it introduces the GXL S905X SoC with the GXL common dtsi, then the S905D
dtsi and the p212 board dts.
Finally the GXL S905D SoC is introduced with a S905D dtsi using the GXL common
and the p23x Board dtsi for the p231 and p230 development boards.
Changes since v1 at http://lkml.kernel.org/r/20160903082227.30559-1-narmstrong at baylibre.com :
- Add missing copyrigh in gx dtsi
- Rename gxl SoCs compatibles to amlogic,s905x and amlogic,s905d
Changes since RFC v1:
- Merge GX common and GXBB changes in a single patch
- Integrate GXL S905X patch
- Add support for S905D and the p23x boards
Note: This patchset integrates the patch "ARM64: dts: amlogic: Add basic support for Amlogic S905X" [1]
from Carlo Caione.
[1] http://lkml.kernel.org/r/1472382113-10754-1-git-send-email-carlo at caione.org
Carlo Caione (1):
ARM64: dts: amlogic: Add basic support for Amlogic S905X
Neil Armstrong (2):
ARM64: dts: amlogic: Add Meson GX dtsi from GXBB
ARM64: dts: amlogic: Add basic support for Amlogic S905D
Documentation/devicetree/bindings/arm/amlogic.txt | 11 +
arch/arm64/boot/dts/amlogic/Makefile | 3 +
arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 200 +++++
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 895 +++++++++------------
.../boot/dts/amlogic/meson-gxl-s905d-p230.dts | 51 ++
.../boot/dts/amlogic/meson-gxl-s905d-p231.dts | 51 ++
.../boot/dts/amlogic/meson-gxl-s905d-p23x.dtsi | 64 ++
arch/arm64/boot/dts/amlogic/meson-gxl-s905d.dtsi | 48 ++
.../boot/dts/amlogic/meson-gxl-s905x-p212.dts | 69 ++
arch/arm64/boot/dts/amlogic/meson-gxl-s905x.dtsi | 48 ++
arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 48 ++
11 files changed, 972 insertions(+), 516 deletions(-)
create mode 100644 arch/arm64/boot/dts/amlogic/meson-gx.dtsi
create mode 100644 arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
create mode 100644 arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p231.dts
create mode 100644 arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p23x.dtsi
create mode 100644 arch/arm64/boot/dts/amlogic/meson-gxl-s905d.dtsi
create mode 100644 arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
create mode 100644 arch/arm64/boot/dts/amlogic/meson-gxl-s905x.dtsi
create mode 100644 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
--
2.7.0
^ 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