* [PATCH RESEND] ARM: AM43XX: Select OMAP_INTERCONNECT in Kconfig
From: Tony Lindgren @ 2016-11-07 23:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161019204412.18607-1-d-gerlach@ti.com>
* Dave Gerlach <d-gerlach@ti.com> [161019 13:45]:
> AM437x makes use of the omap_l3_noc driver so explicitly select
> OMAP_INTERCONNECT in the Kconfig for SOC_AM43XX to ensure it gets enabled
> for AM43XX only builds.
Applying into omap-for-v4.9/fixes thanks.
Tony
^ permalink raw reply
* [PATCH] ARM: OMAP2+: PRM: initialize en_uart4_mask and grpsel_uart4_mask
From: Tony Lindgren @ 2016-11-07 23:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161024110021.10259-1-colin.king@canonical.com>
* Colin King <colin.king@canonical.com> [161024 04:01]:
> From: Colin Ian King <colin.king@canonical.com>
>
> In the case where has_uart4 is false, en_uart4_mask and grpsel_uart4_mask
> are not initialized and so any garbage value is being logically or'd into
> the write of PM_WKEN and OMAP3430_PM_MPUGRPSEL. Fix this by initializing
> these masks to zero.
Thanks applying into omap-for-v4.9/fixes.
Regards,
Tony
^ permalink raw reply
* [PATCH] ARM: OMAP2+: avoid NULL pointer dereference
From: Tony Lindgren @ 2016-11-07 23:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477993765-9980-1-git-send-email-Nicolae_Rosia@mentor.com>
* Nicolae Rosia <Nicolae_Rosia@mentor.com> [161101 02:50]:
> For OMAP4, volt_data is set in omap44xx_voltagedomains_init.
> If the SoC is neither OMAP443X or OMAP446X, we end up with a
> NULL in volt_data which causes a kernel oops.
> This is the case when booting OMAP4470.
Thanks applying into omap-for-v4.9/fixes.
Tony
^ permalink raw reply
* [PATCH v2] media: s5p-mfc include buffer size in error message
From: Shuah Khan @ 2016-11-07 23:39 UTC (permalink / raw)
To: linux-arm-kernel
Include buffer size in s5p_mfc_alloc_priv_buf() the error message when it
fails to allocate the buffer.
Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>
---
Changes since v1:
- Left debug message as is. v1 removed the debug message.
drivers/media/platform/s5p-mfc/s5p_mfc_opr.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
index 1e72502..da4f52a 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
@@ -45,7 +45,8 @@ int s5p_mfc_alloc_priv_buf(struct device *dev, dma_addr_t base,
b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL);
if (!b->virt) {
- mfc_err("Allocating private buffer failed\n");
+ mfc_err("Allocating private buffer of size %zu failed\n",
+ b->size);
return -ENOMEM;
}
--
2.7.4
^ permalink raw reply related
* [PATCH] media: s5p-mfc include buffer size in error message
From: Shuah Khan @ 2016-11-07 23:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <b1a9fa02-6821-7637-881c-a31719e891c9@samsung.com>
On 11/04/2016 04:05 AM, Sylwester Nawrocki wrote:
> On 10/18/2016 02:43 AM, Shuah Khan wrote:
>> Include buffer size in s5p_mfc_alloc_priv_buf() the error message when it
>> fails to allocate the buffer. Remove the debug message that does the same.
>>
>> Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>
>> ---
>> drivers/media/platform/s5p-mfc/s5p_mfc_opr.c | 5 ++---
>> 1 file changed, 2 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
>> index 1e72502..eee16a1 100644
>> --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
>> +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
>> @@ -40,12 +40,11 @@ void s5p_mfc_init_regs(struct s5p_mfc_dev *dev)
>> int s5p_mfc_alloc_priv_buf(struct device *dev, dma_addr_t base,
>> struct s5p_mfc_priv_buf *b)
>> {
>> - mfc_debug(3, "Allocating priv: %zu\n", b->size);
>
> How about keeping this debug message, I think it would be useful
> to leave that information in the debug logs.
Sent v2 with just the error message change.
thanks,
-- Shuah
>
>> b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL);
>>
>> if (!b->virt) {
>> - mfc_err("Allocating private buffer failed\n");
>> + mfc_err("Allocating private buffer of size %zu failed\n",
>> + b->size);
>> return -ENOMEM;
>> }
>
> --
> Thanks,
> Sylwester
>
^ permalink raw reply
* [PATCH] fpga zynq: Check the bitstream for validity
From: Jason Gunthorpe @ 2016-11-08 0:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4e2c7e1c-7c33-a552-5b91-dbdefac58e37@xilinx.com>
On Tue, Nov 01, 2016 at 06:48:42PM +0100, Michal Simek wrote:
> On 1.11.2016 16:33, Jason Gunthorpe wrote:
> > On Tue, Nov 01, 2016 at 07:39:22AM +0100, Michal Simek wrote:
> >
> >> Regarding BIT and BIN format. This support is in vivado for a long time
> >> and it is up2you what you want to support. We have removed that BIT
> >> support and not doing any swap by saying only BIN format is supported.
> >
> > BIN is not supported, it needs a swap as well.
> >
> > Moritz has it right, you have to use vivado to create a PROM image to be
> > compatible with the driver.
>
> hm than that's bad.
IMHO, Xilinx made an error with Zynq DevC, the DMA does not accept a
memory image that is output by the usual Xilinx tools. It should have
accepted a byte swapped input.
I think Moritz is right, the fpgamgr *should not* alter the bitstream
in any way. This is important for future work to make the DMA do
gather and avoid the really bad high-order allocation.
So users will have to provide byte swapped .bin files - the vivado
write_cfgmem command will produce them - this all needs to be
documented.
Also, I think Punnaiah (?) was telling me that bitstream encryption
does not work - DevC must be told the bitstream is encrypted.
That seems like something that needs work at the fpgamgr level - and
maybe this driver should auto-detect encryption by looking at the
bitfile (as is typical for Xilinx programming)
Jason
^ permalink raw reply
* [PATCH] ARM64: dts: amlogic: Reorder copyrights for meson-gx
From: Kevin Hilman @ 2016-11-08 0:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478460334-17644-1-git-send-email-afaerber@suse.de>
Andreas F?rber <afaerber@suse.de> writes:
> meson-gx.dtsi was directly derived from meson-gxbb.dtsi, so keep the
> copyrights in chronological order to not give a wrong impression.
>
> Fixes: c328666d58aa ("ARM64: dts: amlogic: Add Meson GX dtsi from GXBB")
> Cc: Neil Armstrong <narmstrong@baylibre.com>
> Cc: Kevin Hilman <khilman@baylibre.com>
> Signed-off-by: Andreas F?rber <afaerber@suse.de>
Applied.
Kevin
^ permalink raw reply
* [PATCH] fpga zynq: Check the bitstream for validity
From: Jason Gunthorpe @ 2016-11-08 0:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161029000926.GA30169@live.com>
On Fri, Oct 28, 2016 at 05:09:26PM -0700, Moritz Fischer wrote:
> That being said, I don't like the idea of the driver having to search
> either...
I think we are stuck with that, considering what Xilinx tools
produce..
Here is a v2, what do you think?
>From 93ffde371ca50809ba9b4cdca17051a050b0f92d Mon Sep 17 00:00:00 2001
From: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Date: Wed, 26 Oct 2016 16:51:26 -0600
Subject: [PATCH v2] fpga zynq: Check the bitstream for validity
There is no sense in sending a bitstream we know will not work, and
with the variety of options for bitstream generation in Xilinx tools
it is not terribly clear or very well documented what the correct
input should be, especially since auto-detection was removed from this
driver.
All Zynq full configuration bitstreams must start with the sync word in
the correct byte order.
Zynq is also only able to DMA dword quantities, so bitstreams must be
a multiple of 4 bytes. This also fixes a DMA-past the end bug.
Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
---
drivers/fpga/zynq-fpga.c | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index c2fb4120bd62..de475a6a1882 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -175,6 +175,19 @@ static irqreturn_t zynq_fpga_isr(int irq, void *data)
return IRQ_HANDLED;
}
+/* Sanity check the proposed bitstream. It must start with the sync word in
+ * the correct byte order. The input is a Xilinx .bin file with every 32 bit
+ * quantity swapped.
+ */
+static bool zynq_fpga_has_sync(const char *buf, size_t count)
+{
+ for (; count > 4; buf += 4, --count)
+ if (buf[0] == 0x66 && buf[1] == 0x55 && buf[2] == 0x99 &&
+ buf[3] == 0xaa)
+ return true;
+ return false;
+}
+
static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
const char *buf, size_t count)
{
@@ -184,12 +197,23 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
priv = mgr->priv;
+ /* The hardware can only DMA multiples of 4 bytes, and we need at
+ * least the sync word and something else to do anything.
+ */
+ if (count <= 4 || (count % 4) != 0)
+ return -EINVAL;
+
err = clk_enable(priv->clk);
if (err)
return err;
/* don't globally reset PL if we're doing partial reconfig */
if (!(flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+ if (!zynq_fpga_has_sync(buf, count)) {
+ err = -EINVAL;
+ goto out_err;
+ }
+
/* assert AXI interface resets */
regmap_write(priv->slcr, SLCR_FPGA_RST_CTRL_OFFSET,
FPGA_RST_ALL_MASK);
@@ -287,12 +311,9 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr,
struct zynq_fpga_priv *priv;
int err;
char *kbuf;
- size_t in_count;
dma_addr_t dma_addr;
- u32 transfer_length;
u32 intr_status;
- in_count = count;
priv = mgr->priv;
kbuf = dma_alloc_coherent(priv->dev, count, &dma_addr, GFP_KERNEL);
@@ -318,11 +339,7 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr,
*/
zynq_fpga_write(priv, DMA_SRC_ADDR_OFFSET, (u32)(dma_addr) + 1);
zynq_fpga_write(priv, DMA_DST_ADDR_OFFSET, (u32)DMA_INVALID_ADDRESS);
-
- /* convert #bytes to #words */
- transfer_length = (count + 3) / 4;
-
- zynq_fpga_write(priv, DMA_SRC_LEN_OFFSET, transfer_length);
+ zynq_fpga_write(priv, DMA_SRC_LEN_OFFSET, count / 4);
zynq_fpga_write(priv, DMA_DEST_LEN_OFFSET, 0);
wait_for_completion(&priv->dma_done);
@@ -338,7 +355,7 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr,
clk_disable(priv->clk);
out_free:
- dma_free_coherent(priv->dev, in_count, kbuf, dma_addr);
+ dma_free_coherent(priv->dev, count, kbuf, dma_addr);
return err;
}
--
2.1.4
^ permalink raw reply related
* [PATCH] staging: vc04_services: Add 32-bit compatibility ioctls
From: Michael Zoran @ 2016-11-08 0:48 UTC (permalink / raw)
To: linux-arm-kernel
VCHIQ/vc04_services has a userland device interface
that includes ioctls. The ioctls are very pointer
dependent, so in the case of 32-bit running on top
of a 64-bit kernel, it is necessary to deal with
the structure differences.
The solution to this is that the standard ioctl numbering
mechanism includes a data size field. Since the size of the
userland data structures change between 32-bit and 64-bit,
the length of the data can be used to easily determine the
type of application.
On 64-bit ONLY - Compatibility ioctls are defined.
Example:
Native ioctl:
_IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T)
Compatibility ioctl:
_IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE32_T)
For this particular ioctl, the two ioctls share a different
code path. For some ioctls, it is much easier to share
the same code path and only occasionally handle the differences.
This is typically done by copying the 32-bit datastructure into
the 64-bit datastructure after it has been read from userland.
Testing:
1. vchiq_test -f 10 and vchiq_test -p 1 were run from a native
64-bit OS(debian sid) to check for regressions.
2. vchiq_test -f 10 and vchiq_test -p 1 where run from a 32-bit
chroot install from the same OS to test the compatibility ioctls.
Both test cases now pass.
Signed-off-by: Michael Zoran <mzoran@crowfest.net>
---
.../vc04_services/interface/vchiq_arm/vchiq_arm.c | 269 +++++++++++++++++++++
.../vc04_services/interface/vchiq_arm/vchiq_if.h | 25 ++
.../interface/vchiq_arm/vchiq_ioctl.h | 102 ++++++++
3 files changed, 396 insertions(+)
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
index 8fcd940..df343a0 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -573,12 +573,40 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
"vchiq: could not connect: %d", status);
break;
+#if defined(CONFIG_64BIT)
+ case VCHIQ_IOC_CREATE_SERVICE32:
+#endif
case VCHIQ_IOC_CREATE_SERVICE: {
VCHIQ_CREATE_SERVICE_T args;
USER_SERVICE_T *user_service = NULL;
void *userdata;
int srvstate;
+#if defined(CONFIG_64BIT)
+ if (cmd == VCHIQ_IOC_CREATE_SERVICE32) {
+ VCHIQ_CREATE_SERVICE32_T args32;
+
+ if (copy_from_user
+ (&args32, (const void __user *)arg,
+ sizeof(args32)) != 0) {
+ ret = -EFAULT;
+ break;
+ }
+
+ args.params.fourcc = args32.params.fourcc;
+ args.params.callback =
+ (VCHIQ_CALLBACK_T)(unsigned long)
+ args32.params.callback;
+ args.params.userdata =
+ (void *)(unsigned long)
+ args32.params.userdata;
+ args.params.version = args32.params.version;
+ args.params.version_min = args32.params.version_min;
+ args.is_open = args32.is_open;
+ args.is_vchi = args32.is_vchi;
+ args.handle = args32.handle;
+ } else
+#endif
if (copy_from_user
(&args, (const void __user *)arg,
sizeof(args)) != 0) {
@@ -641,6 +669,19 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
}
+#if defined(CONFIG_64BIT)
+ if (cmd == VCHIQ_IOC_CREATE_SERVICE32) {
+ if (copy_to_user((void __user *)
+ &(((VCHIQ_CREATE_SERVICE32_T __user *)
+ arg)->handle),
+ (const void *)&service->handle,
+ sizeof(service->handle)) != 0) {
+ ret = -EFAULT;
+ vchiq_remove_service(
+ service->handle);
+ }
+ } else
+#endif
if (copy_to_user((void __user *)
&(((VCHIQ_CREATE_SERVICE_T __user *)
arg)->handle),
@@ -736,6 +777,49 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
ret = -EINVAL;
} break;
+#if defined(CONFIG_64BIT)
+ case VCHIQ_IOC_QUEUE_MESSAGE32: {
+ VCHIQ_QUEUE_MESSAGE32_T args32;
+
+ if (copy_from_user
+ (&args32, (const void __user *)arg,
+ sizeof(args32)) != 0) {
+ ret = -EFAULT;
+ break;
+ }
+
+ service = find_service_for_instance(instance, args32.handle);
+
+ if ((service) && (args32.count <= MAX_ELEMENTS)) {
+ /* Copy elements into kernel space */
+ VCHIQ_ELEMENT_T elements[MAX_ELEMENTS];
+ VCHIQ_ELEMENT32_T elements32[MAX_ELEMENTS];
+
+ if (copy_from_user(elements32,
+ (void *)(unsigned long)args32.elements,
+ args32.count * sizeof(VCHIQ_ELEMENT32_T)) == 0) {
+ unsigned int i;
+
+ for (i = 0; i < args32.count; i++) {
+ elements[i].data =
+ (const void *)(unsigned long)
+ elements32[i].data;
+ elements[i].size =
+ elements32[i].size;
+ }
+ status = vchiq_ioc_queue_message
+ (args32.handle,
+ elements, args32.count);
+ }
+ else {
+ ret = -EFAULT;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+ } break;
+#endif
+
case VCHIQ_IOC_QUEUE_MESSAGE: {
VCHIQ_QUEUE_MESSAGE_T args;
if (copy_from_user
@@ -762,6 +846,10 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
} break;
+#if defined(CONFIG_64BIT)
+ case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32:
+ case VCHIQ_IOC_QUEUE_BULK_RECEIVE32:
+#endif
case VCHIQ_IOC_QUEUE_BULK_TRANSMIT:
case VCHIQ_IOC_QUEUE_BULK_RECEIVE: {
VCHIQ_QUEUE_BULK_TRANSFER_T args;
@@ -770,6 +858,28 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
(cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
+#if defined(CONFIG_64BIT)
+ if (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT32 ||
+ cmd == VCHIQ_IOC_QUEUE_BULK_RECEIVE32) {
+ VCHIQ_QUEUE_BULK_TRANSFER32_T args32;
+
+ if (copy_from_user
+ (&args32, (const void __user *)arg,
+ sizeof(args32)) != 0) {
+ ret = -EFAULT;
+ break;
+ }
+
+ args.handle = args32.handle;
+ args.data = (void *)(unsigned long)args32.data;
+ args.size = args32.size;
+ args.userdata = (void *)(unsigned long)args32.userdata;
+ args.mode = args32.mode;
+
+ dir = (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT32) ?
+ VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
+ } else
+#endif
if (copy_from_user
(&args, (const void __user *)arg,
sizeof(args)) != 0) {
@@ -847,6 +957,18 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
"saved bulk_waiter %pK for pid %d",
waiter, current->pid);
+#if defined(CONFIG_64BIT)
+ if (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT32 ||
+ cmd == VCHIQ_IOC_QUEUE_BULK_RECEIVE32) {
+ if (copy_to_user((void __user *)
+ &(((VCHIQ_QUEUE_BULK_TRANSFER32_T __user *)
+ arg)->mode),
+ (const void *)&mode_waiting,
+ sizeof(mode_waiting)) != 0)
+ ret = -EFAULT;
+ } else
+#endif
+
if (copy_to_user((void __user *)
&(((VCHIQ_QUEUE_BULK_TRANSFER_T __user *)
arg)->mode),
@@ -856,6 +978,9 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
} break;
+#if defined(CONFIG_64BIT)
+ case VCHIQ_IOC_AWAIT_COMPLETION32:
+#endif
case VCHIQ_IOC_AWAIT_COMPLETION: {
VCHIQ_AWAIT_COMPLETION_T args;
@@ -865,6 +990,25 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
+#if defined(CONFIG_64BIT)
+ if (cmd == VCHIQ_IOC_AWAIT_COMPLETION32) {
+ VCHIQ_AWAIT_COMPLETION32_T args32;
+
+ if (copy_from_user(&args32, (const void __user *)arg,
+ sizeof(args32)) != 0) {
+ ret = -EFAULT;
+ break;
+ }
+
+ args.count = args32.count;
+ args.buf =
+ (VCHIQ_COMPLETION_DATA_T *)(unsigned long)
+ args32.buf;
+ args.msgbufsize = args32.msgbufsize;
+ args.msgbufcount = args32.msgbufcount;
+ args.msgbufs = (void **)(unsigned long)args32.msgbufs;
+ } else
+#endif
if (copy_from_user(&args, (const void __user *)arg,
sizeof(args)) != 0) {
ret = -EFAULT;
@@ -942,6 +1086,24 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
/* Get the pointer from user space */
msgbufcount--;
+#if defined(CONFIG_64BIT)
+ if (cmd == VCHIQ_IOC_AWAIT_COMPLETION32) {
+ u32 msgbuf32;
+
+ if (copy_from_user(&msgbuf32,
+ (const void __user *)
+ (void *)(args.msgbufs) +
+ (sizeof(u32) * msgbufcount),
+ sizeof(msgbuf32)) != 0) {
+ if (ret == 0)
+ ret = -EFAULT;
+ break;
+ }
+
+ msgbuf = (void *)(unsigned long)
+ msgbuf32;
+ } else
+#endif
if (copy_from_user(&msgbuf,
(const void __user *)
&args.msgbufs[msgbufcount],
@@ -974,6 +1136,33 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
!instance->use_close_delivered)
unlock_service(service);
+#if defined(CONFIG_64BIT)
+ if (cmd == VCHIQ_IOC_AWAIT_COMPLETION32) {
+ VCHIQ_COMPLETION_DATA32_T completion32;
+
+ completion32.reason =
+ completion->reason;
+ completion32.header =
+ (u32)(unsigned long)
+ completion->header;
+ completion32.service_userdata =
+ (u32)(unsigned long)
+ completion->service_userdata;
+ completion32.bulk_userdata =
+ (u32)(unsigned long)
+ completion->bulk_userdata;
+
+ if (copy_to_user((void __user *)(
+ (void *)args.buf +
+ ret * sizeof(VCHIQ_COMPLETION_DATA32_T)),
+ &completion32,
+ sizeof(VCHIQ_COMPLETION_DATA32_T)) != 0) {
+ if (ret == 0)
+ ret = -EFAULT;
+ break;
+ }
+ } else
+#endif
if (copy_to_user((void __user *)(
(size_t)args.buf +
ret * sizeof(VCHIQ_COMPLETION_DATA_T)),
@@ -988,6 +1177,17 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
if (msgbufcount != args.msgbufcount) {
+#if defined(CONFIG_64BIT)
+ if (cmd == VCHIQ_IOC_AWAIT_COMPLETION32) {
+ if (copy_to_user((void __user *)
+ &((VCHIQ_AWAIT_COMPLETION32_T *)arg)->
+ msgbufcount,
+ &msgbufcount,
+ sizeof(msgbufcount)) != 0) {
+ ret = -EFAULT;
+ }
+ } else
+#endif
if (copy_to_user((void __user *)
&((VCHIQ_AWAIT_COMPLETION_T *)arg)->
msgbufcount,
@@ -1004,12 +1204,32 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
DEBUG_TRACE(AWAIT_COMPLETION_LINE);
} break;
+#if defined(CONFIG_64BIT)
+ case VCHIQ_IOC_DEQUEUE_MESSAGE32:
+#endif
case VCHIQ_IOC_DEQUEUE_MESSAGE: {
VCHIQ_DEQUEUE_MESSAGE_T args;
USER_SERVICE_T *user_service;
VCHIQ_HEADER_T *header;
DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
+#if defined(CONFIG_64BIT)
+ if (cmd == VCHIQ_IOC_DEQUEUE_MESSAGE32) {
+ VCHIQ_DEQUEUE_MESSAGE32_T args32;
+
+ if (copy_from_user(&args32,
+ (const void __user *)arg,
+ sizeof(args32)) != 0) {
+ ret = -EFAULT;
+ break;
+ }
+
+ args.handle = args32.handle;
+ args.blocking = args32.blocking;
+ args.bufsize = args32.bufsize;
+ args.buf = (void *)(unsigned long)args32.buf;
+ } else
+#endif
if (copy_from_user
(&args, (const void __user *)arg,
sizeof(args)) != 0) {
@@ -1093,6 +1313,37 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
ret = vchiq_get_client_id(handle);
} break;
+#if defined(CONFIG_64BIT)
+ case VCHIQ_IOC_GET_CONFIG32: {
+ VCHIQ_GET_CONFIG32_T args32;
+ VCHIQ_CONFIG_T config;
+
+ if (copy_from_user(&args32,
+ (const void __user *)arg,
+ sizeof(args32)) != 0) {
+ ret = -EFAULT;
+ break;
+ }
+ if (args32.config_size > sizeof(config)) {
+ ret = -EINVAL;
+ break;
+ }
+ status = vchiq_get_config(instance,
+ args32.config_size,
+ &config);
+ if (status == VCHIQ_SUCCESS) {
+ if (copy_to_user((void __user *)(unsigned long)
+ args32.pconfig,
+ &config,
+ args32.config_size)
+ != 0) {
+ ret = -EFAULT;
+ break;
+ }
+ }
+ } break;
+#endif
+
case VCHIQ_IOC_GET_CONFIG: {
VCHIQ_GET_CONFIG_T args;
VCHIQ_CONFIG_T config;
@@ -1136,6 +1387,21 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
args.handle, args.option, args.value);
} break;
+#if defined(CONFIG_64BIT)
+ case VCHIQ_IOC_DUMP_PHYS_MEM32: {
+ VCHIQ_DUMP_MEM32_T args32;
+
+ if (copy_from_user(&args32,
+ (const void __user *)arg,
+ sizeof(args32)) != 0) {
+ ret = -EFAULT;
+ break;
+ }
+ dump_phys_mem((void *)(unsigned long)args32.virt_addr,
+ args32.num_bytes);
+ } break;
+#endif
+
case VCHIQ_IOC_DUMP_PHYS_MEM: {
VCHIQ_DUMP_MEM_T args;
@@ -1660,6 +1926,9 @@ static const struct file_operations
vchiq_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = vchiq_ioctl,
+#if defined(CONFIG_64BIT)
+ .compat_ioctl = vchiq_ioctl,
+#endif
.open = vchiq_open,
.release = vchiq_release,
.read = vchiq_read
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h
index 377e8e4..f4cc324 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h
@@ -93,6 +93,13 @@ typedef struct {
unsigned int size;
} VCHIQ_ELEMENT_T;
+#if defined(CONFIG_64BIT)
+typedef struct {
+ u32 data;
+ unsigned int size;
+} VCHIQ_ELEMENT32_T;
+#endif
+
typedef unsigned int VCHIQ_SERVICE_HANDLE_T;
typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, VCHIQ_HEADER_T *,
@@ -104,6 +111,14 @@ typedef struct vchiq_service_base_struct {
void *userdata;
} VCHIQ_SERVICE_BASE_T;
+#if defined(CONFIG_64BIT)
+typedef struct vchiq_service_base_struct32 {
+ int fourcc;
+ u32 callback;
+ u32 userdata;
+} VCHIQ_SERVICE_BASE32_T;
+#endif
+
typedef struct vchiq_service_params_struct {
int fourcc;
VCHIQ_CALLBACK_T callback;
@@ -112,6 +127,16 @@ typedef struct vchiq_service_params_struct {
short version_min; /* Update for incompatible changes */
} VCHIQ_SERVICE_PARAMS_T;
+#if defined(CONFIG_64BIT)
+typedef struct vchiq_service_params_struct32 {
+ int fourcc;
+ u32 callback;
+ u32 userdata;
+ short version; /* Increment for non-trivial changes */
+ short version_min; /* Update for incompatible changes */
+} VCHIQ_SERVICE_PARAMS32_T;
+#endif
+
typedef struct vchiq_config_struct {
unsigned int max_msg_size;
unsigned int bulk_threshold; /* The message size above which it
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h
index 6137ae9..001d346 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h
@@ -47,12 +47,29 @@ typedef struct {
unsigned int handle; /* OUT */
} VCHIQ_CREATE_SERVICE_T;
+#if defined(CONFIG_64BIT)
+typedef struct {
+ VCHIQ_SERVICE_PARAMS32_T params;
+ int is_open;
+ int is_vchi;
+ unsigned int handle; /* OUT */
+} VCHIQ_CREATE_SERVICE32_T;
+#endif
+
typedef struct {
unsigned int handle;
unsigned int count;
const VCHIQ_ELEMENT_T *elements;
} VCHIQ_QUEUE_MESSAGE_T;
+#if defined(CONFIG_64BIT)
+typedef struct {
+ unsigned int handle;
+ unsigned int count;
+ u32 elements;
+} VCHIQ_QUEUE_MESSAGE32_T;
+#endif
+
typedef struct {
unsigned int handle;
void *data;
@@ -61,6 +78,16 @@ typedef struct {
VCHIQ_BULK_MODE_T mode;
} VCHIQ_QUEUE_BULK_TRANSFER_T;
+#if defined(CONFIG_64BIT)
+typedef struct {
+ unsigned int handle;
+ u32 data;
+ unsigned int size;
+ u32 userdata;
+ VCHIQ_BULK_MODE_T mode;
+} VCHIQ_QUEUE_BULK_TRANSFER32_T;
+#endif
+
typedef struct {
VCHIQ_REASON_T reason;
VCHIQ_HEADER_T *header;
@@ -68,6 +95,15 @@ typedef struct {
void *bulk_userdata;
} VCHIQ_COMPLETION_DATA_T;
+#if defined(CONFIG_64BIT)
+typedef struct {
+ VCHIQ_REASON_T reason;
+ u32 header;
+ u32 service_userdata;
+ u32 bulk_userdata;
+} VCHIQ_COMPLETION_DATA32_T;
+#endif
+
typedef struct {
unsigned int count;
VCHIQ_COMPLETION_DATA_T *buf;
@@ -76,6 +112,16 @@ typedef struct {
void **msgbufs;
} VCHIQ_AWAIT_COMPLETION_T;
+#if defined(CONFIG_64BIT)
+typedef struct {
+ unsigned int count;
+ u32 buf;
+ unsigned int msgbufsize;
+ unsigned int msgbufcount; /* IN/OUT */
+ u32 msgbufs;
+} VCHIQ_AWAIT_COMPLETION32_T;
+#endif
+
typedef struct {
unsigned int handle;
int blocking;
@@ -83,11 +129,27 @@ typedef struct {
void *buf;
} VCHIQ_DEQUEUE_MESSAGE_T;
+#if defined(CONFIG_64BIT)
+typedef struct {
+ unsigned int handle;
+ int blocking;
+ unsigned int bufsize;
+ u32 buf;
+} VCHIQ_DEQUEUE_MESSAGE32_T;
+#endif
+
typedef struct {
unsigned int config_size;
VCHIQ_CONFIG_T *pconfig;
} VCHIQ_GET_CONFIG_T;
+#if defined(CONFIG_64BIT)
+typedef struct {
+ unsigned int config_size;
+ u32 pconfig;
+} VCHIQ_GET_CONFIG32_T;
+#endif
+
typedef struct {
unsigned int handle;
VCHIQ_SERVICE_OPTION_T option;
@@ -99,24 +161,60 @@ typedef struct {
size_t num_bytes;
} VCHIQ_DUMP_MEM_T;
+#if defined(CONFIG_64BIT)
+typedef struct {
+ u32 virt_addr;
+ u32 num_bytes;
+} VCHIQ_DUMP_MEM32_T;
+
+#endif
+
#define VCHIQ_IOC_CONNECT _IO(VCHIQ_IOC_MAGIC, 0)
#define VCHIQ_IOC_SHUTDOWN _IO(VCHIQ_IOC_MAGIC, 1)
#define VCHIQ_IOC_CREATE_SERVICE \
_IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE_T)
+#if defined(CONFIG_64BIT)
+#define VCHIQ_IOC_CREATE_SERVICE32 \
+ _IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE32_T)
+#endif
#define VCHIQ_IOC_REMOVE_SERVICE _IO(VCHIQ_IOC_MAGIC, 3)
#define VCHIQ_IOC_QUEUE_MESSAGE \
_IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T)
+#if defined(CONFIG_64BIT)
+#define VCHIQ_IOC_QUEUE_MESSAGE32 \
+ _IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE32_T)
+#endif
#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT \
_IOWR(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T)
+#if defined(CONFIG_64BIT)
+#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT32 \
+ _IOWR(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER32_T)
+#endif
#define VCHIQ_IOC_QUEUE_BULK_RECEIVE \
_IOWR(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T)
+#if defined(CONFIG_64BIT)
+#define VCHIQ_IOC_QUEUE_BULK_RECEIVE32 \
+ _IOWR(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER32_T)
+#endif
#define VCHIQ_IOC_AWAIT_COMPLETION \
_IOWR(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T)
+#if defined(CONFIG_64BIT)
+#define VCHIQ_IOC_AWAIT_COMPLETION32 \
+ _IOWR(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION32_T)
+#endif
#define VCHIQ_IOC_DEQUEUE_MESSAGE \
_IOWR(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T)
+#if defined(CONFIG_64BIT)
+#define VCHIQ_IOC_DEQUEUE_MESSAGE32 \
+ _IOWR(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE32_T)
+#endif
#define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9)
#define VCHIQ_IOC_GET_CONFIG \
_IOWR(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T)
+#if defined(CONFIG_64BIT)
+#define VCHIQ_IOC_GET_CONFIG32 \
+ _IOWR(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG32_T)
+#endif
#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11)
#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12)
#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13)
@@ -124,6 +222,10 @@ typedef struct {
_IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T)
#define VCHIQ_IOC_DUMP_PHYS_MEM \
_IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T)
+#if defined(CONFIG_64BIT)
+#define VCHIQ_IOC_DUMP_PHYS_MEM32 \
+ _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM32_T)
+#endif
#define VCHIQ_IOC_LIB_VERSION _IO(VCHIQ_IOC_MAGIC, 16)
#define VCHIQ_IOC_CLOSE_DELIVERED _IO(VCHIQ_IOC_MAGIC, 17)
#define VCHIQ_IOC_MAX 17
--
2.10.2
^ permalink raw reply related
* RE: [RESEND PATCH v3 1/2] PM/devfreq: add suspend frequency support
From: MyungJoo Ham @ 2016-11-08 2:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CGME20161107063403epcas5p4f8d0d95b9a997ca7bf2b2702c3e5d337@epcas5p4.samsung.com>
> Add suspend frequency support and if needed set it to
> the frequency obtained from the suspend opp (can be defined
> using opp-v2 bindings and is optional).
>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
> Changes in v2:
> - use update_devfreq() instead devfreq_update_status()
> Changes in v3:
> - fix build error
>
> drivers/devfreq/devfreq.c | 18 ++++++++++++++++--
> drivers/devfreq/governor_simpleondemand.c | 9 +++++++++
> include/linux/devfreq.h | 8 ++++++++
> 3 files changed, 33 insertions(+), 2 deletions(-)
>
>
[]
> +void devfreq_opp_get_suspend_opp(struct device *dev, struct devfreq *devfreq)
> +{
> + struct dev_pm_opp *suspend_opp;
> +
> + rcu_read_lock();
> + suspend_opp = dev_pm_opp_get_suspend_opp(dev);
> + if (suspend_opp)
> + devfreq->suspend_freq = dev_pm_opp_get_freq(suspend_opp);
> + rcu_read_unlock();
> +}
> +EXPORT_SYMBOL(devfreq_opp_get_suspend_opp);
> +
Why do we need this function?
This could be done at the init time (devfreq_add_device).
Plus, when it does not have dev_pm_opp_get_suspend_opp() available,
> /**
> * devfreq_register_opp_notifier() - Helper function to get devfreq notified
> * for any changes in the OPP availability
> diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c
> index ae72ba5..a3efee0 100644
> --- a/drivers/devfreq/governor_simpleondemand.c
> +++ b/drivers/devfreq/governor_simpleondemand.c
> @@ -29,6 +29,15 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
> struct devfreq_simple_ondemand_data *data = df->data;
> unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX;
>
> + /*
> + * if devfreq in suspend status and have suspend_freq,
> + * the frequency need to set to suspend_freq
> + */
> + if (df->stop_polling && df->suspend_freq) {
> + *freq = df->suspend_freq;
> + return 0;
> + }
> +
Why are you adding this? "Stop polling" does not necessarily mean that
we are doing suspend-to-RAM/disk. It may really mean just to stop at
the current frequency.
Cheers,
MyungJoo
^ permalink raw reply
* [PATCH 2/2] mm: hugetlb: support gigantic surplus pages
From: Huang Shijie @ 2016-11-08 2:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161107162504.17591806@thinkpad>
On Mon, Nov 07, 2016 at 04:25:04PM +0100, Gerald Schaefer wrote:
> On Thu, 3 Nov 2016 10:51:38 +0800
> Huang Shijie <shijie.huang@arm.com> wrote:
>
> > When testing the gigantic page whose order is too large for the buddy
> > allocator, the libhugetlbfs test case "counter.sh" will fail.
> >
> > The failure is caused by:
> > 1) kernel fails to allocate a gigantic page for the surplus case.
> > And the gather_surplus_pages() will return NULL in the end.
> >
> > 2) The condition checks for "over-commit" is wrong.
> >
> > This patch adds code to allocate the gigantic page in the
> > __alloc_huge_page(). After this patch, gather_surplus_pages()
> > can return a gigantic page for the surplus case.
> >
> > This patch also changes the condition checks for:
> > return_unused_surplus_pages()
> > nr_overcommit_hugepages_store()
> >
> > After this patch, the counter.sh can pass for the gigantic page.
> >
> > Acked-by: Steve Capper <steve.capper@arm.com>
> > Signed-off-by: Huang Shijie <shijie.huang@arm.com>
> > ---
> > mm/hugetlb.c | 15 ++++++++++-----
> > 1 file changed, 10 insertions(+), 5 deletions(-)
> >
> > diff --git a/mm/hugetlb.c b/mm/hugetlb.c
> > index 0bf4444..2b67aff 100644
> > --- a/mm/hugetlb.c
> > +++ b/mm/hugetlb.c
> > @@ -1574,7 +1574,7 @@ static struct page *__alloc_huge_page(struct hstate *h,
> > struct page *page;
> > unsigned int r_nid;
> >
> > - if (hstate_is_gigantic(h))
> > + if (hstate_is_gigantic(h) && !gigantic_page_supported())
> > return NULL;
>
> Is it really possible to stumble over gigantic pages w/o having
> gigantic_page_supported()?
>
> Also, I've just tried this on s390 and counter.sh still fails after these
> patches, and it should fail on all archs as long as you use the gigantic
I guess the failure you met is caused by the libhugetlbfs itself, there are
several bugs in the libhugetlbfs. I have a patch set for the
libhugetlbfs too. I will send it as soon as possible.
> hugepage size as default hugepage size. This is because you only changed
> nr_overcommit_hugepages_store(), which handles nr_overcommit_hugepages
> in sysfs, and missed hugetlb_overcommit_handler() which handles
> /proc/sys/vm/nr_overcommit_hugepages for the default sized hugepages.
This is wrong. :)
I did have an extra patch to fix the hugetlb_overcommit_handler().
but the counters.sh does not use the /proc/sys/vm/nr_overcommit_hugepages.
Please grep it in the code.
Thanks
Huang Shijie
^ permalink raw reply
* [PATCH 1/2] arm64: hugetlb: remove the wrong pmd check in find_num_contig()
From: Huang Shijie @ 2016-11-08 2:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161104154814.hizb5277wp2f3enj@localhost>
On Fri, Nov 04, 2016 at 09:48:14AM -0600, Catalin Marinas wrote:
> On Fri, Nov 04, 2016 at 10:52:17AM +0800, Huang Shijie wrote:
> > On Thu, Nov 03, 2016 at 06:16:16PM -0600, Catalin Marinas wrote:
> > > On Thu, Nov 03, 2016 at 10:27:38AM +0800, Huang Shijie wrote:
> > > > diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
> > > > index 2e49bd2..4811ef1 100644
> > > > --- a/arch/arm64/mm/hugetlbpage.c
> > > > +++ b/arch/arm64/mm/hugetlbpage.c
> > > > @@ -61,10 +61,6 @@ static int find_num_contig(struct mm_struct *mm, unsigned long addr,
> > > > return 1;
> > > > }
> > > > pmd = pmd_offset(pud, addr);
> > > > - if (!pmd_present(*pmd)) {
> > > > - VM_BUG_ON(!pmd_present(*pmd));
> > > > - return 1;
> > > > - }
> > > > if ((pte_t *)pmd == ptep) {
> > > > *pgsize = PMD_SIZE;
> > > > return CONT_PMDS;
> > >
> > > BTW, for the !pud_present() and !pgd_present() cases, shouldn't
> > > find_num_contig() actually return 0? These are more likely real bugs, so
> > > no point in setting the huge pte.
> >
> > The kernel will not call the find_num_contig() if the PGD/PUD are empty.
> > Please see the code in the hugetlb_fault().
> >
> > ------------------------------------------------------
> > ptep = huge_pte_offset(mm, address);
> > if (ptep) {
> > ...............................
> > } else {
> > ptep = huge_pte_alloc(mm, address, huge_page_size(h));
> > if (!ptep)
> > return VM_FAULT_OOM;
> > }
> > ------------------------------------------------------
>
> Exactly. So what is the reason for returning 1 if !pgd_present()? Would
I think the author was too cautious for returning 1 if !pgd_present().
:)
> removing the checks entirely or adding BUG() be a better option?
I will remove the checks in the next version.
Thanks
Huang Shijie
^ permalink raw reply
* [PATCHv3] ARM: dts: socfpga: Enable QSPI on the Cyclone5 sockit
From: Dinh Nguyen @ 2016-11-08 2:28 UTC (permalink / raw)
To: linux-arm-kernel
From: Dinh Nguyen <dinguyen@opensource.altera.com>
Enable the QSPI node and add the flash chip.
Signed-off-by: Dinh Nguyen <dinguyen@opensource.altera.com>
---
v3: Use n25q00 for the compatible entry for the flash part and
tested on SoCKit
v2: Remove partition entries for the SoCKIT
---
arch/arm/boot/dts/socfpga_cyclone5_sockit.dts | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts
index 02e22f5..d59f6f2 100644
--- a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts
+++ b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts
@@ -175,6 +175,27 @@
status = "okay";
};
+&qspi {
+ status = "okay";
+
+ flash: flash at 0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "n25q00";
+ reg = <0>;
+ spi-max-frequency = <100000000>;
+
+ m25p,fast-read;
+ cdns,page-size = <256>;
+ cdns,block-size = <16>;
+ cdns,read-delay = <4>;
+ cdns,tshsl-ns = <50>;
+ cdns,tsd2d-ns = <50>;
+ cdns,tchsh-ns = <4>;
+ cdns,tslch-ns = <4>;
+ };
+};
+
&usb1 {
status = "okay";
};
--
2.8.3
^ permalink raw reply related
* Summary of LPC guest MSI discussion in Santa Fe (was: Re: [RFC 0/8] KVM PCIe/MSI passthrough on ARM/ARM64 (Alt II))
From: Will Deacon @ 2016-11-08 2:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103220205.37715b49@t450s.home>
Hi all,
I figured this was a reasonable post to piggy-back on for the LPC minutes
relating to guest MSIs on arm64.
On Thu, Nov 03, 2016 at 10:02:05PM -0600, Alex Williamson wrote:
> We can always have QEMU reject hot-adding the device if the reserved
> region overlaps existing guest RAM, but I don't even really see how we
> advise users to give them a reasonable chance of avoiding that
> possibility. Apparently there are also ARM platforms where MSI pages
> cannot be remapped to support the previous programmable user/VM
> address, is it even worthwhile to support those platforms? Does that
> decision influence whether user programmable MSI reserved regions are
> really a second class citizen to fixed reserved regions? I expect
> we'll be talking about this tomorrow morning, but I certainly haven't
> come up with any viable solutions to this. Thanks,
At LPC last week, we discussed guest MSIs on arm64 as part of the PCI
microconference. I presented some slides to illustrate some of the issues
we're trying to solve:
http://www.willdeacon.ukfsn.org/bitbucket/lpc-16/msi-in-guest-arm64.pdf
Punit took some notes (thanks!) on the etherpad here:
https://etherpad.openstack.org/p/LPC2016_PCI
although the discussion was pretty lively and jumped about, so I've had
to go from memory where the notes didn't capture everything that was
said.
To summarise, arm64 platforms differ in their handling of MSIs when compared
to x86:
1. The physical memory map is not standardised (Jon pointed out that
this is something that was realised late on)
2. MSIs are usually treated the same as DMA writes, in that they must be
mapped by the SMMU page tables so that they target a physical MSI
doorbell
3. On some platforms, MSIs bypass the SMMU entirely (e.g. due to an MSI
doorbell built into the PCI RC)
4. Platforms typically have some set of addresses that abort before
reaching the SMMU (e.g. because the PCI identifies them as P2P).
All of this means that userspace (QEMU) needs to identify the memory
regions corresponding to points (3) and (4) and ensure that they are
not allocated in the guest physical (IPA) space. For platforms that can
remap the MSI doorbell as in (2), then some space also needs to be
allocated for that.
Rather than treat these as separate problems, a better interface is to
tell userspace about a set of reserved regions, and have this include
the MSI doorbell, irrespective of whether or not it can be remapped.
Don suggested that we statically pick an address for the doorbell in a
similar way to x86, and have the kernel map it there. We could even pick
0xfee00000. If it conflicts with a reserved region on the platform (due
to (4)), then we'd obviously have to (deterministically?) allocate it
somewhere else, but probably within the bottom 4G.
The next question is how to tell userspace about all of the reserved
regions. Initially, the idea was to extend VFIO, however Alex pointed
out a horrible scenario:
1. QEMU spawns a VM on system 0
2. VM is migrated to system 1
3. QEMU attempts to passthrough a device using PCI hotplug
In this scenario, the guest memory map is chosen at step (1), yet there
is no VFIO fd available to determine the reserved regions. Furthermore,
the reserved regions may vary between system 0 and system 1. This pretty
much rules out using VFIO to determine the reserved regions. Alex suggested
that the SMMU driver can advertise the regions via /sys/class/iommu/. This
would solve part of the problem, but migration between systems with
different memory maps can still cause problems if the reserved regions
of the new system conflict with the guest memory map chosen by QEMU.
Jon pointed out that most people are pretty conservative about hardware
choices when migrating between them -- that is, they may only migrate
between different revisions of the same SoC, or they know ahead of time
all of the memory maps they want to support and this could be communicated
by way of configuration to libvirt. It would be up to QEMU to fail the
hotplug if it detected a conflict. Alex asked if there was a security
issue with DMA bypassing the SMMU, but there aren't currently any systems
where that is known to happen. Such a system would surely not be safe for
passthrough.
Ben mused that a way to handle conflicts dynamically might be to hotplug
on the entire host bridge in the guest, passing firmware tables describing
the new reserved regions as a property of the host bridge. Whilst this
may well solve the issue, it was largely considered future work due to
its invasive nature and dependency on firmware tables (and guest support)
that do not currently exist.
Will
^ permalink raw reply
* [PATCH v9 0/8] power: add power sequence library
From: Peter Chen @ 2016-11-08 2:51 UTC (permalink / raw)
To: linux-arm-kernel
Hi all,
This is a follow-up for my last power sequence framework patch set [1].
According to Rob Herring and Ulf Hansson's comments[2]. The kinds of
power sequence instances will be added at postcore_initcall, the match
criteria is compatible string first, if the compatible string is not
matched between dts and library, it will try to use generic power sequence.
The host driver just needs to call of_pwrseq_on/of_pwrseq_off
if only one power sequence instance is needed, for more power sequences
are used, using of_pwrseq_on_list/of_pwrseq_off_list instead (eg, USB hub driver).
In future, if there are special power sequence requirements, the special
power sequence library can be created.
This patch set is tested on i.mx6 sabresx evk using a dts change, I use
two hot-plug devices to simulate this use case, the related binding
change is updated at patch [1/6], The udoo board changes were tested
using my last power sequence patch set.[3]
Except for hard-wired MMC and USB devices, I find the USB ULPI PHY also
need to power on itself before it can be found by ULPI bus.
[1] http://www.spinics.net/lists/linux-usb/msg142755.html
[2] http://www.spinics.net/lists/linux-usb/msg143106.html
[3] http://www.spinics.net/lists/linux-usb/msg142815.html
Changes for v9:
- Add Vaibhav Hiremath's reviewed-by [Patch 4/8]
- Rebase to v4.9-rc1
Changes for v8:
- Allocate one extra pwrseq instance if pwrseq_get has succeed, it can avoid
preallocate instances problem which the number of instance is decided at
compile time, thanks for Heiko Stuebner's suggestion [Patch 2/8]
- Delete pwrseq_compatible_sample.c which is the demo purpose to show compatible
match method. [Patch 2/8]
- Add Maciej S. Szmigiero's tested-by. [Patch 7/8]
Changes for v7:
- Create kinds of power sequence instance at postcore_initcall, and match
the instance with node using compatible string, the beneit of this is
the host driver doesn't need to consider which pwrseq instance needs
to be used, and pwrseq core will match it, however, it eats some memories
if less power sequence instances are used. [Patch 2/8]
- Add pwrseq_compatible_sample.c to test match pwrseq using device_id. [Patch 2/8]
- Fix the comments Vaibhav Hiremath adds for error path for clock and do not
use device_node for parameters at pwrseq_on. [Patch 2/8]
- Simplify the caller to use power sequence, follows Alan's commnets [Patch 4/8]
- Tested three pwrseq instances together using both specific compatible string and
generic libraries.
Changes for v6:
- Add Matthias Kaehlcke's Reviewed-by and Tested-by. (patch [2/6])
- Change chipidea core of_node assignment for coming user. (patch [5/6])
- Applies Joshua Clayton's three dts changes for two boards,
the USB device's reg has only #address-cells, but without #size-cells.
Changes for v5:
- Delete pwrseq_register/pwrseq_unregister, which is useless currently
- Fix the linker error when the pwrseq user is compiled as module
Changes for v4:
- Create the patch on next-20160722
- Fix the of_node is not NULL after chipidea driver is unbinded [Patch 5/6]
- Using more friendly wait method for reset gpio [Patch 2/6]
- Support multiple input clocks [Patch 2/6]
- Add Rob Herring's ack for DT changes
- Add Joshua Clayton's Tested-by
Changes for v3:
- Delete "power-sequence" property at binding-doc, and change related code
at both library and user code.
- Change binding-doc example node name with Rob's comments
- of_get_named_gpio_flags only gets the gpio, but without setting gpio flags,
add additional code request gpio with proper gpio flags
- Add Philipp Zabel's Ack and MAINTAINER's entry
Changes for v2:
- Delete "pwrseq" prefix and clock-names for properties at dt binding
- Should use structure not but its pointer for kzalloc
- Since chipidea core has no of_node, let core's of_node equals glue
layer's at core's probe
Joshua Clayton (2):
ARM: dts: imx6qdl: Enable usb node children with <reg>
ARM: dts: imx6q-evi: Fix onboard hub reset line
Peter Chen (6):
binding-doc: power: pwrseq-generic: add binding doc for generic power
sequence library
power: add power sequence library
binding-doc: usb: usb-device: add optional properties for power
sequence
usb: core: add power sequence handling for USB devices
usb: chipidea: let chipidea core device of_node equal's glue layer
device of_node
ARM: dts: imx6qdl-udoo.dtsi: fix onboard USB HUB property
.../bindings/power/pwrseq/pwrseq-generic.txt | 48 ++++++
.../devicetree/bindings/usb/usb-device.txt | 10 +-
MAINTAINERS | 9 +
arch/arm/boot/dts/imx6q-evi.dts | 25 +--
arch/arm/boot/dts/imx6qdl-udoo.dtsi | 26 ++-
arch/arm/boot/dts/imx6qdl.dtsi | 6 +
drivers/power/Kconfig | 1 +
drivers/power/Makefile | 1 +
drivers/power/pwrseq/Kconfig | 19 ++
drivers/power/pwrseq/Makefile | 2 +
drivers/power/pwrseq/core.c | 191 +++++++++++++++++++++
drivers/power/pwrseq/pwrseq_generic.c | 183 ++++++++++++++++++++
drivers/usb/chipidea/core.c | 27 ++-
drivers/usb/core/hub.c | 41 ++++-
drivers/usb/core/hub.h | 1 +
include/linux/power/pwrseq.h | 72 ++++++++
16 files changed, 621 insertions(+), 41 deletions(-)
create mode 100644 Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
create mode 100644 drivers/power/pwrseq/Kconfig
create mode 100644 drivers/power/pwrseq/Makefile
create mode 100644 drivers/power/pwrseq/core.c
create mode 100644 drivers/power/pwrseq/pwrseq_generic.c
create mode 100644 include/linux/power/pwrseq.h
--
2.7.4
^ permalink raw reply
* [PATCH v9 1/8] binding-doc: power: pwrseq-generic: add binding doc for generic power sequence library
From: Peter Chen @ 2016-11-08 2:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478573472-29516-1-git-send-email-peter.chen@nxp.com>
Add binding doc for generic power sequence library.
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---
.../bindings/power/pwrseq/pwrseq-generic.txt | 48 ++++++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
new file mode 100644
index 0000000..ebf0d47
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
@@ -0,0 +1,48 @@
+The generic power sequence library
+
+Some hard-wired devices (eg USB/MMC) need to do power sequence before
+the device can be enumerated on the bus, the typical power sequence
+like: enable USB PHY clock, toggle reset pin, etc. But current
+Linux device driver lacks of such code to do it, it may cause some
+hard-wired devices works abnormal or can't be recognized by
+controller at all. The power sequence will be done before this device
+can be found at the bus.
+
+The power sequence properties is under the device node.
+
+Optional properties:
+- clocks: the input clocks for device.
+- reset-gpios: Should specify the GPIO for reset.
+- reset-duration-us: the duration in microsecond for assert reset signal.
+
+Below is the example of USB power sequence properties on USB device
+nodes which have two level USB hubs.
+
+&usbotg1 {
+ vbus-supply = <®_usb_otg1_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usb_otg1_id>;
+ status = "okay";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ genesys: hub at 1 {
+ compatible = "usb5e3,608";
+ reg = <1>;
+
+ clocks = <&clks IMX6SX_CLK_CKO>;
+ reset-gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; /* hub reset pin */
+ reset-duration-us = <10>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ asix: ethernet at 1 {
+ compatible = "usbb95,1708";
+ reg = <1>;
+
+ clocks = <&clks IMX6SX_CLK_IPG>;
+ reset-gpios = <&gpio4 6 GPIO_ACTIVE_LOW>; /* ethernet_rst */
+ reset-duration-us = <15>;
+ };
+ };
+};
--
2.7.4
^ permalink raw reply related
* [PATCH v9 2/8] power: add power sequence library
From: Peter Chen @ 2016-11-08 2:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478573472-29516-1-git-send-email-peter.chen@nxp.com>
We have an well-known problem that the device needs to do some power
sequence before it can be recognized by related host, the typical
example like hard-wired mmc devices and usb devices.
This power sequence is hard to be described at device tree and handled by
related host driver, so we have created a common power sequence
library to cover this requirement. The core code has supplied
some common helpers for host driver, and individual power sequence
libraries handle kinds of power sequence for devices. The pwrseq
librares always need to allocate extra instance for compatible
string match.
pwrseq_generic is intended for general purpose of power sequence, which
handles gpios and clocks currently, and can cover other controls in
future. The host driver just needs to call of_pwrseq_on/of_pwrseq_off
if only one power sequence is needed, else call of_pwrseq_on_list
/of_pwrseq_off_list instead (eg, USB hub driver).
For new power sequence library, it can add its compatible string
to pwrseq_of_match_table, then the pwrseq core will match it with
DT's, and choose this library at runtime.
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Tested-by: Matthias Kaehlcke <mka@chromium.org>
---
MAINTAINERS | 9 ++
drivers/power/Kconfig | 1 +
drivers/power/Makefile | 1 +
drivers/power/pwrseq/Kconfig | 19 ++++
drivers/power/pwrseq/Makefile | 2 +
drivers/power/pwrseq/core.c | 191 ++++++++++++++++++++++++++++++++++
drivers/power/pwrseq/pwrseq_generic.c | 183 ++++++++++++++++++++++++++++++++
include/linux/power/pwrseq.h | 72 +++++++++++++
8 files changed, 478 insertions(+)
create mode 100644 drivers/power/pwrseq/Kconfig
create mode 100644 drivers/power/pwrseq/Makefile
create mode 100644 drivers/power/pwrseq/core.c
create mode 100644 drivers/power/pwrseq/pwrseq_generic.c
create mode 100644 include/linux/power/pwrseq.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 1cd38a7..5dab975 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9612,6 +9612,15 @@ F: include/linux/pm_*
F: include/linux/powercap.h
F: drivers/powercap/
+POWER SEQUENCE LIBRARY
+M: Peter Chen <Peter.Chen@nxp.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
+L: linux-pm at vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/power/pwrseq/
+F: drivers/power/pwrseq/
+F: include/linux/power/pwrseq.h/
+
POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS
M: Sebastian Reichel <sre@kernel.org>
L: linux-pm at vger.kernel.org
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 63454b5..c1bb046 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,3 +1,4 @@
source "drivers/power/avs/Kconfig"
source "drivers/power/reset/Kconfig"
source "drivers/power/supply/Kconfig"
+source "drivers/power/pwrseq/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ff35c71..7db8035 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_POWER_AVS) += avs/
obj-$(CONFIG_POWER_RESET) += reset/
obj-$(CONFIG_POWER_SUPPLY) += supply/
+obj-$(CONFIG_POWER_SEQUENCE) += pwrseq/
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
new file mode 100644
index 0000000..3859a67
--- /dev/null
+++ b/drivers/power/pwrseq/Kconfig
@@ -0,0 +1,19 @@
+#
+# Power Sequence library
+#
+
+config POWER_SEQUENCE
+ bool
+
+menu "Power Sequence Support"
+
+config PWRSEQ_GENERIC
+ bool "Generic power sequence control"
+ depends on OF
+ select POWER_SEQUENCE
+ help
+ It is used for drivers which needs to do power sequence
+ (eg, turn on clock, toggle reset gpio) before the related
+ devices can be found by hardware. This generic one can be
+ used for common power sequence control.
+endmenu
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
new file mode 100644
index 0000000..ad82389
--- /dev/null
+++ b/drivers/power/pwrseq/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_POWER_SEQUENCE) += core.o
+obj-$(CONFIG_PWRSEQ_GENERIC) += pwrseq_generic.o
diff --git a/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c
new file mode 100644
index 0000000..9cb1223
--- /dev/null
+++ b/drivers/power/pwrseq/core.c
@@ -0,0 +1,191 @@
+/*
+ * core.c power sequence core file
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Author: Peter Chen <peter.chen@nxp.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/power/pwrseq.h>
+
+static DEFINE_MUTEX(pwrseq_list_mutex);
+static LIST_HEAD(pwrseq_list);
+
+int pwrseq_get(struct device_node *np, struct pwrseq *p)
+{
+ if (p && p->get)
+ return p->get(np, p);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(pwrseq_get);
+
+int pwrseq_on(struct pwrseq *p)
+{
+ if (p && p->on)
+ return p->on(p);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(pwrseq_on);
+
+void pwrseq_off(struct pwrseq *p)
+{
+ if (p && p->off)
+ p->off(p);
+}
+EXPORT_SYMBOL_GPL(pwrseq_off);
+
+void pwrseq_put(struct pwrseq *p)
+{
+ if (p && p->put)
+ p->put(p);
+}
+EXPORT_SYMBOL_GPL(pwrseq_put);
+
+int pwrseq_suspend(struct pwrseq *p)
+{
+ if (p && p->suspend)
+ return p->suspend(p);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwrseq_suspend);
+
+int pwrseq_resume(struct pwrseq *p)
+{
+ if (p && p->resume)
+ return p->resume(p);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwrseq_resume);
+
+void pwrseq_register(struct pwrseq *pwrseq)
+{
+ mutex_lock(&pwrseq_list_mutex);
+ list_add(&pwrseq->node, &pwrseq_list);
+ mutex_unlock(&pwrseq_list_mutex);
+}
+EXPORT_SYMBOL_GPL(pwrseq_register);
+
+void pwrseq_unregister(struct pwrseq *pwrseq)
+{
+ mutex_lock(&pwrseq_list_mutex);
+ list_del(&pwrseq->node);
+ mutex_unlock(&pwrseq_list_mutex);
+}
+EXPORT_SYMBOL_GPL(pwrseq_unregister);
+
+static struct pwrseq *pwrseq_find_available_instance(struct device_node *np)
+{
+ struct pwrseq *pwrseq;
+
+ list_for_each_entry(pwrseq, &pwrseq_list, node) {
+ if (pwrseq->used)
+ continue;
+
+ /* compare compatible string for pwrseq node */
+ if (of_match_node(pwrseq->pwrseq_of_match_table, np)) {
+ pwrseq->used = true;
+ return pwrseq;
+ }
+
+ /* return generic pwrseq instance */
+ if (!strcmp(pwrseq->pwrseq_of_match_table->compatible,
+ "generic")) {
+ pr_debug("using generic pwrseq instance for %s\n",
+ np->full_name);
+ pwrseq->used = true;
+ return pwrseq;
+ }
+ }
+ pr_warn("Can't find any pwrseq instances for %s\n", np->full_name);
+
+ return NULL;
+}
+
+struct pwrseq *of_pwrseq_on(struct device_node *np)
+{
+ struct pwrseq *pwrseq;
+ int ret;
+
+ pwrseq = pwrseq_find_available_instance(np);
+ if (!pwrseq)
+ return ERR_PTR(-ENONET);
+
+ ret = pwrseq_get(np, pwrseq);
+ if (ret) {
+ /* Mark current pwrseq as unused */
+ pwrseq->used = false;
+ return ERR_PTR(ret);
+ }
+
+ ret = pwrseq_on(pwrseq);
+ if (ret)
+ goto pwr_put;
+
+ return pwrseq;
+
+pwr_put:
+ pwrseq_put(pwrseq);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_on);
+
+void of_pwrseq_off(struct pwrseq *pwrseq)
+{
+ pwrseq_off(pwrseq);
+ pwrseq_put(pwrseq);
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_off);
+
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
+{
+ struct pwrseq *pwrseq;
+ struct pwrseq_list_per_dev *pwrseq_list_node;
+
+ pwrseq = of_pwrseq_on(np);
+ if (IS_ERR(pwrseq))
+ return PTR_ERR(pwrseq);
+
+ pwrseq_list_node = kzalloc(sizeof(*pwrseq_list_node), GFP_KERNEL);
+ if (!pwrseq_list_node) {
+ of_pwrseq_off(pwrseq);
+ return -ENOMEM;
+ }
+ pwrseq_list_node->pwrseq = pwrseq;
+ list_add(&pwrseq_list_node->list, head);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_on_list);
+
+void of_pwrseq_off_list(struct list_head *head)
+{
+ struct pwrseq *pwrseq;
+ struct pwrseq_list_per_dev *pwrseq_list_node, *tmp_node;
+
+ list_for_each_entry_safe(pwrseq_list_node, tmp_node, head, list) {
+ pwrseq = pwrseq_list_node->pwrseq;
+ of_pwrseq_off(pwrseq);
+ list_del(&pwrseq_list_node->list);
+ kfree(pwrseq_list_node);
+ }
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_off_list);
diff --git a/drivers/power/pwrseq/pwrseq_generic.c b/drivers/power/pwrseq/pwrseq_generic.c
new file mode 100644
index 0000000..d7a77f2
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_generic.c
@@ -0,0 +1,183 @@
+/*
+ * pwrseq_generic.c Generic power sequence handling
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Author: Peter Chen <peter.chen@nxp.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+#include <linux/power/pwrseq.h>
+
+struct pwrseq_generic {
+ struct pwrseq pwrseq;
+ struct gpio_desc *gpiod_reset;
+ struct clk *clks[PWRSEQ_MAX_CLKS];
+ u32 duration_us;
+};
+
+#define to_generic_pwrseq(p) container_of(p, struct pwrseq_generic, pwrseq)
+
+static int pwrseq_generic_alloc_instance(void);
+static const struct of_device_id generic_id_table[] = {
+ { .compatible = "generic",},
+ { /* sentinel */ }
+};
+
+static void pwrseq_generic_put(struct pwrseq *pwrseq)
+{
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+ int clk;
+
+ if (pwrseq_gen->gpiod_reset)
+ gpiod_put(pwrseq_gen->gpiod_reset);
+
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++)
+ clk_put(pwrseq_gen->clks[clk]);
+
+ pwrseq_unregister(&pwrseq_gen->pwrseq);
+ kfree(pwrseq_gen);
+}
+
+static void pwrseq_generic_off(struct pwrseq *pwrseq)
+{
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+ int clk;
+
+ for (clk = PWRSEQ_MAX_CLKS - 1; clk >= 0; clk--)
+ clk_disable_unprepare(pwrseq_gen->clks[clk]);
+}
+
+static int pwrseq_generic_on(struct pwrseq *pwrseq)
+{
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+ int clk, ret = 0;
+ struct gpio_desc *gpiod_reset = pwrseq_gen->gpiod_reset;
+
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS && pwrseq_gen->clks[clk]; clk++) {
+ ret = clk_prepare_enable(pwrseq_gen->clks[clk]);
+ if (ret) {
+ pr_err("Can't enable clock, ret=%d\n", ret);
+ goto err_disable_clks;
+ }
+ }
+
+ if (gpiod_reset) {
+ u32 duration_us = pwrseq_gen->duration_us;
+
+ if (duration_us <= 10)
+ udelay(10);
+ else
+ usleep_range(duration_us, duration_us + 100);
+ gpiod_set_value(gpiod_reset, 0);
+ }
+
+ return ret;
+
+err_disable_clks:
+ while (--clk >= 0)
+ clk_disable_unprepare(pwrseq_gen->clks[clk]);
+
+ return ret;
+}
+
+static int pwrseq_generic_get(struct device_node *np, struct pwrseq *pwrseq)
+{
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+ enum of_gpio_flags flags;
+ int reset_gpio, clk, ret = 0;
+
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++) {
+ pwrseq_gen->clks[clk] = of_clk_get(np, clk);
+ if (IS_ERR(pwrseq_gen->clks[clk])) {
+ ret = PTR_ERR(pwrseq_gen->clks[clk]);
+ if (ret != -ENOENT)
+ goto err_put_clks;
+ pwrseq_gen->clks[clk] = NULL;
+ break;
+ }
+ }
+
+ reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags);
+ if (gpio_is_valid(reset_gpio)) {
+ unsigned long gpio_flags;
+
+ if (flags & OF_GPIO_ACTIVE_LOW)
+ gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW;
+ else
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+
+ ret = gpio_request_one(reset_gpio, gpio_flags,
+ "pwrseq-reset-gpios");
+ if (ret)
+ goto err_put_clks;
+
+ pwrseq_gen->gpiod_reset = gpio_to_desc(reset_gpio);
+ of_property_read_u32(np, "reset-duration-us",
+ &pwrseq_gen->duration_us);
+ } else if (reset_gpio == -ENOENT) {
+ ; /* no such gpio */
+ } else {
+ ret = reset_gpio;
+ pr_err("Failed to get reset gpio on %s, err = %d\n",
+ np->full_name, reset_gpio);
+ goto err_put_clks;
+ }
+
+ /* allocate new one for later pwrseq instance request */
+ ret = pwrseq_generic_alloc_instance();
+ if (ret)
+ goto err_put_gpio;
+
+ return 0;
+
+err_put_gpio:
+ if (pwrseq_gen->gpiod_reset)
+ gpiod_put(pwrseq_gen->gpiod_reset);
+err_put_clks:
+ while (--clk >= 0)
+ clk_put(pwrseq_gen->clks[clk]);
+ return ret;
+}
+
+static int pwrseq_generic_alloc_instance(void)
+{
+ struct pwrseq_generic *pwrseq_gen;
+
+ pwrseq_gen = kzalloc(sizeof(*pwrseq_gen), GFP_KERNEL);
+ if (!pwrseq_gen)
+ return -ENOMEM;
+
+ pwrseq_gen->pwrseq.pwrseq_of_match_table = generic_id_table;
+ pwrseq_gen->pwrseq.get = pwrseq_generic_get;
+ pwrseq_gen->pwrseq.on = pwrseq_generic_on;
+ pwrseq_gen->pwrseq.off = pwrseq_generic_off;
+ pwrseq_gen->pwrseq.put = pwrseq_generic_put;
+
+ pwrseq_register(&pwrseq_gen->pwrseq);
+ return 0;
+}
+
+static int __init pwrseq_generic_register(void)
+{
+ return pwrseq_generic_alloc_instance();
+}
+postcore_initcall(pwrseq_generic_register)
diff --git a/include/linux/power/pwrseq.h b/include/linux/power/pwrseq.h
new file mode 100644
index 0000000..4f37275
--- /dev/null
+++ b/include/linux/power/pwrseq.h
@@ -0,0 +1,72 @@
+#ifndef __LINUX_PWRSEQ_H
+#define __LINUX_PWRSEQ_H
+
+#include <linux/of.h>
+
+#define PWRSEQ_MAX_CLKS 3
+
+struct pwrseq {
+ const struct of_device_id *pwrseq_of_match_table;
+ struct list_head node;
+ int (*get)(struct device_node *np, struct pwrseq *p);
+ int (*on)(struct pwrseq *p);
+ void (*off)(struct pwrseq *p);
+ void (*put)(struct pwrseq *p);
+ int (*suspend)(struct pwrseq *p);
+ int (*resume)(struct pwrseq *p);
+ bool used;
+};
+
+/* used for power sequence instance list in one driver */
+struct pwrseq_list_per_dev {
+ struct pwrseq *pwrseq;
+ struct list_head list;
+};
+
+#if IS_ENABLED(CONFIG_POWER_SEQUENCE)
+int pwrseq_get(struct device_node *np, struct pwrseq *p);
+int pwrseq_on(struct pwrseq *p);
+void pwrseq_off(struct pwrseq *p);
+void pwrseq_put(struct pwrseq *p);
+int pwrseq_suspend(struct pwrseq *p);
+int pwrseq_resume(struct pwrseq *p);
+void pwrseq_register(struct pwrseq *pwrseq);
+void pwrseq_unregister(struct pwrseq *pwrseq);
+struct pwrseq *of_pwrseq_on(struct device_node *np);
+void of_pwrseq_off(struct pwrseq *pwrseq);
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head);
+void of_pwrseq_off_list(struct list_head *head);
+#else
+static inline int pwrseq_get(struct device_node *np, struct pwrseq *p)
+{
+ return 0;
+}
+static inline int pwrseq_on(struct pwrseq *p)
+{
+ return 0;
+}
+static inline void pwrseq_off(struct pwrseq *p) {}
+static inline void pwrseq_put(struct pwrseq *p) {}
+static inline int pwrseq_suspend(struct pwrseq *p)
+{
+ return 0;
+}
+static inline int pwrseq_resume(struct pwrseq *p)
+{
+ return 0;
+}
+static inline void pwrseq_register(struct pwrseq *pwrseq) {}
+static inline void pwrseq_unregister(struct pwrseq *pwrseq) {}
+static inline struct pwrseq *of_pwrseq_on(struct device_node *np)
+{
+ return NULL;
+}
+void of_pwrseq_off(struct pwrseq *pwrseq) {}
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
+{
+ return 0;
+}
+void of_pwrseq_off_list(struct list_head *head) {}
+#endif /* CONFIG_POWER_SEQUENCE */
+
+#endif /* __LINUX_PWRSEQ_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v9 3/8] binding-doc: usb: usb-device: add optional properties for power sequence
From: Peter Chen @ 2016-11-08 2:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478573472-29516-1-git-send-email-peter.chen@nxp.com>
Add optional properties for power sequence.
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/usb/usb-device.txt | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/usb/usb-device.txt b/Documentation/devicetree/bindings/usb/usb-device.txt
index 1c35e7b..3661dd2 100644
--- a/Documentation/devicetree/bindings/usb/usb-device.txt
+++ b/Documentation/devicetree/bindings/usb/usb-device.txt
@@ -13,6 +13,10 @@ Required properties:
- reg: the port number which this device is connecting to, the range
is 1-31.
+Optional properties:
+power sequence properties, see
+Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt for detail
+
Example:
&usb1 {
@@ -21,8 +25,12 @@ Example:
#address-cells = <1>;
#size-cells = <0>;
- hub: genesys at 1 {
+ genesys: hub at 1 {
compatible = "usb5e3,608";
reg = <1>;
+
+ clocks = <&clks IMX6SX_CLK_CKO>;
+ reset-gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; /* hub reset pin */
+ reset-duration-us = <10>;
};
}
--
2.7.4
^ permalink raw reply related
* [PATCH v9 4/8] usb: core: add power sequence handling for USB devices
From: Peter Chen @ 2016-11-08 2:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478573472-29516-1-git-send-email-peter.chen@nxp.com>
Some hard-wired USB devices need to do power sequence to let the
device work normally, the typical power sequence like: enable USB
PHY clock, toggle reset pin, etc. But current Linux USB driver
lacks of such code to do it, it may cause some hard-wired USB devices
works abnormal or can't be recognized by controller at all.
In this patch, it calls power sequence library APIs to finish
the power sequence events. It will do power on sequence at hub's
probe for all devices under this hub (includes root hub).
At hub_disconnect, it will do power off sequence which is at powered
on list.
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Reviewed-by: Vaibhav Hiremath <hvaibhav.linux@gmail.com>
---
drivers/usb/core/hub.c | 41 ++++++++++++++++++++++++++++++++++++++---
drivers/usb/core/hub.h | 1 +
2 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index cbb1467..acbbf0a 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -26,6 +26,7 @@
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
+#include <linux/power/pwrseq.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -1695,6 +1696,7 @@ static void hub_disconnect(struct usb_interface *intf)
hub->error = 0;
hub_quiesce(hub, HUB_DISCONNECT);
+ of_pwrseq_off_list(&hub->pwrseq_on_list);
mutex_lock(&usb_port_peer_mutex);
/* Avoid races with recursively_mark_NOTATTACHED() */
@@ -1722,12 +1724,41 @@ static void hub_disconnect(struct usb_interface *intf)
kref_put(&hub->kref, hub_release);
}
+#ifdef CONFIG_OF
+static int hub_of_pwrseq_on(struct usb_hub *hub)
+{
+ struct device *parent;
+ struct usb_device *hdev = hub->hdev;
+ struct device_node *np;
+ int ret;
+
+ if (hdev->parent)
+ parent = &hdev->dev;
+ else
+ parent = bus_to_hcd(hdev->bus)->self.controller;
+
+ for_each_child_of_node(parent->of_node, np) {
+ ret = of_pwrseq_on_list(np, &hub->pwrseq_on_list);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#else
+static int hub_of_pwrseq_on(struct usb_hub *hub)
+{
+ return 0;
+}
+#endif
+
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_host_interface *desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *hdev;
struct usb_hub *hub;
+ int ret = -ENODEV;
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
@@ -1832,6 +1863,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
INIT_WORK(&hub->events, hub_event);
+ INIT_LIST_HEAD(&hub->pwrseq_on_list);
usb_get_intf(intf);
usb_get_dev(hdev);
@@ -1845,11 +1877,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)
hub->quirk_check_port_auto_suspend = 1;
- if (hub_configure(hub, endpoint) >= 0)
- return 0;
+ if (hub_configure(hub, endpoint) >= 0) {
+ ret = hub_of_pwrseq_on(hub);
+ if (!ret)
+ return 0;
+ }
hub_disconnect(intf);
- return -ENODEV;
+ return ret;
}
static int
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 34c1a7e..cd86f91 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -78,6 +78,7 @@ struct usb_hub {
struct delayed_work init_work;
struct work_struct events;
struct usb_port **ports;
+ struct list_head pwrseq_on_list; /* powered pwrseq node list */
};
/**
--
2.7.4
^ permalink raw reply related
* [PATCH v9 5/8] usb: chipidea: let chipidea core device of_node equal's glue layer device of_node
From: Peter Chen @ 2016-11-08 2:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478573472-29516-1-git-send-email-peter.chen@nxp.com>
From: Peter Chen <peter.chen@freescale.com>
At device tree, we have no device node for chipidea core,
the glue layer's node is the parent node for host and udc
device. But in related driver, the parent device is chipidea
core. So, in order to let the common driver get parent's node,
we let the core's device node equals glue layer device node.
Signed-off-by: Peter Chen <peter.chen@freescale.com>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
---
drivers/usb/chipidea/core.c | 27 ++++++++++++++++++++++-----
1 file changed, 22 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 69426e6..6839e19 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -927,6 +927,16 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
+ /*
+ * At device tree, we have no device node for chipidea core,
+ * the glue layer's node is the parent node for host and udc
+ * device. But in related driver, the parent device is chipidea
+ * core. So, in order to let the common driver get parent's node,
+ * we let the core's device node equals glue layer's node.
+ */
+ if (dev->parent && dev->parent->of_node)
+ dev->of_node = dev->parent->of_node;
+
if (ci->platdata->phy) {
ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) {
@@ -937,11 +947,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
/* if both generic PHY and USB PHY layers aren't enabled */
if (PTR_ERR(ci->phy) == -ENOSYS &&
- PTR_ERR(ci->usb_phy) == -ENXIO)
- return -ENXIO;
+ PTR_ERR(ci->usb_phy) == -ENXIO) {
+ ret = -ENXIO;
+ goto clear_of_node;
+ }
- if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
- return -EPROBE_DEFER;
+ if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
+ ret = -EPROBE_DEFER;
+ goto clear_of_node;
+ }
if (IS_ERR(ci->phy))
ci->phy = NULL;
@@ -952,7 +966,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ret = ci_usb_phy_init(ci);
if (ret) {
dev_err(dev, "unable to init phy: %d\n", ret);
- return ret;
+ goto clear_of_node;
}
ci->hw_bank.phys = res->start;
@@ -1058,6 +1072,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci_role_destroy(ci);
deinit_phy:
ci_usb_phy_exit(ci);
+clear_of_node:
+ dev->of_node = NULL;
return ret;
}
@@ -1076,6 +1092,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
ci_extcon_unregister(ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
+ ci->dev->of_node = NULL;
ci_usb_phy_exit(ci);
return 0;
--
2.7.4
^ permalink raw reply related
* [PATCH v9 6/8] ARM: dts: imx6qdl: Enable usb node children with <reg>
From: Peter Chen @ 2016-11-08 2:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478573472-29516-1-git-send-email-peter.chen@nxp.com>
From: Joshua Clayton <stillcompiling@gmail.com>
Give usb nodes #address and #size attributes, so that a child node
representing a permanently connected device such as an onboard hub may
be addressed with a <reg> attribute
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
---
arch/arm/boot/dts/imx6qdl.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index b13b0b2..8fc66e1 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -935,6 +935,8 @@
usbh1: usb at 02184200 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <0x02184200 0x200>;
interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_USBOH3>;
@@ -949,6 +951,8 @@
usbh2: usb at 02184400 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <0x02184400 0x200>;
interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_USBOH3>;
@@ -962,6 +966,8 @@
usbh3: usb at 02184600 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <0x02184600 0x200>;
interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_USBOH3>;
--
2.7.4
^ permalink raw reply related
* [PATCH v9 7/8] ARM: dts: imx6qdl-udoo.dtsi: fix onboard USB HUB property
From: Peter Chen @ 2016-11-08 2:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478573472-29516-1-git-send-email-peter.chen@nxp.com>
The current dts describes USB HUB's property at USB controller's
entry, it is improper. The USB HUB should be the child node
under USB controller, and power sequence properties are under
it. Besides, using gpio pinctrl setting for USB2415's reset pin.
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
---
arch/arm/boot/dts/imx6qdl-udoo.dtsi | 26 ++++++++++++--------------
1 file changed, 12 insertions(+), 14 deletions(-)
diff --git a/arch/arm/boot/dts/imx6qdl-udoo.dtsi b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
index c96c91d..a173de2 100644
--- a/arch/arm/boot/dts/imx6qdl-udoo.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
@@ -9,6 +9,8 @@
*
*/
+#include <dt-bindings/gpio/gpio.h>
+
/ {
aliases {
backlight = &backlight;
@@ -58,17 +60,6 @@
#address-cells = <1>;
#size-cells = <0>;
- reg_usb_h1_vbus: regulator at 0 {
- compatible = "regulator-fixed";
- reg = <0>;
- regulator-name = "usb_h1_vbus";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- enable-active-high;
- startup-delay-us = <2>; /* USB2415 requires a POR of 1 us minimum */
- gpio = <&gpio7 12 0>;
- };
-
reg_panel: regulator at 1 {
compatible = "regulator-fixed";
reg = <1>;
@@ -188,7 +179,7 @@
pinctrl_usbh: usbhgrp {
fsl,pins = <
- MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000
+ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0
MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x130b0
>;
};
@@ -259,9 +250,16 @@
&usbh1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usbh>;
- vbus-supply = <®_usb_h1_vbus>;
- clocks = <&clks IMX6QDL_CLK_CKO>;
status = "okay";
+
+ usb2415: hub at 1 {
+ compatible = "usb424,2514";
+ reg = <1>;
+
+ clocks = <&clks IMX6QDL_CLK_CKO>;
+ reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+ reset-duration-us = <3000>;
+ };
};
&usdhc3 {
--
2.7.4
^ permalink raw reply related
* [PATCH v9 8/8] ARM: dts: imx6q-evi: Fix onboard hub reset line
From: Peter Chen @ 2016-11-08 2:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478573472-29516-1-git-send-email-peter.chen@nxp.com>
From: Joshua Clayton <stillcompiling@gmail.com>
Previously the onboard hub was made to work by treating its
reset gpio as a regulator enable.
Get rid of that kludge now that pwseq has added reset gpio support
Move pin muxing the hub reset pin into the usbh1 group
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
---
arch/arm/boot/dts/imx6q-evi.dts | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/arch/arm/boot/dts/imx6q-evi.dts b/arch/arm/boot/dts/imx6q-evi.dts
index 6de21ff..3277a06 100644
--- a/arch/arm/boot/dts/imx6q-evi.dts
+++ b/arch/arm/boot/dts/imx6q-evi.dts
@@ -54,18 +54,6 @@
reg = <0x10000000 0x40000000>;
};
- reg_usbh1_vbus: regulator-usbhubreset {
- compatible = "regulator-fixed";
- regulator-name = "usbh1_vbus";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- enable-active-high;
- startup-delay-us = <2>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usbh1_hubreset>;
- gpio = <&gpio7 12 GPIO_ACTIVE_HIGH>;
- };
-
reg_usb_otg_vbus: regulator-usbotgvbus {
compatible = "regulator-fixed";
regulator-name = "usb_otg_vbus";
@@ -207,12 +195,18 @@
};
&usbh1 {
- vbus-supply = <®_usbh1_vbus>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usbh1>;
dr_mode = "host";
disable-over-current;
status = "okay";
+
+ usb2415host: hub at 1 {
+ compatible = "usb424,2513";
+ reg = <1>;
+ reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+ reset-duration-us = <3000>;
+ };
};
&usbotg {
@@ -471,11 +465,6 @@
MX6QDL_PAD_GPIO_3__USB_H1_OC 0x1b0b0
/* usbh1_b OC */
MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x1b0b0
- >;
- };
-
- pinctrl_usbh1_hubreset: usbh1hubresetgrp {
- fsl,pins = <
MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0
>;
};
--
2.7.4
^ permalink raw reply related
* [PATCH v2 2/3] irqchip: mtk-cirq: Add mediatek mtk-cirq implement
From: Youlin Pei @ 2016-11-08 2:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <867f8ilwcl.fsf@arm.com>
On Fri, 2016-11-04 at 22:21 +0000, Marc Zyngier wrote:
> On Fri, Nov 04 2016 at 04:42:57 AM, Youlin Pei <youlin.pei@mediatek.com> wrote:
> > On Tue, 2016-11-01 at 20:49 +0000, Marc Zyngier wrote:
> >> On Tue, Nov 01 2016 at 11:52:01 AM, Youlin Pei <youlin.pei@mediatek.com> wrote:
> >> > In Mediatek SOCs, the CIRQ is a low power interrupt controller
> >> > designed to works outside MCUSYS which comprises with Cortex-Ax
> >> > cores,CCI and GIC.
> >> >
> >> > The CIRQ controller is integrated in between MCUSYS( include
> >> > Cortex-Ax, CCI and GIC ) and interrupt sources as the second
> >> > level interrupt controller. The external interrupts which outside
> >> > MCUSYS will feed through CIRQ then bypass to GIC. CIRQ can monitors
> >> > all edge trigger interupts. When an edge interrupt is triggered,
> >> > CIRQ can record the status and generate a pulse signal to GIC when
> >> > flush command executed.
> >> >
> >> > When system enters sleep mode, MCUSYS will be turned off to improve
> >> > power consumption, also GIC is power down. The edge trigger interrupts
> >> > will be lost in this scenario without CIRQ.
> >> >
> >> > This commit provides the CIRQ irqchip implement.
> >> >
> >> > Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
> >> > ---
> >> > drivers/irqchip/Makefile | 2 +-
> >> > drivers/irqchip/irq-mtk-cirq.c | 262 ++++++++++++++++++++++++++++++++++++++++
> >> > 2 files changed, 263 insertions(+), 1 deletion(-)
> >> > create mode 100644 drivers/irqchip/irq-mtk-cirq.c
> >> >
> >> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> >> > index e4dbfc8..8f33580 100644
> >> > --- a/drivers/irqchip/Makefile
> >> > +++ b/drivers/irqchip/Makefile
> >> > @@ -60,7 +60,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
> >> > obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
> >> > obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
> >> > obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o
> >> > -obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o
> >> > +obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o irq-mtk-cirq.o
> >> > obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o
> >> > obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o
> >> > obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
> >> > diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c
> >> > new file mode 100644
> >> > index 0000000..fc43ef3
> >> > --- /dev/null
> >> > +++ b/drivers/irqchip/irq-mtk-cirq.c
> >> > @@ -0,0 +1,262 @@
> >> > +/*
> >> > + * Copyright (c) 2016 MediaTek Inc.
> >> > + * Author: Youlin.Pei <youlin.pei@mediatek.com>
> >> > + *
> >> > + * This program is free software; you can redistribute it and/or modify
> >> > + * it under the terms of the GNU General Public License version 2 as
> >> > + * published by the Free Software Foundation.
> >> > + *
> >> > + * This program is distributed in the hope that it will be useful,
> >> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> >> > + * GNU General Public License for more details.
> >> > + */
> >> > +
> >> > +#include <linux/irq.h>
> >> > +#include <linux/irqchip.h>
> >> > +#include <linux/irqdomain.h>
> >> > +#include <linux/of.h>
> >> > +#include <linux/of_irq.h>
> >> > +#include <linux/of_address.h>
> >> > +#include <linux/io.h>
> >> > +#include <linux/slab.h>
> >> > +#include <linux/syscore_ops.h>
> >> > +
> >> > +#define CIRQ_ACK 0x40
> >> > +#define CIRQ_MASK_SET 0xc0
> >> > +#define CIRQ_MASK_CLR 0x100
> >> > +#define CIRQ_SENS_SET 0x180
> >> > +#define CIRQ_SENS_CLR 0x1c0
> >> > +#define CIRQ_POL_SET 0x240
> >> > +#define CIRQ_POL_CLR 0x280
> >> > +#define CIRQ_CONTROL 0x300
> >> > +
> >> > +#define CIRQ_EN 0x1
> >> > +#define CIRQ_EDGE 0x2
> >> > +#define CIRQ_FLUSH 0x4
> >> > +
> >> > +#define CIRQ_IRQ_NUM 0x200
> >> > +
> >> > +struct mtk_cirq_chip_data {
> >> > + void __iomem *base;
> >> > + unsigned int ext_irq_start;
> >> > +};
> >> > +
> >> > +static struct mtk_cirq_chip_data *cirq_data;
> >>
> >> Are you guaranteed that you'll only ever have a single CIRQ in any
> >> system?
> >
> > In Mediatek's SOC, only hace a single CIRQ.
> >
> >>
> >> > +
> >> > +static void mtk_cirq_write_mask(struct irq_data *data, unsigned int offset)
> >> > +{
> >> > + struct mtk_cirq_chip_data *chip_data = data->chip_data;
> >> > + unsigned int cirq_num = data->hwirq;
> >> > + u32 mask = 1 << (cirq_num % 32);
> >> > +
> >> > + writel(mask, chip_data->base + offset + (cirq_num / 32) * 4);
> >>
> >> Why can't you use the relaxed accessors?
> >
> > It seems that i use wrong function, i will change the writel to
> > writel_relaxed in next ve
> >
> >>
> >> > +}
> >> > +
> >> > +static void mtk_cirq_mask(struct irq_data *data)
> >> > +{
> >> > + mtk_cirq_write_mask(data, CIRQ_MASK_SET);
> >> > + irq_chip_mask_parent(data);
> >> > +}
> >> > +
> >> > +static void mtk_cirq_unmask(struct irq_data *data)
> >> > +{
> >> > + mtk_cirq_write_mask(data, CIRQ_MASK_CLR);
> >> > + irq_chip_unmask_parent(data);
> >> > +}
> >> > +
> >> > +static void mtk_cirq_eoi(struct irq_data *data)
> >> > +{
> >> > + mtk_cirq_write_mask(data, CIRQ_ACK);
> >>
> >> EOI and ACK have very different semantics. What is this write actually
> >> doing? Also, you're now doing an additional MMIO write on each interrupt
> >> EOI, doubling its cost. Do you really need to do actually signal the HW
> >> that we've EOIed an interrupt? I would have hoped that you'd be able to
> >> put it in "bypass" mode as long as you're not suspending...
> >>
> >
> > When external interrupt happened, CIRQ status register record the status
> > even CIRQ is not enabled. when execute the flush command, CIRQ will
> > resend the signals according to the status. So if don't clear the
> > status, CIRQ will resend the wrong signals. the ACK write operation will
> > clear the status.
>
> But at this time, we haven't suspended yet, and there is nothing to
> replay. Also, you only enable the edge capture when suspending. So what
> are you ACKing here? Can't you simply clear everything right when
> suspending and not do it at all on the fast path?
I had planned to ACK the status in cirq suspend callback, but
encountered a problem.
following is a piece of code from
http://lxr.free-electrons.com/source/kernel/power/suspend.c#L361
arch_suspend_disable_irqs(); ---step1
BUG_ON(!irqs_disabled());
error = syscore_suspend();
|
---cirq suspend(); ---step2
if ack the status in cirq suspend, the interrupts will be lost which
happened during step1 to step2.
So would you mind give me some suggestions about this?
Thanks a lot!
>
> Thanks,
>
> M.
^ permalink raw reply
* [PATCH 3/4] ARM: EXYNOS: Remove static mapping of SCU SFR
From: pankaj.dubey @ 2016-11-08 3:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161107175349.GD4865@kozik-lap>
Hi Krzysztof,
On Monday 07 November 2016 11:23 PM, Krzysztof Kozlowski wrote:
> On Fri, Nov 04, 2016 at 09:09:23AM +0530, Pankaj Dubey wrote:
>> Lets remove static mapping of SCU SFR mainly used in CORTEX-A9 SoC based boards.
>> Instead use mapping from device tree node of SCU.
>>
>> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
>> ---
>> arch/arm/mach-exynos/exynos.c | 22 ----------------------
>> arch/arm/mach-exynos/include/mach/map.h | 2 --
>> arch/arm/mach-exynos/platsmp.c | 18 +++++++++++-------
>> arch/arm/mach-exynos/pm.c | 14 +++++++++++---
>> arch/arm/mach-exynos/suspend.c | 15 +++++++++++----
>> arch/arm/plat-samsung/include/plat/map-s5p.h | 4 ----
>> 6 files changed, 33 insertions(+), 42 deletions(-)
>>
>> diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
>> index 757fc11..fa08ef9 100644
>> --- a/arch/arm/mach-exynos/exynos.c
>> +++ b/arch/arm/mach-exynos/exynos.c
>> @@ -28,15 +28,6 @@
>>
>> #include "common.h"
>>
>> -static struct map_desc exynos4_iodesc[] __initdata = {
>> - {
>> - .virtual = (unsigned long)S5P_VA_COREPERI_BASE,
>> - .pfn = __phys_to_pfn(EXYNOS4_PA_COREPERI),
>> - .length = SZ_8K,
>> - .type = MT_DEVICE,
>> - },
>> -};
>> -
>> static struct platform_device exynos_cpuidle = {
>> .name = "exynos_cpuidle",
>> #ifdef CONFIG_ARM_EXYNOS_CPUIDLE
>> @@ -99,17 +90,6 @@ static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
>> return 1;
>> }
>>
>> -/*
>> - * exynos_map_io
>> - *
>> - * register the standard cpu IO areas
>> - */
>> -static void __init exynos_map_io(void)
>> -{
>> - if (soc_is_exynos4())
>> - iotable_init(exynos4_iodesc, ARRAY_SIZE(exynos4_iodesc));
>> -}
>> -
>> static void __init exynos_init_io(void)
>> {
>> debug_ll_io_init();
>> @@ -118,8 +98,6 @@ static void __init exynos_init_io(void)
>>
>> /* detect cpu id and rev. */
>> s5p_init_cpu(S5P_VA_CHIPID);
>> -
>> - exynos_map_io();
>> }
>>
>> /*
>> diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
>> index 5fb0040..0eef407 100644
>> --- a/arch/arm/mach-exynos/include/mach/map.h
>> +++ b/arch/arm/mach-exynos/include/mach/map.h
>> @@ -18,6 +18,4 @@
>>
>> #define EXYNOS_PA_CHIPID 0x10000000
>>
>> -#define EXYNOS4_PA_COREPERI 0x10500000
>> -
>> #endif /* __ASM_ARCH_MAP_H */
>> diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
>> index a5d6841..553d0d9 100644
>> --- a/arch/arm/mach-exynos/platsmp.c
>> +++ b/arch/arm/mach-exynos/platsmp.c
>> @@ -224,11 +224,6 @@ static void write_pen_release(int val)
>> sync_cache_w(&pen_release);
>> }
>>
>> -static void __iomem *scu_base_addr(void)
>> -{
>> - return (void __iomem *)(S5P_VA_SCU);
>> -}
>> -
>> static DEFINE_SPINLOCK(boot_lock);
>>
>> static void exynos_secondary_init(unsigned int cpu)
>> @@ -387,14 +382,23 @@ fail:
>>
>> static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
>> {
>> + struct device_node *np;
>> + void __iomem *scu_base;
>> int i;
>>
>> exynos_sysram_init();
>>
>> exynos_set_delayed_reset_assertion(true);
>>
>> - if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
>> - scu_enable(scu_base_addr());
>> + if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
>> + np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
>> + scu_base = of_iomap(np, 0);
>> + if (scu_base) {
>> + scu_enable(scu_base);
>> + iounmap(scu_base);
>> + }
>> + of_node_put(np);
>
> I do not like the duplication - this code appears in three places and
> they are the same. Could you split it into separate function?
>
OK, will do these changes in v2.
Only pm.c and suspend.c file's scu_enable can be moved to single place
without introducing more dependencies between these mach files.
> As you mentioned to Alim, in case of lack of node, it would be nice to notify the
> user (pr_err() etc).
>
OK, will do these changes in v2.
Thanks,
Pankaj Dubey
> Best regards,
> Krzysztof
>
>
>
>
^ 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