* PROBLEM: DWC3 USB 3.0 not working on Odroid-XU4 with Exynos 5422
From: Michael Niewöhner @ 2016-10-18 12:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <a4d744ae-d043-b652-3073-f29ebe76f865@codeaurora.org>
Hi,
On Mon, 2016-10-17 at 15:21 +0530, Vivek Gautam wrote:
>
> On 10/17/2016 01:38 PM, Felipe Balbi wrote:
> > Hi,
> >
> > Michael Niew?hner <linux@mniewoehner.de> writes:
> > > Hi Felipe,
> > > On Fri, 2016-10-07 at 22:26 +0200, Michael Niew?hner wrote:
> > > > Hi Felipe,
> > > >
> > > > On Fr, 2016-10-07 at 10:42 +0300, Felipe Balbi wrote:
> > > > > Hi,
> > > > >
> > > > > Michael Niew?hner <linux@mniewoehner.de> writes:
> > > > > > > The clocks are same across working/non-working.
> > > > > > > Is it possible to bisect the commit that's causing hang for 4.8x ?
> > > > > >
> > > > > > [c499ff71ff2a281366c6ec7a904c547d806cbcd1] usb: dwc3: core: re-factor init and exit paths
> > > > > > This patch causes both the hang on reboot and the lsusb hang.
> > > > >
> > > > > How to reproduce? Why don't we see this on x86 and TI boards? I'm
> > > > > guessing this is failed bisection, as I can't see anything in that
> > > > > commit that would cause reboot hang. Also, that code path is *NOT*
> > > > > executed when you run lsusb.
> > > > >
> > > >
> > > > I've tested this procedure multiple times to be sure:
> > > >
> > > > - checkout c499ff71, compile, boot the odroid
> > > > - run lsusb -v => lsusb hangs, can't terminate with ctrl-c
> > > > - hard reset, after boot run poweroff or reboot => board does not completely power off / reboot (see log below)
> > > > - revert c499ff71, mrproper, compile, boot the odroid
> > > > - run lsusb -v => shows full output, not hanging
> > > > - run reboot or poweroff => board powers off / reboots just fine
> > > >
> > > >
> > > > dmesg poweroff not working:
> > > > ...
> > > > [??120.733519] systemd-journald[144]: systemd-journald stopped as pid 144
> > > > [??120.742663] systemd-shutdown[1]: Sending SIGKILL to remaining processes...
> > > > [??120.769212] systemd-shutdown[1]: Unmounting file systems.
> > > > [??120.773713] systemd-shutdown[1]: Unmounting /sys/kernel/debug.
> > > > [??120.827211] systemd-shutdown[1]: Unmounting /dev/mqueue.
> > > > [??121.081672] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
> > > > [??121.091687] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
> > > > [??121.095608] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
> > > > [??121.101014] systemd-shutdown[1]: All filesystems unmounted.
> > > > [??121.106523] systemd-shutdown[1]: Deactivating swaps.
> > > > [??121.111585] systemd-shutdown[1]: All swaps deactivated.
> > > > [??121.116661] systemd-shutdown[1]: Detaching loop devices.
> > > > [??121.126395] systemd-shutdown[1]: All loop devices detached.
> > > > [??121.130525] systemd-shutdown[1]: Detaching DM devices.
> > > > [??121.135824] systemd-shutdown[1]: All DM devices detached.
> > > > [??121.166327] systemd-shutdown[1]: /lib/systemd/system-shutdown succeeded.
> > > > [??121.171739] systemd-shutdown[1]: Powering off.
> > > >
> > > > => at this point removing the sd card would show a message
> > > > "removed mmc0" (not sure what the real message was...) so the board is not completely off.
> > > >
> > > >
> > > > dmesg poweroff working:
> > > > ...
> > > > [??120.733519] systemd-journald[144]: systemd-journald stopped as pid 144
> > > > [??120.742663] systemd-shutdown[1]: Sending SIGKILL to remaining processes...
> > > > [??120.769212] systemd-shutdown[1]: Unmounting file systems.
> > > > [??120.773713] systemd-shutdown[1]: Unmounting /sys/kernel/debug.
> > > > [??120.827211] systemd-shutdown[1]: Unmounting /dev/mqueue.
> > > > [??121.081672] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
> > > > [??121.091687] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
> > > > [??121.095608] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
> > > > [??121.101014] systemd-shutdown[1]: All filesystems unmounted.
> > > > [??121.106523] systemd-shutdown[1]: Deactivating swaps.
> > > > [??121.111585] systemd-shutdown[1]: All swaps deactivated.
> > > > [??121.116661] systemd-shutdown[1]: Detaching loop devices.
> > > > [??121.126395] systemd-shutdown[1]: All loop devices detached.
> > > > [??121.130525] systemd-shutdown[1]: Detaching DM devices.
> > > > [??121.135824] systemd-shutdown[1]: All DM devices detached.
> > > > [??121.166327] systemd-shutdown[1]: /lib/systemd/system-shutdown succeeded.
> > > > [??121.171739] systemd-shutdown[1]: Powering off.
> > > > [??121.182331] rebo?
> > > >
> > > >
> > > >
> > > > Best regards
> > > > Michael Niew?hner
> > >
> > > I did some more tests with next-20161016. Reverting / commenting out
> > > one part of your patch "solves" the lsusb hang, the reboot problem
> > > and also the "debounce failed" message. [1]
> > > Another "solution" is to call phy_power_off before phy_power_on. [2]
> > >
> > > Disclaimer: I have no idea what I was doing ;-) These were just some
> > > simple trial-and-error attempts that maybe help to find the real
> > > cause of the problems.
> > >
> > > [1]
> > > diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> > > index 7287a76..5ef589d 100644
> > > --- a/drivers/usb/dwc3/core.c
> > > +++ b/drivers/usb/dwc3/core.c
> > > @@ -724,6 +724,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
> > > ?? /* Adjust Frame Length */
> > > ?? dwc3_frame_length_adjustment(dwc);
> > > ??
> > > +/*
> > > ?? usb_phy_set_suspend(dwc->usb2_phy, 0);
> > > ?? usb_phy_set_suspend(dwc->usb3_phy, 0);
> > > ?? ret = phy_power_on(dwc->usb2_generic_phy);
> > > @@ -733,6 +734,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
> > > ?? ret = phy_power_on(dwc->usb3_generic_phy);
> > > ?? if (ret < 0)
> > > ?? goto err3;
> > > +*/
> > > ??
> > > ?? ret = dwc3_event_buffers_setup(dwc);
> > > ?? if (ret) {
> > >
> > > [2]
> > > diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> > > index 7287a76..f6c8e13 100644
> > > --- a/drivers/usb/dwc3/core.c
> > > +++ b/drivers/usb/dwc3/core.c
> > > @@ -726,6 +726,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
> > > ??
> > > ?????????usb_phy_set_suspend(dwc->usb2_phy, 0);
> > > ?????????usb_phy_set_suspend(dwc->usb3_phy, 0);
> > > +???????phy_power_off(dwc->usb2_generic_phy);
> > > +???????phy_power_off(dwc->usb3_generic_phy);
> >
> > This looks like a PHY driver bug to me. Which PHY driver are you using?
> >
>
> The exynos5-usbdrd phy driver is used for exynos platforms.
> Looks like something is not right with the phy driver even
> after applying the phy_calibrate patches.
>
> Michael, are you using the last set of patches for phy calibration [1]?
> [1] https://lkml.org/lkml/2015/2/2/257.
yes, I'm using the original LOS level patch.
The phy patch doesn't apply to next, so I use this adapted patch:
https://github.com/c0d3z3r0/linux/commit/8b7a0b2a19e235c308b9082a14ffe73477e2c9ed
>
>
> Thanks
> Vivek
>
Michael
^ permalink raw reply
* [PATCH v4 3/9] drm/hisilicon/hibmc: Add support for frame buffer
From: Daniel Vetter @ 2016-10-18 12:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <58060529.5010708@huawei.com>
On Tue, Oct 18, 2016 at 07:19:05PM +0800, Rongrong Zou wrote:
> Hi Daniel,
>
> Thanks for your review!
>
> ? 2016/10/18 15:49, Daniel Vetter ??:
> > On Tue, Oct 18, 2016 at 12:01:18PM +0800, Rongrong Zou wrote:
> > > Add support for fbdev and framebuffer.
> > >
> > > Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
> > > ---
> > > drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +-
> > > drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 25 +++
> > > drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 25 +++
> > > drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c | 257 ++++++++++++++++++++++
> > > drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c | 67 ++++++
> > > 5 files changed, 375 insertions(+), 1 deletion(-)
> > > create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
> > >
> > > diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
> > > index d5c40b8..810a37e 100644
> > > --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
> > > +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
> > > @@ -1,5 +1,5 @@
> > > ccflags-y := -Iinclude/drm
> > > -hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_power.o hibmc_ttm.o
> > > +hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_fbdev.o hibmc_drm_power.o hibmc_ttm.o
> > >
> > > obj-$(CONFIG_DRM_HISI_HIBMC) +=hibmc-drm.o
> > > #obj-y += hibmc-drm.o
> > > diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
> > > index e118f3b..8ddb763 100644
> > > --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
> > > +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
> > > @@ -66,11 +66,31 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
> > >
> > > static int hibmc_pm_suspend(struct device *dev)
> > > {
> > > + struct pci_dev *pdev = to_pci_dev(dev);
> > > + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> > > + struct hibmc_drm_device *hidev = drm_dev->dev_private;
> > > +
> > > + if (hidev->fbdev.initialized) {
> >
> > What do you need these checks for? This looks a bit fishy tbh ...
>
> It is delivered from the bochs driver, and I will fix if there are
> any problems, Thanks.
Yeah, existing drivers aren't up to latest best practices. Not sure why
that was added to bochs really ...
>
> >
> > > + console_lock();
> > > + drm_fb_helper_set_suspend(&hidev->fbdev.helper, 1);
> > > + console_unlock();
> >
> > drm_fb_helper_set_suspend_unlocked is the new fancy one. Please use that
> > one instead. Also, that has the check you might need already included,
> > which means you can nuke this entire function here and just call it
> > directly.
>
> So it should be wrote as:
> static int hibmc_pm_suspend(struct device *dev)
> {
> ...
> drm_kms_helper_poll_disable(drm_dev);
> drm_fb_helper_set_suspend_unlocked(&hidev->fbdev.helper, 1);
> ...
> }
>
> static int hibmc_pm_resume(struct device *dev)
> {
> ...
> drm_helper_resume_force_mode(drm_dev);
> drm_fb_helper_set_suspend_unlocked(&hidev->fbdev.helper, 0);
> drm_kms_helper_poll_enable(drm_dev);
> ...
> }, is it ?
Yup.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
^ permalink raw reply
* [PATCH] crypto: sun4i-ss: support the Security System PRNG
From: Corentin Labbe @ 2016-10-18 12:34 UTC (permalink / raw)
To: linux-arm-kernel
From: LABBE Corentin <clabbe.montjoie@gmail.com>
The Security System have a PRNG.
This patch add support for it as an hwrng.
Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
drivers/crypto/Kconfig | 8 ++++
drivers/crypto/sunxi-ss/Makefile | 1 +
drivers/crypto/sunxi-ss/sun4i-ss-core.c | 14 +++++++
drivers/crypto/sunxi-ss/sun4i-ss-hwrng.c | 70 ++++++++++++++++++++++++++++++++
drivers/crypto/sunxi-ss/sun4i-ss.h | 8 ++++
5 files changed, 101 insertions(+)
create mode 100644 drivers/crypto/sunxi-ss/sun4i-ss-hwrng.c
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 4d2b81f..38f7aca 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -538,6 +538,14 @@ config CRYPTO_DEV_SUN4I_SS
To compile this driver as a module, choose M here: the module
will be called sun4i-ss.
+config CRYPTO_DEV_SUN4I_SS_PRNG
+ bool "Support for Allwinner Security System PRNG"
+ depends on CRYPTO_DEV_SUN4I_SS
+ select HW_RANDOM
+ help
+ This driver provides kernel-side support for the Pseudo-Random
+ Number Generator found in the Security System.
+
config CRYPTO_DEV_ROCKCHIP
tristate "Rockchip's Cryptographic Engine driver"
depends on OF && ARCH_ROCKCHIP
diff --git a/drivers/crypto/sunxi-ss/Makefile b/drivers/crypto/sunxi-ss/Makefile
index 8f4c7a2..ca049ee 100644
--- a/drivers/crypto/sunxi-ss/Makefile
+++ b/drivers/crypto/sunxi-ss/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_CRYPTO_DEV_SUN4I_SS) += sun4i-ss.o
sun4i-ss-y += sun4i-ss-core.o sun4i-ss-hash.o sun4i-ss-cipher.o
+sun4i-ss-$(CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG) += sun4i-ss-hwrng.o
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-core.c b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
index 3ac6c6c..fa739de 100644
--- a/drivers/crypto/sunxi-ss/sun4i-ss-core.c
+++ b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
@@ -359,6 +359,16 @@ static int sun4i_ss_probe(struct platform_device *pdev)
}
}
platform_set_drvdata(pdev, ss);
+
+#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG
+ /* Voluntarily made the PRNG optional */
+ err = sun4i_ss_hwrng_register(&ss->hwrng);
+ if (!err)
+ dev_info(ss->dev, "sun4i-ss PRNG loaded");
+ else
+ dev_err(ss->dev, "sun4i-ss PRNG failed");
+#endif
+
return 0;
error_alg:
i--;
@@ -386,6 +396,10 @@ static int sun4i_ss_remove(struct platform_device *pdev)
int i;
struct sun4i_ss_ctx *ss = platform_get_drvdata(pdev);
+#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG
+ sun4i_ss_hwrng_remove(&ss->hwrng);
+#endif
+
for (i = 0; i < ARRAY_SIZE(ss_algs); i++) {
switch (ss_algs[i].type) {
case CRYPTO_ALG_TYPE_ABLKCIPHER:
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-hwrng.c b/drivers/crypto/sunxi-ss/sun4i-ss-hwrng.c
new file mode 100644
index 0000000..95fadb7
--- /dev/null
+++ b/drivers/crypto/sunxi-ss/sun4i-ss-hwrng.c
@@ -0,0 +1,70 @@
+#include "sun4i-ss.h"
+
+static int sun4i_ss_hwrng_init(struct hwrng *hwrng)
+{
+ struct sun4i_ss_ctx *ss;
+
+ ss = container_of(hwrng, struct sun4i_ss_ctx, hwrng);
+ get_random_bytes(ss->seed, SS_SEED_LEN);
+
+ return 0;
+}
+
+static int sun4i_ss_hwrng_read(struct hwrng *hwrng, void *buf,
+ size_t max, bool wait)
+{
+ int i;
+ u32 v;
+ u32 *data = buf;
+ const u32 mode = SS_OP_PRNG | SS_PRNG_CONTINUE | SS_ENABLED;
+ size_t len;
+ struct sun4i_ss_ctx *ss;
+
+ ss = container_of(hwrng, struct sun4i_ss_ctx, hwrng);
+ len = min_t(size_t, SS_DATA_LEN, max);
+
+ spin_lock_bh(&ss->slock);
+
+ writel(mode, ss->base + SS_CTL);
+
+ /* write the seed */
+ for (i = 0; i < SS_SEED_LEN / 4; i++)
+ writel(ss->seed[i], ss->base + SS_KEY0 + i * 4);
+ writel(mode | SS_PRNG_START, ss->base + SS_CTL);
+
+ /* Read the random data */
+ readsl(ss->base + SS_TXFIFO, data, len / 4);
+
+ if (len % 4 > 0) {
+ v = readl(ss->base + SS_TXFIFO);
+ memcpy(data + len / 4, &v, len % 4);
+ }
+
+ /* Update the seed */
+ for (i = 0; i < SS_SEED_LEN / 4; i++) {
+ v = readl(ss->base + SS_KEY0 + i * 4);
+ ss->seed[i] = v;
+ }
+
+ writel(0, ss->base + SS_CTL);
+ spin_unlock_bh(&ss->slock);
+ return len;
+}
+
+int sun4i_ss_hwrng_register(struct hwrng *hwrng)
+{
+ hwrng->name = "sun4i Security System PRNG";
+ hwrng->init = sun4i_ss_hwrng_init;
+ hwrng->read = sun4i_ss_hwrng_read;
+ hwrng->quality = 1000;
+
+ /* Cannot use devm_hwrng_register() since we need to hwrng_unregister
+ * before stopping clocks/regulator
+ */
+ return hwrng_register(hwrng);
+}
+
+void sun4i_ss_hwrng_remove(struct hwrng *hwrng)
+{
+ hwrng_unregister(hwrng);
+}
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss.h b/drivers/crypto/sunxi-ss/sun4i-ss.h
index f04c0f8..1297510 100644
--- a/drivers/crypto/sunxi-ss/sun4i-ss.h
+++ b/drivers/crypto/sunxi-ss/sun4i-ss.h
@@ -23,6 +23,7 @@
#include <linux/scatterlist.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <linux/hw_random.h>
#include <crypto/md5.h>
#include <crypto/sha.h>
#include <crypto/hash.h>
@@ -125,6 +126,9 @@
#define SS_RXFIFO_EMP_INT_ENABLE (1 << 2)
#define SS_TXFIFO_AVA_INT_ENABLE (1 << 0)
+#define SS_SEED_LEN (192 / 8)
+#define SS_DATA_LEN (160 / 8)
+
struct sun4i_ss_ctx {
void __iomem *base;
int irq;
@@ -134,6 +138,8 @@ struct sun4i_ss_ctx {
struct device *dev;
struct resource *res;
spinlock_t slock; /* control the use of the device */
+ struct hwrng hwrng;
+ u32 seed[SS_SEED_LEN / 4];
};
struct sun4i_ss_alg_template {
@@ -199,3 +205,5 @@ int sun4i_ss_des_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
unsigned int keylen);
int sun4i_ss_des3_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
unsigned int keylen);
+int sun4i_ss_hwrng_register(struct hwrng *hwrng);
+void sun4i_ss_hwrng_remove(struct hwrng *hwrng);
--
2.7.3
^ permalink raw reply related
* [PATCH 01/10] mm: remove write/force parameters from __get_user_pages_locked()
From: Jan Kara @ 2016-10-18 12:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-2-lstoakes@gmail.com>
On Thu 13-10-16 01:20:11, Lorenzo Stoakes wrote:
> This patch removes the write and force parameters from __get_user_pages_locked()
> to make the use of FOLL_FORCE explicit in callers as use of this flag can result
> in surprising behaviour (and hence bugs) within the mm subsystem.
>
> Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
Looks good. You can add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply
* [PATCH v2 6/9] dt-bindings: pinctrl: Deprecate sunxi pinctrl bindings
From: Rob Herring @ 2016-10-18 12:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <ee6d214a14ed31689c5f70fc6ee5736c3d26737b.1476200742.git-series.maxime.ripard@free-electrons.com>
On Tue, Oct 11, 2016 at 05:46:04PM +0200, Maxime Ripard wrote:
> The generic pin configuration and multiplexing should be preferred now,
> even though we still support the old one.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+), 0 deletions(-)
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [PATCH V3 05/10] acpi: apei: handle SEA notification type for ARMv8
From: Hanjun Guo @ 2016-10-18 12:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1475875882-2604-6-git-send-email-tbaicar@codeaurora.org>
Hi Tyler,
On 2016/10/8 5:31, Tyler Baicar wrote:
> ARM APEI extension proposal added SEA (Synchrounous External
> Abort) notification type for ARMv8.
> Add a new GHES error source handling function for SEA. If an error
> source's notification type is SEA, then this function can be registered
> into the SEA exception handler. That way GHES will parse and report
> SEA exceptions when they occur.
Does this SEA is replayed by the firmware (firmware first handling)
or directly triggered by the hardware when error is happened?
Thanks
Hanjun
^ permalink raw reply
* [PATCH] arm64/kprobes: Tidy up sign-extension usage
From: Robin Murphy @ 2016-10-18 12:46 UTC (permalink / raw)
To: linux-arm-kernel
Kprobes does not need its own homebrewed (and frankly inscrutable) sign
extension macro; just use the standard kernel functions instead. Since
the compiler actually recognises the sign-extension idiom of the latter,
we also get the small bonus of some nicer codegen, as each displacement
calculation helper then compiles to a single optimal SBFX instruction.
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
arch/arm64/kernel/probes/simulate-insn.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/arch/arm64/kernel/probes/simulate-insn.c b/arch/arm64/kernel/probes/simulate-insn.c
index 8977ce9d009d..357d3efe1366 100644
--- a/arch/arm64/kernel/probes/simulate-insn.c
+++ b/arch/arm64/kernel/probes/simulate-insn.c
@@ -13,28 +13,26 @@
* General Public License for more details.
*/
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include "simulate-insn.h"
-#define sign_extend(x, signbit) \
- ((x) | (0 - ((x) & (1 << (signbit)))))
-
#define bbl_displacement(insn) \
- sign_extend(((insn) & 0x3ffffff) << 2, 27)
+ sign_extend32(((insn) & 0x3ffffff) << 2, 27)
#define bcond_displacement(insn) \
- sign_extend(((insn >> 5) & 0x7ffff) << 2, 20)
+ sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
#define cbz_displacement(insn) \
- sign_extend(((insn >> 5) & 0x7ffff) << 2, 20)
+ sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
#define tbz_displacement(insn) \
- sign_extend(((insn >> 5) & 0x3fff) << 2, 15)
+ sign_extend32(((insn >> 5) & 0x3fff) << 2, 15)
#define ldr_displacement(insn) \
- sign_extend(((insn >> 5) & 0x7ffff) << 2, 20)
+ sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
static inline void set_x_reg(struct pt_regs *regs, int reg, u64 val)
{
@@ -106,7 +104,7 @@ static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs)
xn = opcode & 0x1f;
imm = ((opcode >> 3) & 0x1ffffc) | ((opcode >> 29) & 0x3);
- imm = sign_extend(imm, 20);
+ imm = sign_extend64(imm, 20);
if (opcode & 0x80000000)
val = (imm<<12) + (addr & 0xfffffffffffff000);
else
--
1.9.1
^ permalink raw reply related
* [PATCH 02/10] mm: remove write/force parameters from __get_user_pages_unlocked()
From: Jan Kara @ 2016-10-18 12:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-3-lstoakes@gmail.com>
On Thu 13-10-16 01:20:12, Lorenzo Stoakes wrote:
> This patch removes the write and force parameters from
> __get_user_pages_unlocked() to make the use of FOLL_FORCE explicit in callers as
> use of this flag can result in surprising behaviour (and hence bugs) within the
> mm subsystem.
>
> Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
The patch looks good. You can add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply
* [PATCH 03/10] mm: replace get_user_pages_unlocked() write/force parameters with gup_flags
From: Jan Kara @ 2016-10-18 12:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-4-lstoakes@gmail.com>
On Thu 13-10-16 01:20:13, Lorenzo Stoakes wrote:
> This patch removes the write and force parameters from get_user_pages_unlocked()
> and replaces them with a gup_flags parameter to make the use of FOLL_FORCE
> explicit in callers as use of this flag can result in surprising behaviour (and
> hence bugs) within the mm subsystem.
>
> Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
Looks good. You can add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply
* [PATCH 04/10] mm: replace get_user_pages_locked() write/force parameters with gup_flags
From: Jan Kara @ 2016-10-18 12:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-5-lstoakes@gmail.com>
On Thu 13-10-16 01:20:14, Lorenzo Stoakes wrote:
> This patch removes the write and force parameters from get_user_pages_locked()
> and replaces them with a gup_flags parameter to make the use of FOLL_FORCE
> explicit in callers as use of this flag can result in surprising behaviour (and
> hence bugs) within the mm subsystem.
>
> Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
> ---
> include/linux/mm.h | 2 +-
> mm/frame_vector.c | 8 +++++++-
> mm/gup.c | 12 +++---------
> mm/nommu.c | 5 ++++-
> 4 files changed, 15 insertions(+), 12 deletions(-)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 6adc4bc..27ab538 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -1282,7 +1282,7 @@ long get_user_pages(unsigned long start, unsigned long nr_pages,
> int write, int force, struct page **pages,
> struct vm_area_struct **vmas);
> long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
> - int write, int force, struct page **pages, int *locked);
> + unsigned int gup_flags, struct page **pages, int *locked);
Hum, the prototype is inconsistent with e.g. __get_user_pages_unlocked()
where gup_flags come after **pages argument. Actually it makes more sense
to have it before **pages so that input arguments come first and output
arguments second but I don't care that much. But it definitely should be
consistent...
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply
* [PATCH] arm64: Cortex-A53 errata workaround: check for kernel addresses
From: Mark Rutland @ 2016-10-18 13:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161018111627.17366-1-andre.przywara@arm.com>
On Tue, Oct 18, 2016 at 12:16:27PM +0100, Andre Przywara wrote:
> Commit 7dd01aef0557 ("arm64: trap userspace "dc cvau" cache operation on
> errata-affected core") adds code to execute cache maintenance instructions
> in the kernel on behalf of userland on CPUs with certain ARM CPU errata.
> It turns out that the address hasn't been checked to be a valid user
> space address, allowing userland to clean cache lines in kernel space.
> Fix this by introducing an access_ok() check before executing the
> instructions on behalf of userland, taking care of tagged pointers on
> the way.
It would be worth calling out why we need access_ok_tagged here (i.e.
since this is not a syscall, the tag bits may validly be set, and we
must mask them out to check the "real" address).
> Reported-by: Kristina Martsenko <kristina.martsenko@arm.com>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> Cc: <stable@vger.kernel.org> # 4.8.x
It would be good to have an explicit:
Fixes: 7dd01aef0557 ("arm64: trap userspace "dc cvau" cache operation on errata-affected core")
> ---
> arch/arm64/include/asm/uaccess.h | 4 ++++
> arch/arm64/kernel/traps.c | 32 ++++++++++++++++++++++++++++----
> 2 files changed, 32 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
> index bcaf6fb..f842b47 100644
> --- a/arch/arm64/include/asm/uaccess.h
> +++ b/arch/arm64/include/asm/uaccess.h
> @@ -21,6 +21,7 @@
> /*
> * User space memory access functions
> */
> +#include <linux/bitops.h>
> #include <linux/kasan-checks.h>
> #include <linux/string.h>
> #include <linux/thread_info.h>
> @@ -103,6 +104,9 @@ static inline void set_fs(mm_segment_t fs)
> })
>
> #define access_ok(type, addr, size) __range_ok(addr, size)
> +#define access_ok_tagged(type, addr, size) access_ok(type, \
> + sign_extend64(addr, 55), \
> + size)
> #define user_addr_max get_fs
>
> #define _ASM_EXTABLE(from, to) \
> diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
> index 5ff020f..04ea0d7 100644
> --- a/arch/arm64/kernel/traps.c
> +++ b/arch/arm64/kernel/traps.c
> @@ -447,6 +447,30 @@ void cpu_enable_cache_maint_trap(void *__unused)
> : "=r" (res) \
> : "r" (address), "i" (-EFAULT) )
>
> +enum {USER_CACHE_MAINT_DC_CIVAC, USER_CACHE_MAINT_IC_IVAU};
> +
> +static int do_user_cache_maint(int ins_type, unsigned long address)
> +{
> + int ret;
> + unsigned long cl_size = cache_line_size();
> +
> + if (!access_ok_tagged(VERIFY_READ,
> + round_down(address, cl_size),
> + cl_size))
> + return -EFAULT;
We're only checking the D$ line size here; the I$ is not reported by
cache_line_size().
We may as well use PAGE_SIZE here, given cache lines have to be
naturally aligned and permissions are at page granularity. There's no
functional difference, but the value can't change under our feet, and
the compiler may be able to better optimize by folding the contant in.
> +
> + switch (ins_type) {
> + case USER_CACHE_MAINT_DC_CIVAC:
> + __user_cache_maint("dc civac", address, ret);
> + break;
> + case USER_CACHE_MAINT_IC_IVAU:
> + __user_cache_maint("ic ivau", address, ret);
> + break;
> + }
> +
> + return ret;
> +}
We could make this function a macro (passing in the instruction
explicitly), and avoid the enum and switch.
Other than that, this looks good to me.
Thanks,
Mark.
> +
> static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
> {
> unsigned long address;
> @@ -458,16 +482,16 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
>
> switch (crm) {
> case ESR_ELx_SYS64_ISS_CRM_DC_CVAU: /* DC CVAU, gets promoted */
> - __user_cache_maint("dc civac", address, ret);
> + ret = do_user_cache_maint(USER_CACHE_MAINT_DC_CIVAC, address);
> break;
> case ESR_ELx_SYS64_ISS_CRM_DC_CVAC: /* DC CVAC, gets promoted */
> - __user_cache_maint("dc civac", address, ret);
> + ret = do_user_cache_maint(USER_CACHE_MAINT_DC_CIVAC, address);
> break;
> case ESR_ELx_SYS64_ISS_CRM_DC_CIVAC: /* DC CIVAC */
> - __user_cache_maint("dc civac", address, ret);
> + ret = do_user_cache_maint(USER_CACHE_MAINT_DC_CIVAC, address);
> break;
> case ESR_ELx_SYS64_ISS_CRM_IC_IVAU: /* IC IVAU */
> - __user_cache_maint("ic ivau", address, ret);
> + ret = do_user_cache_maint(USER_CACHE_MAINT_IC_IVAU, address);
> break;
> default:
> force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
> --
> 2.9.0
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply
* [PATCH V3 05/10] acpi: apei: handle SEA notification type for ARMv8
From: Hanjun Guo @ 2016-10-18 13:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1475875882-2604-6-git-send-email-tbaicar@codeaurora.org>
On 2016/10/8 5:31, Tyler Baicar wrote:
> ARM APEI extension proposal added SEA (Synchrounous External
> Abort) notification type for ARMv8.
> Add a new GHES error source handling function for SEA. If an error
> source's notification type is SEA, then this function can be registered
> into the SEA exception handler. That way GHES will parse and report
> SEA exceptions when they occur.
>
> Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
> Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
> Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
> ---
> arch/arm64/Kconfig | 1 +
> drivers/acpi/apei/Kconfig | 15 +++++++++
> drivers/acpi/apei/ghes.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 99 insertions(+)
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index b380c87..ae34349 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -53,6 +53,7 @@ config ARM64
> select HANDLE_DOMAIN_IRQ
> select HARDIRQS_SW_RESEND
> select HAVE_ACPI_APEI if (ACPI && EFI)
> + select HAVE_ACPI_APEI_SEA if (ACPI && EFI)
> select HAVE_ALIGNED_STRUCT_PAGE if SLUB
> select HAVE_ARCH_AUDITSYSCALL
> select HAVE_ARCH_BITREVERSE
> diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
> index b0140c8..fb99c1c 100644
> --- a/drivers/acpi/apei/Kconfig
> +++ b/drivers/acpi/apei/Kconfig
> @@ -4,6 +4,21 @@ config HAVE_ACPI_APEI
> config HAVE_ACPI_APEI_NMI
> bool
>
> +config HAVE_ACPI_APEI_SEA
> + bool "APEI Synchronous External Abort logging/recovering support"
> + depends on ARM64
> + help
> + This option should be enabled if the system supports
> + firmware first handling of SEA (Synchronous External Abort).
> + SEA happens with certain faults of data abort or instruction
> + abort synchronous exceptions on ARMv8 systems. If a system
> + supports firmware first handling of SEA, the platform analyzes
> + and handles hardware error notifications with SEA, and it may then
> + form a HW error record for the OS to parse and handle. This
> + option allows the OS to look for such HW error record, and
> + take appropriate action.
OK, I can see that it's firmware first handling, so it's triggered
by firmware to me, correct me if I'm wrong.
[...]
> #ifdef CONFIG_HAVE_ACPI_APEI_NMI
> /*
> * printk is not safe in NMI context. So in NMI handler, we allocate
> @@ -1023,6 +1083,14 @@ static int ghes_probe(struct platform_device *ghes_dev)
> case ACPI_HEST_NOTIFY_EXTERNAL:
> case ACPI_HEST_NOTIFY_SCI:
> break;
> + case ACPI_HEST_NOTIFY_SEA:
> + if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_SEA)) {
> + pr_warn(GHES_PFX "Generic hardware error source: %d notified via SEA is not supported\n",
> + generic->header.source_id);
> + rc = -ENOTSUPP;
> + goto err;
> + }
> + break;
> case ACPI_HEST_NOTIFY_NMI:
> if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) {
> pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n",
> @@ -1034,6 +1102,13 @@ static int ghes_probe(struct platform_device *ghes_dev)
> pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
> generic->header.source_id);
> goto err;
> + case ACPI_HEST_NOTIFY_GPIO:
> + case ACPI_HEST_NOTIFY_SEI:
> + case ACPI_HEST_NOTIFY_GSIV:
> + pr_warn(GHES_PFX "Generic hardware error source: %d notified via notification type %u is not supported\n",
> + generic->header.source_id, generic->header.source_id);
Hmm, some platform may trigger a interrupt to OS for firmware handling
and it's in the ACPI 6.1 spec, is it a limitation now, or we need to
add code later to support it?
Thanks
Hanjun
^ permalink raw reply
* [PATCH v6 2/2] devicetree: bindings: uart: Add new compatible string for ZynqMP
From: Rob Herring @ 2016-10-18 13:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476258448-20483-2-git-send-email-navam@xilinx.com>
On Wed, Oct 12, 2016 at 01:17:28PM +0530, Nava kishore Manne wrote:
> From: Nava kishore Manne <nava.manne@xilinx.com>
>
> This patch Adds the new compatible string for ZynqMP SoC.
>
> Signed-off-by: Nava kishore Manne <navam@xilinx.com>
> ---
> Changes for v6:
> -Added New compatiable string for ZynqMP SoC as
> suggested by Rob Herring.
> Changes for v5:
> -Mofified the compatible session.
> Changes for v4:
> -Modified the ChangeLog comment.
> Changes for v3:
> -Added changeLog comment.
> Changes for v2:
> -None
>
> Documentation/devicetree/bindings/serial/cdns,uart.txt | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [PATCH 7/10] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
From: Adrian Hunter @ 2016-10-18 13:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <d6e1c702-1dfe-3993-da28-01cf01f24b4a@marvell.com>
On 18/10/16 15:04, Ziji Hu wrote:
> Hi Adrian,
>
> On 2016/10/17 15:55, Adrian Hunter wrote:
>> On 12/10/16 15:17, Ziji Hu wrote:
>>> Hi Adrian,
>>>
>>> On 2016/10/11 20:39, Adrian Hunter wrote:
> <snip>
>>>>> +static int __xenon_emmc_delay_adj_test(struct mmc_card *card)
>>>>> +{
>>>>> + int err;
>>>>> + u8 *ext_csd = NULL;
>>>>> +
>>>>> + err = mmc_get_ext_csd(card, &ext_csd);
>>>>> + kfree(ext_csd);
>>>>> +
>>>>> + return err;
>>>>> +}
>>>>> +
>>>>> +static int __xenon_sdio_delay_adj_test(struct mmc_card *card)
>>>>> +{
>>>>> + struct mmc_command cmd = {0};
>>>>> + int err;
>>>>> +
>>>>> + cmd.opcode = SD_IO_RW_DIRECT;
>>>>> + cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
>>>>> +
>>>>> + err = mmc_wait_for_cmd(card->host, &cmd, 0);
>>>>> + if (err)
>>>>> + return err;
>>>>> +
>>>>> + if (cmd.resp[0] & R5_ERROR)
>>>>> + return -EIO;
>>>>> + if (cmd.resp[0] & R5_FUNCTION_NUMBER)
>>>>> + return -EINVAL;
>>>>> + if (cmd.resp[0] & R5_OUT_OF_RANGE)
>>>>> + return -ERANGE;
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int __xenon_sd_delay_adj_test(struct mmc_card *card)
>>>>> +{
>>>>> + struct mmc_command cmd = {0};
>>>>> + int err;
>>>>> +
>>>>> + cmd.opcode = MMC_SEND_STATUS;
>>>>> + cmd.arg = card->rca << 16;
>>>>> + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
>>>>> +
>>>>> + err = mmc_wait_for_cmd(card->host, &cmd, 0);
>>>>> + return err;
>>>>> +}
>>>>> +
>>>>> +static int xenon_delay_adj_test(struct mmc_card *card)
>>>>> +{
>>>>> + WARN_ON(!card);
>>>>> + WARN_ON(!card->host);
>>>>> +
>>>>> + if (mmc_card_mmc(card))
>>>>> + return __xenon_emmc_delay_adj_test(card);
>>>>> + else if (mmc_card_sd(card))
>>>>> + return __xenon_sd_delay_adj_test(card);
>>>>> + else if (mmc_card_sdio(card))
>>>>> + return __xenon_sdio_delay_adj_test(card);
>>>>> + else
>>>>> + return -EINVAL;
>>>>> +}
>>>>
>>>> So you are issuing commands from the ->set_ios() callback. I would want to
>>>> get Ulf's OK for that before going further.
>>>>
>>> Yes, you are correct.
>>> In some speed mode, Xenon SDHC has to send a series of transfers to search for a perfect sampling point in PHY delay line.
>>> It is like tuning process.
>>>
>>>> One thing: you will need to ensure you don't trigger get HS400 re-tuning
>>>> because it will call back into ->set_ios().
>>>>
>>> Could you please make the term "HS400 re-tuning" more detailed?
>>> In current MMC driver, "HS400 re-tuning" will go back to HS200, execute HS200 tuning and come back to HS400.
>>> I'm sure our Xenon SDHC will not execute it.
>>
>> Currently, re-tuning is automatically enabled whenever tuning is executed,
>> and then re-tuning will be done periodically or after CRC errors. The
>> function to disable re-tuning mmc_retune_disable() is not exported, however
>> if you have you are determining the sampling point your own way, you could
>> simply not implement ->execute_tuning() and then there would be no tuning
>> and no re-tuning.
>>
>
> It is a little complex in our Xenon SDHC.
> For the speed mode which requests tuning, such as SDR104 and HS200, our driver will execute standard tuning as spec requires.
> However, for those speed mode in which tuning is not requested in spec, our driver has to issues commands to search for the best sampling point.
>
> It seems that HS400 re-tuning/tuning is disabled by default. Is it correct?
No, it is enabled by default - there is currently no periodic re-tuning for
HS400 but CRC errors or runtime suspend / resume will cause re-tuning.
>
>>>
>>> However, in coming eMMC 5.2, there is a real HS400 re-tuning, in which tuning can be directly executed in HS400 mode.
>>> Our Xenon SDHC will neither trigger this HS400 re-tuning.
>>> But since so far there is no such feature in MMC driver, I cannot give you a 100% guarantee now.
>>>
>>>> And you have the problem that you need to get a reference to the card before
>>>> the card device has been added. As I wrote in response to the previous
>>>> patch, you should get Ulf's help with that too.
>>>>
>>> Sure.
>>> I will get card_candidate solved at first.
>>> Thank you again for your review and help.
>>>
>>> Thank you.
>>>
>>> Best regards,
>>> Hu Ziji
>>>>
>>>
>>
>
^ permalink raw reply
* [PATCH 1/2] mmc: sdhci-iproc: Add brcm, sdhci-iproc compat string in bindings document
From: Rob Herring @ 2016-10-18 13:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476297352-7812-2-git-send-email-scott.branden@broadcom.com>
On Wed, Oct 12, 2016 at 11:35:51AM -0700, Scott Branden wrote:
> Adds brcm,sdhci-iproc compat string to DT bindings document for
> the iProc SDHCI driver.
>
> Signed-off-by: Anup Patel <anup.patel@broadcom.com>
> Signed-off-by: Scott Branden <scott.branden@broadcom.com>
> ---
> Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt b/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
> index be56d2b..aa58b94 100644
> --- a/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
> +++ b/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
> @@ -7,6 +7,7 @@ Required properties:
> - compatible : Should be one of the following
> "brcm,bcm2835-sdhci"
> "brcm,sdhci-iproc-cygnus"
> + "brcm,sdhci-iproc"
Seems kind of generic. SoC specific compatible strings please.
^ permalink raw reply
* [PATCH 5/10] dt: bindings: Add bindings for Marvell Xenon SD Host Controller
From: Gregory CLEMENT @ 2016-10-18 13:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <d95c3035-e3cd-a93c-d408-27f4c8dba589@marvell.com>
Hi Rob,
On mar., oct. 11 2016, Ziji Hu <huziji@marvell.com> wrote:
[...]
>>> + Different Xenon SDHC release has different register set size.
>>> + The specific size should also refer to the SOC implementation.
>>> +
>>> +Optional Properties:
>>> +- Slot Index
>>> + A single Xenon IP can support multiple slots.
>>> + During initialization, each slot should set corresponding setting bit in
>>> + some Xenon-specific registers. The corresponding bit is determined by
>>> + this property.
>>> + - xenon,slotno = <slot_index>;
>>
>> Slots should probably be represented as child nodes with the reg
>> property being the slot number.
>
> Since each SDHC slot is independent, I find it is more
> convenient to implement each one as independent SD host/MMC host
> instant.
> Otherwise, a main function should loop and initialize each
> slot, like sdhci-pci. I prefer to avoiding such a unnecessary main
> function.
>
> It is very hard to determine the slot number by reg property.
> Xenon slots are likely to be different types. 1st slot might
> be eMMC and 2nd one might be SD. They might have different register
> size.
> The register size might also varies in different Xenon versions.
>
Something that took me a while to figure out is that even it is the same
hardware block which handle multiple SoCs.
Each slots is managed by its own set of register. From the point of view
of the OS, it is as if we have an independent controller for each
slot.
But for an obscure reason, some command need to know which slot is
used. That's why we ended with this property.
With some example what you had in mind was something like that:
sdhci at aa0000 {
compatible = "marvell,armada-3700-sdhci";
reg = <0xaa0000 0x1000>;
[...]
slot0 {
/* slot0 is an eMMC */
reg = <0>;
bus-width = <8>;
xenon,pad-type = "fixed-1-8v";
}
slot1 {
/* slot1 is an SD Card */
reg = <1>;
bus-width = <4>;
xenon,pad-type = "fixed-1-8v";
}
};
But it won't work as each slot uses its own address registers, that why we
ended with this:
sdhci at aa0000 {
/* slot0 is an eMMC */
compatible = "marvell,armada-3700-sdhci";
reg = <0xaa0000 0x1000>;
[...]
xenon,slotno = <0>;
bus-width = <8>;
xenon,pad-type = "fixed-1-8v";
};
sdhci at bb0000 {
/* slot1 is an SD Card */
compatible = "marvell,armada-3700-sdhci";
reg = <0xbb0000 0x1000>;
[...]
xenon,slotno = <1>;
bus-width = <4>;
xenon,pad-type = "fixed-1-8v";
};
I hope it is more clear now.
Gregory
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* [PATCH 04/10] mm: replace get_user_pages_locked() write/force parameters with gup_flags
From: Lorenzo Stoakes @ 2016-10-18 13:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161018125425.GD29967@quack2.suse.cz>
On Tue, Oct 18, 2016 at 02:54:25PM +0200, Jan Kara wrote:
> > @@ -1282,7 +1282,7 @@ long get_user_pages(unsigned long start, unsigned long nr_pages,
> > int write, int force, struct page **pages,
> > struct vm_area_struct **vmas);
> > long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
> > - int write, int force, struct page **pages, int *locked);
> > + unsigned int gup_flags, struct page **pages, int *locked);
>
> Hum, the prototype is inconsistent with e.g. __get_user_pages_unlocked()
> where gup_flags come after **pages argument. Actually it makes more sense
> to have it before **pages so that input arguments come first and output
> arguments second but I don't care that much. But it definitely should be
> consistent...
It was difficult to decide quite how to arrange parameters as there was
inconsitency with regards to parameter ordering already - for example
__get_user_pages() places its flags argument before pages whereas, as you note,
__get_user_pages_unlocked() puts them afterwards.
I ended up compromising by trying to match the existing ordering of the function
as much as I could by replacing write, force pairs with gup_flags in the same
location (with the exception of get_user_pages_unlocked() which I felt should
match __get_user_pages_unlocked() in signature) or if there was already a
gup_flags parameter as in the case of __get_user_pages_unlocked() I simply
removed the write, force pair and left the flags as the last parameter.
I am happy to rearrange parameters as needed, however I am not sure if it'd be
worthwhile for me to do so (I am keen to try to avoid adding too much noise here
:)
If we were to rearrange parameters for consistency I'd suggest adjusting
__get_user_pages_unlocked() to put gup_flags before pages and do the same with
get_user_pages_unlocked(), let me know what you think.
^ permalink raw reply
* [PATCH V3 1/3] ACPI, PCI IRQ: add PCI_USING penalty for ISA interrupts
From: Bjorn Helgaas @ 2016-10-18 13:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476505867-24599-2-git-send-email-okaya@codeaurora.org>
On Sat, Oct 15, 2016 at 12:31:05AM -0400, Sinan Kaya wrote:
> The change introduced in commit 103544d86976 ("ACPI,PCI,IRQ: reduce
> resource requirements") removed PCI_USING penalty from
> acpi_pci_link_allocate function as there is no longer a fixed size penalty
> array for both PCI interrupts greater than 16.
>
> The array size has been reduced to 16 and array name got prefixed as ISA
> since it only is accountable for the ISA interrupts.
>
> The original change in commit 103544d86976 ("ACPI,PCI,IRQ: reduce
> resource requirements") removed penalty assignment in the code for PCI
> thinking that we will add the penalty later in acpi_irq_pci_sharing_penalty
> function.
>
> However, this function only gets called if the IRQ number is greater than
> 16 and acpi_irq_get_penalty function gets called before ACPI start in
> acpi_isa_irq_available and acpi_penalize_isa_irq functions. We can't rely
> on iterating the link list.
It seems wrong to me that we call acpi_irq_get_penalty() from
acpi_irq_penalty_update() and acpi_penalize_isa_irq(). It seems like they
should just manipulate acpi_isa_irq_penalty[irq] directly.
acpi_irq_penalty_update() is for command-line parameters, so it certainly
doesn't need the acpi_irq_pci_sharing_penalty() information (the
acpi_link_list should be empty at the time we process the command-line
parameters).
acpi_penalize_isa_irq() is telling us that a PNP or ACPI device is using
the IRQ -- this should modify the IRQ's penalty, but it shouldn't depend on
the acpi_irq_pci_sharing_penalty() value at all.
> We need to add the PCI_USING penalty for ISA interrupts too if the link is
> in use and matches our ISA IRQ number.
>
> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
> ---
> drivers/acpi/pci_link.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
> index c983bf7..a212709 100644
> --- a/drivers/acpi/pci_link.c
> +++ b/drivers/acpi/pci_link.c
> @@ -619,6 +619,10 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link)
> acpi_device_bid(link->device));
> return -ENODEV;
> } else {
> + if (link->irq.active < ACPI_MAX_ISA_IRQS)
> + acpi_isa_irq_penalty[link->irq.active] +=
> + PIRQ_PENALTY_PCI_USING;
> +
> printk(KERN_WARNING PREFIX "%s [%s] enabled at IRQ %d\n",
> acpi_device_name(link->device),
> acpi_device_bid(link->device), link->irq.active);
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH V3 2/3] Revert "ACPI,PCI,IRQ: remove SCI penalize function"
From: Bjorn Helgaas @ 2016-10-18 14:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476505867-24599-3-git-send-email-okaya@codeaurora.org>
On Sat, Oct 15, 2016 at 12:31:06AM -0400, Sinan Kaya wrote:
> The SCI function was removed in two steps (first refactor and then remove).
> This patch does the revert in one step.
>
> The commit 103544d86976 ("ACPI,PCI,IRQ: reduce resource requirements")
> refactored the original code so that SCI penalty is calculated dynamically
> by the time get_penalty function is called. This patch does a partial
> revert for the SCI functionality only.
>
> The commit 9e5ed6d1fb87 ("ACPI,PCI,IRQ: remove SCI penalize function") is
> for the removal of the function. SCI penalty API was replaced by the
> runtime penalty calculation based on the value of
> acpi_gbl_FADT.sci_interrupt.
>
> The IRQ type does not get updated at the right time for some platforms and
> results in incorrect penalty assignment for PCI IRQs as
> irq_get_trigger_type returns the wrong type.
>
> The register_gsi function delivers the IRQ found in the ACPI table to
> the interrupt controller driver. Penalties are calculated before a
> link object is enabled to find out which interrupt has the least number
> of users. By the time penalties are calculated, the IRQ is not registered
> yet and the API returns the wrong type.
I think you should squash patches 2 & 3 and not bother with trying to
make this a revert followed by a fix. Squashing them makes the git
history much easier to follow, plus you don't have to deal with the
headache of ensuring the intermediate state is correct and bisectable.
> Tested-by: Jonathan Liu <net147@gmail.com>
> Tested-by: Ondrej Zary <linux@rainbow-software.org>
> Link: https://lkml.org/lkml/2016/10/4/283
> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
> Acked-by: Thomas Gleixner <tglx@linutronix.de>
> ---
> arch/x86/kernel/acpi/boot.c | 1 +
> drivers/acpi/pci_link.c | 32 +++++++++++++-------------------
> include/linux/acpi.h | 1 +
> 3 files changed, 15 insertions(+), 19 deletions(-)
>
> diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
> index 90d84c3..0ffd26e 100644
> --- a/arch/x86/kernel/acpi/boot.c
> +++ b/arch/x86/kernel/acpi/boot.c
> @@ -453,6 +453,7 @@ static void __init acpi_sci_ioapic_setup(u8 bus_irq, u16 polarity, u16 trigger,
> polarity = acpi_sci_flags & ACPI_MADT_POLARITY_MASK;
>
> mp_override_legacy_irq(bus_irq, polarity, trigger, gsi);
> + acpi_penalize_sci_irq(bus_irq, trigger, polarity);
>
> /*
> * stash over-ride to indicate we've been here
> diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
> index a212709..1934e2a 100644
> --- a/drivers/acpi/pci_link.c
> +++ b/drivers/acpi/pci_link.c
> @@ -494,27 +494,10 @@ static int acpi_irq_pci_sharing_penalty(int irq)
>
> static int acpi_irq_get_penalty(int irq)
> {
> - int penalty = 0;
> -
> - /*
> - * Penalize IRQ used by ACPI SCI. If ACPI SCI pin attributes conflict
> - * with PCI IRQ attributes, mark ACPI SCI as ISA_ALWAYS so it won't be
> - * use for PCI IRQs.
> - */
> - if (irq == acpi_gbl_FADT.sci_interrupt) {
> - u32 type = irq_get_trigger_type(irq) & IRQ_TYPE_SENSE_MASK;
> -
> - if (type != IRQ_TYPE_LEVEL_LOW)
> - penalty += PIRQ_PENALTY_ISA_ALWAYS;
> - else
> - penalty += PIRQ_PENALTY_PCI_USING;
> - }
> -
> if (irq < ACPI_MAX_ISA_IRQS)
> - return penalty + acpi_isa_irq_penalty[irq];
> + return acpi_isa_irq_penalty[irq];
>
> - penalty += acpi_irq_pci_sharing_penalty(irq);
> - return penalty;
> + return acpi_irq_pci_sharing_penalty(irq);
> }
>
> int __init acpi_irq_penalty_init(void)
> @@ -885,6 +868,17 @@ bool acpi_isa_irq_available(int irq)
> acpi_irq_get_penalty(irq) < PIRQ_PENALTY_ISA_ALWAYS);
> }
>
> +void acpi_penalize_sci_irq(int irq, int trigger, int polarity)
> +{
> + if (irq >= 0 && irq < ARRAY_SIZE(acpi_isa_irq_penalty)) {
> + if (trigger != ACPI_MADT_TRIGGER_LEVEL ||
> + polarity != ACPI_MADT_POLARITY_ACTIVE_LOW)
> + acpi_isa_irq_penalty[irq] += PIRQ_PENALTY_ISA_ALWAYS;
> + else
> + acpi_isa_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING;
> + }
> +}
> +
> /*
> * Over-ride default table to reserve additional IRQs for use by ISA
> * e.g. acpi_irq_isa=5
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index c5eaf2f..67d1d3e 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -318,6 +318,7 @@ struct pci_dev;
> int acpi_pci_irq_enable (struct pci_dev *dev);
> void acpi_penalize_isa_irq(int irq, int active);
> bool acpi_isa_irq_available(int irq);
> +void acpi_penalize_sci_irq(int irq, int trigger, int polarity);
> void acpi_pci_irq_disable (struct pci_dev *dev);
>
> extern int ec_read(u8 addr, u8 *val);
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 1/4] dt-bindings: mfd: Add Altera Arria10 SR Monitor
From: Rob Herring @ 2016-10-18 14:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476394329-31696-2-git-send-email-tthayer@opensource.altera.com>
On Thu, Oct 13, 2016 at 04:32:06PM -0500, tthayer at opensource.altera.com wrote:
> From: Thor Thayer <tthayer@opensource.altera.com>
>
> Add the Arria10 DevKit System Resource Chip register and state
> monitoring module to the MFD.
>
> Signed-off-by: Thor Thayer <tthayer@opensource.altera.com>
> ---
> Note: This needs to be applied to the bindings document that
> was Acked & Applied but didn't reach the for-next branch.
> See https://patchwork.ozlabs.org/patch/629397/
> ---
> ---
> .../devicetree/bindings/mfd/altera-a10sr.txt | 9 +++++++++
> 1 file changed, 9 insertions(+)
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [PATCH v2 5/6] clk: stm32f469: Add QSPI clock
From: Rob Herring @ 2016-10-18 14:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476436699-21879-6-git-send-email-gabriel.fernandez@st.com>
On Fri, Oct 14, 2016 at 11:18:18AM +0200, gabriel.fernandez at st.com wrote:
> From: Gabriel Fernandez <gabriel.fernandez@st.com>
>
> This patch adds the QSPI clock for stm32f469 discovery board.
> The gate mapping is a little bit different from stm32f429 soc.
>
> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
> ---
> .../devicetree/bindings/clock/st,stm32-rcc.txt | 4 +-
> drivers/clk/clk-stm32f4.c | 173 ++++++++++++++++++---
> 2 files changed, 158 insertions(+), 19 deletions(-)
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [PATCH] crypto: sun4i-ss: support the Security System PRNG
From: Stephan Mueller @ 2016-10-18 14:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476794067-28563-1-git-send-email-clabbe.montjoie@gmail.com>
Am Dienstag, 18. Oktober 2016, 14:34:27 CEST schrieb Corentin Labbe:
Hi Corentin,
> diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-hwrng.c
> b/drivers/crypto/sunxi-ss/sun4i-ss-hwrng.c new file mode 100644
> index 0000000..95fadb7
> --- /dev/null
> +++ b/drivers/crypto/sunxi-ss/sun4i-ss-hwrng.c
> @@ -0,0 +1,70 @@
> +#include "sun4i-ss.h"
> +
> +static int sun4i_ss_hwrng_init(struct hwrng *hwrng)
> +{
> + struct sun4i_ss_ctx *ss;
> +
> + ss = container_of(hwrng, struct sun4i_ss_ctx, hwrng);
> + get_random_bytes(ss->seed, SS_SEED_LEN);
Is it wise to call get_random_bytes once in the init function and never
thereafter?
This init function may be called during boot time of the kernel at which the
input_pool may not yet have received sufficient amounts of entropy.
What about registering a callback with add_random_ready_callback and seed
again when sufficient entropy was collected?
Ciao
Stephan
^ permalink raw reply
* [PATCH 4/9] regulator: lp873x: Add support for populating input supply
From: Rob Herring @ 2016-10-18 14:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161014130033.14172-5-lokeshvutla@ti.com>
On Fri, Oct 14, 2016 at 06:30:28PM +0530, Lokesh Vutla wrote:
> In order to have a proper topology of regulators for a platform, each
> registering regulator needs to populate supply_name field for identifying
> its supply's name. Add supply_name field for lp873x regulators.
>
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: Keerthy <j-keerthy@ti.com>
> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
> ---
> Documentation/devicetree/bindings/mfd/lp873x.txt | 8 ++++++++
> drivers/regulator/lp873x-regulator.c | 1 +
> 2 files changed, 9 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mfd/lp873x.txt b/Documentation/devicetree/bindings/mfd/lp873x.txt
> index 52766c2..936cba3 100644
> --- a/Documentation/devicetree/bindings/mfd/lp873x.txt
> +++ b/Documentation/devicetree/bindings/mfd/lp873x.txt
> @@ -7,6 +7,9 @@ Required properties:
> - #gpio-cells: Should be two. The first cell is the pin number and
> the second cell is used to specify flags.
> See ../gpio/gpio.txt for more information.
> + - xxx-supply: Phandle to parent supply node of each regulator
> + populated under regulators node. xxx should match
> + the supply_name populated in driver.
xxx-in-supply?
> - regulators: List of child nodes that specify the regulator
> initialization data.
> Example:
> @@ -17,6 +20,11 @@ pmic: lp8733 at 60 {
> gpio-controller;
> #gpio-cells = <2>;
>
> + buck0-in-supply = <&vsys_3v3>;
> + buck1-in-supply = <&vsys_3v3>;
> + ldo0-in-supply = <&vsys_3v3>;
> + ldo1-in-supply = <&vsys_3v3>;
> +
> regulators {
> lp8733_buck0: buck0 {
> regulator-name = "lp8733-buck0";
^ permalink raw reply
* [PATCH 5/9] ARM: OMAP2+: board-generic: add support for DRA71x family
From: Rob Herring @ 2016-10-18 14:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161014130033.14172-6-lokeshvutla@ti.com>
On Fri, Oct 14, 2016 at 06:30:29PM +0530, Lokesh Vutla wrote:
> DRA71x processor family is a derivative of DRA722 ES2.0 targeted for
> infotainment systems.
>
> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
> ---
> Documentation/devicetree/bindings/arm/omap/omap.txt | 6 ++++++
> arch/arm/mach-omap2/board-generic.c | 1 +
> 2 files changed, 7 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/arm/omap/omap.txt b/Documentation/devicetree/bindings/arm/omap/omap.txt
> index f53e2ee..454b1be 100644
> --- a/Documentation/devicetree/bindings/arm/omap/omap.txt
> +++ b/Documentation/devicetree/bindings/arm/omap/omap.txt
> @@ -86,6 +86,9 @@ SoCs:
> - DRA722
> compatible = "ti,dra722", "ti,dra72", "ti,dra7"
>
> +- DRA718
> + compatible = "ti,dra718", "ti,dra722", "ti,dra72", "ti,dra7"
Are dra72 and dra7 really useful? Your list is a bit long. Perhaps it is
time to drop those on new parts at least.
Either way:
Acked-by: Rob Herring <robh@kernel.org>
> +
> - AM5728
> compatible = "ti,am5728", "ti,dra742", "ti,dra74", "ti,dra7"
>
> @@ -181,6 +184,9 @@ Boards:
> - DRA722 EVM: Software Development Board for DRA722
> compatible = "ti,dra72-evm", "ti,dra722", "ti,dra72", "ti,dra7"
>
> +- DRA718 EVM: Software Development Board for DRA718
> + compatible = "ti,dra718-evm", "ti,dra718", "ti,dra722", "ti,dra72", "ti,dra7"
> +
> - DM3730 Logic PD Torpedo + Wireless: Commercial System on Module with WiFi and Bluetooth
> compatible = "logicpd,dm3730-torpedo-devkit", "ti,omap3630", "ti,omap3"
>
> diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
> index bab814d..981b23a 100644
> --- a/arch/arm/mach-omap2/board-generic.c
> +++ b/arch/arm/mach-omap2/board-generic.c
> @@ -341,6 +341,7 @@ static const char *const dra72x_boards_compat[] __initconst = {
> "ti,am5718",
> "ti,am5716",
> "ti,dra722",
> + "ti,dra718",
> NULL,
> };
>
> --
> 2.9.3
>
^ permalink raw reply
* [v17 2/2] drm/bridge: Add I2C based driver for ps8640 bridge
From: Enric Balletbo Serra @ 2016-10-18 14:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1472280263-18177-2-git-send-email-jitao.shi@mediatek.com>
Hi Jitao,
2016-08-27 8:44 GMT+02:00 Jitao Shi <jitao.shi@mediatek.com>:
> This patch adds drm_bridge driver for parade DSI to eDP bridge chip.
>
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
> Reviewed-by: Daniel Kurtz <djkurtz@chromium.org>
> ---
> Changes since v16:
> - Disable ps8640 DSI MCS Function.
> - Rename gpios name more clearly.
> - Tune the ps8640 power on sequence.
>
> Changes since v15:
> - Drop drm_connector_(un)register calls from parade ps8640.
> The main DRM driver mtk_drm_drv now calls
> drm_connector_register_all() after drm_dev_register() in the
> mtk_drm_bind() function. That function should iterate over all
> connectors and call drm_connector_register() for each of them.
> So, remove drm_connector_(un)register calls from parade ps8640.
>
> Changes since v14:
> - update copyright info.
> - change bridge_to_ps8640 and connector_to_ps8640 to inline function.
> - fix some coding style.
> - use sizeof as array counter.
> - use drm_get_edid when read edid.
> - add mutex when firmware updating.
>
> Changes since v13:
> - add const on data, ps8640_write_bytes(struct i2c_client *client, const u8 *data, u16 data_len)
> - fix PAGE2_SW_REST tyro.
> - move the buf[3] init to entrance of the function.
>
> Changes since v12:
> - fix hw_chip_id build warning
>
> Changes since v11:
> - Remove depends on I2C, add DRM depends
> - Reuse ps8640_write_bytes() in ps8640_write_byte()
> - Use timer check for polling like the routines in <linux/iopoll.h>
> - Fix no drm_connector_unregister/drm_connector_cleanup when ps8640_bridge_attach fail
> - Check the ps8640 hardware id in ps8640_validate_firmware
> - Remove fw_version check
> - Move ps8640_validate_firmware before ps8640_enter_bl
> - Add ddc_i2c unregister when probe fail and ps8640_remove
> ---
> drivers/gpu/drm/bridge/Kconfig | 12 +
> drivers/gpu/drm/bridge/Makefile | 1 +
> drivers/gpu/drm/bridge/parade-ps8640.c | 1077 ++++++++++++++++++++++++++++++++
> 3 files changed, 1090 insertions(+)
> create mode 100644 drivers/gpu/drm/bridge/parade-ps8640.c
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index b590e67..c59d043 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -50,6 +50,18 @@ config DRM_PARADE_PS8622
> ---help---
> Parade eDP-LVDS bridge chip driver.
>
> +config DRM_PARADE_PS8640
> + tristate "Parade PS8640 MIPI DSI to eDP Converter"
> + depends on DRM
> + depends on OF
> + select DRM_KMS_HELPER
> + select DRM_MIPI_DSI
> + select DRM_PANEL
> + ---help---
> + Choose this option if you have PS8640 for display
> + The PS8640 is a high-performance and low-power
> + MIPI DSI to eDP converter
> +
> config DRM_SII902X
> tristate "Silicon Image sii902x RGB/HDMI bridge"
> depends on OF
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index efdb07e..3360537 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -5,6 +5,7 @@ obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
> obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
> obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
> obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
> +obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
> obj-$(CONFIG_DRM_SII902X) += sii902x.o
> obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
> obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
> diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
> new file mode 100644
> index 0000000..7d67431
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/parade-ps8640.c
> @@ -0,0 +1,1077 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + *
> + * 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/delay.h>
> +#include <linux/err.h>
> +#include <linux/firmware.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_graph.h>
> +#include <linux/regulator/consumer.h>
> +#include <asm/unaligned.h>
> +#include <drm/drm_panel.h>
> +
> +#include <drmP.h>
> +#include <drm_atomic_helper.h>
> +#include <drm_crtc_helper.h>
> +#include <drm_crtc.h>
> +#include <drm_edid.h>
> +#include <drm_mipi_dsi.h>
> +
> +#define PAGE1_VSTART 0x6b
> +#define PAGE2_SPI_CFG3 0x82
> +#define I2C_TO_SPI_RESET 0x20
> +#define PAGE2_ROMADD_BYTE1 0x8e
> +#define PAGE2_ROMADD_BYTE2 0x8f
> +#define PAGE2_SWSPI_WDATA 0x90
> +#define PAGE2_SWSPI_RDATA 0x91
> +#define PAGE2_SWSPI_LEN 0x92
> +#define PAGE2_SWSPI_CTL 0x93
> +#define TRIGGER_NO_READBACK 0x05
> +#define TRIGGER_READBACK 0x01
> +#define PAGE2_SPI_STATUS 0x9e
> +#define SPI_READY 0x0c
> +#define PAGE2_GPIO_L 0xa6
> +#define PAGE2_GPIO_H 0xa7
> +#define PS_GPIO9 BIT(1)
> +#define PAGE2_IROM_CTRL 0xb0
> +#define IROM_ENABLE 0xc0
> +#define IROM_DISABLE 0x80
> +#define PAGE2_SW_RESET 0xbc
> +#define SPI_SW_RESET BIT(7)
> +#define MPU_SW_RESET BIT(6)
> +#define PAGE2_ENCTLSPI_WR 0xda
> +#define PAGE2_I2C_BYPASS 0xea
> +#define I2C_BYPASS_EN 0xd0
> +#define PAGE2_MCS_EN 0xf3
> +#define MCS_EN BIT(0)
> +#define PAGE3_SET_ADD 0xfe
> +#define PAGE3_SET_VAL 0xff
> +#define VDO_CTL_ADD 0x13
> +#define VDO_DIS 0x18
> +#define VDO_EN 0x1c
> +#define PAGE4_REV_L 0xf0
> +#define PAGE4_REV_H 0xf1
> +#define PAGE4_CHIP_L 0xf2
> +#define PAGE4_CHIP_H 0xf3
> +
> +/* Firmware */
> +#define PS_FW_NAME "ps864x_fw.bin"
> +
>From where I can download this firmware image?
> +#define FW_CHIP_ID_OFFSET 0
> +#define FW_VERSION_OFFSET 2
> +#define EDID_I2C_ADDR 0x50
> +
> +#define WRITE_STATUS_REG_CMD 0x01
> +#define READ_STATUS_REG_CMD 0x05
> +#define BUSY BIT(0)
> +#define CLEAR_ALL_PROTECT 0x00
> +#define BLK_PROTECT_BITS 0x0c
> +#define STATUS_REG_PROTECT BIT(7)
> +#define WRITE_ENABLE_CMD 0x06
> +#define CHIP_ERASE_CMD 0xc7
> +#define MAX_DEVS 0x8
> +
> +struct ps8640_info {
> + u8 family_id;
> + u8 variant_id;
> + u16 version;
> +};
> +
> +struct ps8640 {
> + struct drm_connector connector;
> + struct drm_bridge bridge;
> + struct edid *edid;
> + struct mipi_dsi_device dsi;
> + struct i2c_client *page[MAX_DEVS];
> + struct i2c_client *ddc_i2c;
> + struct regulator_bulk_data supplies[2];
> + struct drm_panel *panel;
> + struct gpio_desc *gpio_reset;
> + struct gpio_desc *gpio_power_down;
> + struct gpio_desc *gpio_mode_sel;
> + bool enabled;
> +
> + /* firmware file info */
> + struct ps8640_info info;
> + bool in_fw_update;
> + /* for firmware update protect */
> + struct mutex fw_mutex;
> +};
> +
> +static const u8 enc_ctrl_code[6] = { 0xaa, 0x55, 0x50, 0x41, 0x52, 0x44 };
> +static const u8 hw_chip_id[4] = { 0x00, 0x0a, 0x00, 0x30 };
> +
> +static inline struct ps8640 *bridge_to_ps8640(struct drm_bridge *e)
> +{
> + return container_of(e, struct ps8640, bridge);
> +}
> +
> +static inline struct ps8640 *connector_to_ps8640(struct drm_connector *e)
> +{
> + return container_of(e, struct ps8640, connector);
> +}
> +
> +static int ps8640_read(struct i2c_client *client, u8 reg, u8 *data,
> + u16 data_len)
> +{
> + int ret;
> + struct i2c_msg msgs[] = {
> + {
> + .addr = client->addr,
> + .flags = 0,
> + .len = 1,
> + .buf = ®,
> + },
> + {
> + .addr = client->addr,
> + .flags = I2C_M_RD,
> + .len = data_len,
> + .buf = data,
> + }
> + };
> +
> + ret = i2c_transfer(client->adapter, msgs, 2);
> +
> + if (ret == 2)
> + return 0;
> + if (ret < 0)
> + return ret;
> + else
> + return -EIO;
> +}
> +
> +static int ps8640_write_bytes(struct i2c_client *client, const u8 *data,
> + u16 data_len)
> +{
> + int ret;
> + struct i2c_msg msg;
> +
> + msg.addr = client->addr;
> + msg.flags = 0;
> + msg.len = data_len;
> + msg.buf = (u8 *)data;
> +
> + ret = i2c_transfer(client->adapter, &msg, 1);
> + if (ret == 1)
> + return 0;
> + if (ret < 0)
> + return ret;
> + else
> + return -EIO;
> +}
> +
> +static int ps8640_write_byte(struct i2c_client *client, u8 reg, u8 data)
> +{
> + u8 buf[] = { reg, data };
> +
> + return ps8640_write_bytes(client, buf, sizeof(buf));
> +}
> +
> +static void ps8640_get_mcu_fw_version(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[5];
> + u8 fw_ver[2];
> +
> + ps8640_read(client, 0x4, fw_ver, sizeof(fw_ver));
> + ps_bridge->info.version = (fw_ver[0] << 8) | fw_ver[1];
> +
> + DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", fw_ver[0], fw_ver[1]);
> +}
> +
> +static int ps8640_bridge_unmute(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[3];
> + u8 vdo_ctrl_buf[3] = { PAGE3_SET_ADD, VDO_CTL_ADD, VDO_EN };
> +
> + return ps8640_write_bytes(client, vdo_ctrl_buf, sizeof(vdo_ctrl_buf));
> +}
> +
> +static int ps8640_bridge_mute(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[3];
> + u8 vdo_ctrl_buf[3] = { PAGE3_SET_ADD, VDO_CTL_ADD, VDO_DIS };
> +
> + return ps8640_write_bytes(client, vdo_ctrl_buf, sizeof(vdo_ctrl_buf));
> +}
> +
> +static void ps8640_pre_enable(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + struct i2c_client *client = ps_bridge->page[2];
> + struct i2c_client *page1 = ps_bridge->page[1];
> + int err;
> + u8 set_vdo_done, mcs_en, vstart;
> + ktime_t timeout;
> +
> + if (ps_bridge->in_fw_update)
> + return;
> +
> + if (ps_bridge->enabled)
> + return;
> +
> + err = drm_panel_prepare(ps_bridge->panel);
> + if (err < 0) {
> + DRM_ERROR("failed to prepare panel: %d\n", err);
> + return;
> + }
> +
> + err = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),
> + ps_bridge->supplies);
> + if (err < 0) {
> + DRM_ERROR("cannot enable regulators %d\n", err);
> + goto err_panel_unprepare;
> + }
> +
> + gpiod_set_value(ps_bridge->gpio_power_down, 1);
> + gpiod_set_value(ps_bridge->gpio_reset, 0);
> + usleep_range(2000, 2500);
> + gpiod_set_value(ps_bridge->gpio_reset, 1);
> +
> + /*
> + * Wait for the ps8640 embed mcu ready
> + * First wait 200ms and then check the mcu ready flag every 20ms
> + */
> + msleep(200);
> +
> + timeout = ktime_add_ms(ktime_get(), 200);
> + for (;;) {
> + err = ps8640_read(client, PAGE2_GPIO_H, &set_vdo_done, 1);
> + if (err < 0) {
> + DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", err);
> + goto err_regulators_disable;
> + }
> + if ((set_vdo_done & PS_GPIO9) == PS_GPIO9)
> + break;
> + if (ktime_compare(ktime_get(), timeout) > 0)
> + break;
> + msleep(20);
> + }
> +
> + msleep(50);
> +
> + ps8640_read(page1, PAGE1_VSTART, &vstart, 1);
> + DRM_INFO("PS8640 PAGE1.0x6B = 0x%x\n", vstart);
> +
> + /**
> + * The Manufacturer Command Set (MCS) is a device dependent interface
> + * intended for factory programming of the display module default
> + * parameters. Once the display module is configured, the MCS shall be
> + * disabled by the manufacturer. Once disabled, all MCS commands are
> + * ignored by the display interface.
> + */
> + ps8640_read(client, PAGE2_MCS_EN, &mcs_en, 1);
> + ps8640_write_byte(client, PAGE2_MCS_EN, mcs_en & ~MCS_EN);
> +
> + if (ps_bridge->info.version == 0)
> + ps8640_get_mcu_fw_version(ps_bridge);
> +
> + err = ps8640_bridge_unmute(ps_bridge);
> + if (err)
> + DRM_ERROR("failed to enable unmutevideo: %d\n", err);
> + /* Switch access edp panel's edid through i2c */
> + ps8640_write_byte(client, PAGE2_I2C_BYPASS, I2C_BYPASS_EN);
> + ps_bridge->enabled = true;
> +
> + return;
> +
> +err_regulators_disable:
> + regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
> + ps_bridge->supplies);
> +err_panel_unprepare:
> + drm_panel_unprepare(ps_bridge->panel);
> +}
> +
> +static void ps8640_enable(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + int err;
> +
> + err = drm_panel_enable(ps_bridge->panel);
> + if (err < 0)
> + DRM_ERROR("failed to enable panel: %d\n", err);
> +}
> +
> +static void ps8640_disable(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + int err;
> +
> + err = drm_panel_disable(ps_bridge->panel);
> + if (err < 0)
> + DRM_ERROR("failed to disable panel: %d\n", err);
> +}
> +
> +static void ps8640_post_disable(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + int err;
> +
> + if (ps_bridge->in_fw_update)
> + return;
> +
> + if (!ps_bridge->enabled)
> + return;
> +
> + ps_bridge->enabled = false;
> +
> + err = ps8640_bridge_mute(ps_bridge);
> + if (err < 0)
> + DRM_ERROR("failed to unmutevideo: %d\n", err);
> +
> + gpiod_set_value(ps_bridge->gpio_reset, 0);
> + gpiod_set_value(ps_bridge->gpio_power_down, 0);
> + err = regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
> + ps_bridge->supplies);
> + if (err < 0)
> + DRM_ERROR("cannot disable regulators %d\n", err);
> +
> + err = drm_panel_unprepare(ps_bridge->panel);
> + if (err)
> + DRM_ERROR("failed to unprepare panel: %d\n", err);
> +}
> +
> +static int ps8640_get_modes(struct drm_connector *connector)
> +{
> + struct ps8640 *ps_bridge = connector_to_ps8640(connector);
> + struct edid *edid;
> + int num_modes = 0;
> + bool power_off;
> +
> + if (ps_bridge->edid)
> + return drm_add_edid_modes(connector, ps_bridge->edid);
> +
> + power_off = !ps_bridge->enabled;
> + ps8640_pre_enable(&ps_bridge->bridge);
> +
> + edid = drm_get_edid(connector, ps_bridge->ddc_i2c->adapter);
> + if (!edid)
> + goto out;
> +
> + ps_bridge->edid = edid;
> + drm_mode_connector_update_edid_property(connector, ps_bridge->edid);
> + num_modes = drm_add_edid_modes(connector, ps_bridge->edid);
> +
> +out:
> + if (power_off)
> + ps8640_post_disable(&ps_bridge->bridge);
> +
> + return num_modes;
> +}
> +
> +static struct drm_encoder *ps8640_best_encoder(struct drm_connector *connector)
> +{
> + struct ps8640 *ps_bridge = connector_to_ps8640(connector);
> +
> + return ps_bridge->bridge.encoder;
> +}
> +
> +static const struct drm_connector_helper_funcs ps8640_connector_helper_funcs = {
> + .get_modes = ps8640_get_modes,
> + .best_encoder = ps8640_best_encoder,
> +};
> +
> +static enum drm_connector_status ps8640_detect(struct drm_connector *connector,
> + bool force)
> +{
> + return connector_status_connected;
> +}
> +
> +static const struct drm_connector_funcs ps8640_connector_funcs = {
> + .dpms = drm_atomic_helper_connector_dpms,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .detect = ps8640_detect,
> + .reset = drm_atomic_helper_connector_reset,
> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +int ps8640_bridge_attach(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + struct device *dev = &ps_bridge->page[0]->dev;
> + struct device_node *port, *in_ep;
> + struct device_node *dsi_node = NULL;
> + struct mipi_dsi_host *host = NULL;
> + int ret;
> +
> + ret = drm_connector_init(bridge->dev, &ps_bridge->connector,
> + &ps8640_connector_funcs,
> + DRM_MODE_CONNECTOR_eDP);
> +
> + if (ret) {
> + DRM_ERROR("Failed to initialize connector with drm: %d\n", ret);
> + return ret;
> + }
> +
> + drm_connector_helper_add(&ps_bridge->connector,
> + &ps8640_connector_helper_funcs);
> +
> + ps_bridge->connector.dpms = DRM_MODE_DPMS_ON;
> + drm_mode_connector_attach_encoder(&ps_bridge->connector,
> + bridge->encoder);
> +
> + if (ps_bridge->panel)
> + drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
> +
> + /* port at 0 is ps8640 dsi input port */
> + port = of_graph_get_port_by_id(dev->of_node, 0);
> + if (port) {
> + in_ep = of_get_child_by_name(port, "endpoint");
> + of_node_put(port);
> + if (in_ep) {
> + dsi_node = of_graph_get_remote_port_parent(in_ep);
> + of_node_put(in_ep);
> + }
> + }
> + if (dsi_node) {
> + host = of_find_mipi_dsi_host_by_node(dsi_node);
> + of_node_put(dsi_node);
> + if (!host) {
> + ret = -ENODEV;
> + goto err;
> + }
> + }
> +
> + ps_bridge->dsi.host = host;
> + ps_bridge->dsi.mode_flags = MIPI_DSI_MODE_VIDEO |
> + MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
> + ps_bridge->dsi.format = MIPI_DSI_FMT_RGB888;
> + ps_bridge->dsi.lanes = 4;
> + ret = mipi_dsi_attach(&ps_bridge->dsi);
> + if (ret)
> + goto err;
> +
> + return 0;
> +err:
> + if (ps_bridge->panel)
> + drm_panel_detach(ps_bridge->panel);
> + drm_connector_cleanup(&ps_bridge->connector);
> + return ret;
> +}
> +
> +static const struct drm_bridge_funcs ps8640_bridge_funcs = {
> + .attach = ps8640_bridge_attach,
> + .disable = ps8640_disable,
> + .post_disable = ps8640_post_disable,
> + .pre_enable = ps8640_pre_enable,
> + .enable = ps8640_enable,
> +};
> +
> +/* Firmware Version is returned as Major.Minor */
> +static ssize_t ps8640_fw_version_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct ps8640 *ps_bridge = dev_get_drvdata(dev);
> + struct ps8640_info *info = &ps_bridge->info;
> +
> + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", info->version >> 8,
> + info->version & 0xff);
> +}
> +
> +/* Hardware Version is returned as FamilyID.VariantID */
> +static ssize_t ps8640_hw_version_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct ps8640 *ps_bridge = dev_get_drvdata(dev);
> + struct ps8640_info *info = &ps_bridge->info;
> +
> + return scnprintf(buf, PAGE_SIZE, "ps%u.%u\n", info->family_id,
> + info->variant_id);
> +}
> +
> +static int ps8640_spi_send_cmd(struct ps8640 *ps_bridge, u8 *cmd, u8 cmd_len)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + u8 i, buf[3] = { PAGE2_SWSPI_LEN, cmd_len - 1, TRIGGER_NO_READBACK };
> + int ret;
> +
> + ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
> + if (ret)
> + goto err;
> +
> + /* write command in write port */
> + for (i = 0; i < cmd_len; i++) {
> + ret = ps8640_write_byte(client, PAGE2_SWSPI_WDATA, cmd[i]);
> + if (ret)
> + goto err_irom_disable;
> + }
> +
> + ret = ps8640_write_bytes(client, buf, sizeof(buf));
> + if (ret)
> + goto err_irom_disable;
> +
> + ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> + if (ret)
> + goto err;
> +
> + return 0;
> +err_irom_disable:
> + ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> +err:
> + dev_err(&client->dev, "send command err: %d\n", ret);
> + return ret;
> +}
> +
> +static int ps8640_wait_spi_ready(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + u8 spi_rdy_st;
> + ktime_t timeout;
> +
> + timeout = ktime_add_ms(ktime_get(), 200);
> + for (;;) {
> + ps8640_read(client, PAGE2_SPI_STATUS, &spi_rdy_st, 1);
> + if ((spi_rdy_st & SPI_READY) != SPI_READY)
> + break;
> +
> + if (ktime_compare(ktime_get(), timeout) > 0) {
> + dev_err(&client->dev, "wait spi ready timeout\n");
> + return -EBUSY;
> + }
> +
> + msleep(20);
> + }
> +
> + return 0;
> +}
> +
> +static int ps8640_wait_spi_nobusy(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + u8 spi_status, buf[3] = { PAGE2_SWSPI_LEN, 0, TRIGGER_READBACK };
> + int ret;
> + ktime_t timeout;
> +
> + timeout = ktime_add_ms(ktime_get(), 500);
> + for (;;) {
> + /* 0x05 RDSR; Read-Status-Register */
> + ret = ps8640_write_byte(client, PAGE2_SWSPI_WDATA,
> + READ_STATUS_REG_CMD);
> + if (ret)
> + goto err_send_cmd_exit;
> +
> + ret = ps8640_write_bytes(client, buf, 3);
> + if (ret)
> + goto err_send_cmd_exit;
> +
> + /* delay for cmd send */
> + usleep_range(300, 500);
> + /* wait for SPI ROM until not busy */
> + ret = ps8640_read(client, PAGE2_SWSPI_RDATA, &spi_status, 1);
> + if (ret)
> + goto err_send_cmd_exit;
> +
> + if (!(spi_status & BUSY))
> + break;
> +
> + if (ktime_compare(ktime_get(), timeout) > 0) {
> + dev_err(&client->dev, "wait spi no busy timeout: %d\n",
> + ret);
> + return -EBUSY;
> + }
> + }
> +
> + return 0;
> +
> +err_send_cmd_exit:
> + dev_err(&client->dev, "send command err: %d\n", ret);
> + return ret;
> +}
> +
> +static int ps8640_wait_rom_idle(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[0];
> + int ret;
> +
> + ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
> + if (ret)
> + goto exit;
> +
> + ret = ps8640_wait_spi_ready(ps_bridge);
> + if (ret)
> + goto err_spi;
> +
> + ret = ps8640_wait_spi_nobusy(ps_bridge);
> + if (ret)
> + goto err_spi;
> +
> + ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> + if (ret)
> + goto exit;
> +
> + return 0;
> +
> +err_spi:
> + ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> +exit:
> + dev_err(&client->dev, "wait ps8640 rom idle fail: %d\n", ret);
> +
> + return ret;
> +}
> +
> +static int ps8640_spi_dl_mode(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + int ret;
> +
> + /* switch ps8640 mode to spi dl mode */
> + if (ps_bridge->gpio_mode_sel)
> + gpiod_set_value(ps_bridge->gpio_mode_sel, 0);
> +
> + /* reset spi interface */
> + ret = ps8640_write_byte(client, PAGE2_SW_RESET,
> + SPI_SW_RESET | MPU_SW_RESET);
> + if (ret)
> + goto exit;
> +
> + ret = ps8640_write_byte(client, PAGE2_SW_RESET, MPU_SW_RESET);
> + if (ret)
> + goto exit;
> +
> + return 0;
> +
> +exit:
> + dev_err(&client->dev, "fail reset spi interface: %d\n", ret);
> +
> + return ret;
> +}
> +
> +static int ps8640_rom_prepare(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + struct device *dev = &client->dev;
> + u8 i, cmd[2];
> + int ret;
> +
> + cmd[0] = WRITE_ENABLE_CMD;
> + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1);
> + if (ret) {
> + dev_err(dev, "failed enable-write-status-register: %d\n", ret);
> + return ret;
> + }
> +
> + cmd[0] = WRITE_STATUS_REG_CMD;
> + cmd[1] = CLEAR_ALL_PROTECT;
> + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 2);
> + if (ret) {
> + dev_err(dev, "fail disable all protection: %d\n", ret);
> + return ret;
> + }
> +
> + /* wait for SPI module ready */
> + ret = ps8640_wait_rom_idle(ps_bridge);
> + if (ret) {
> + dev_err(dev, "fail wait rom idle: %d\n", ret);
> + return ret;
> + }
> +
> + ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
> + for (i = 0; i < ARRAY_SIZE(enc_ctrl_code); i++)
> + ps8640_write_byte(client, PAGE2_ENCTLSPI_WR, enc_ctrl_code[i]);
> + ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> +
> + /* Enable-Write-Status-Register */
> + cmd[0] = WRITE_ENABLE_CMD;
> + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1);
> + if (ret) {
> + dev_err(dev, "fail enable-write-status-register: %d\n", ret);
> + return ret;
> + }
> +
> + /* chip erase command */
> + cmd[0] = CHIP_ERASE_CMD;
> + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1);
> + if (ret) {
> + dev_err(dev, "fail disable all protection: %d\n", ret);
> + return ret;
> + }
> +
> + ret = ps8640_wait_rom_idle(ps_bridge);
> + if (ret) {
> + dev_err(dev, "fail wait rom idle: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ps8640_check_chip_id(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[4];
> + u8 buf[4];
> +
> + ps8640_read(client, PAGE4_REV_L, buf, 4);
> + return memcmp(buf, hw_chip_id, sizeof(buf));
> +}
> +
> +static int ps8640_validate_firmware(struct ps8640 *ps_bridge,
> + const struct firmware *fw)
> +{
> + struct i2c_client *client = ps_bridge->page[0];
> + u16 fw_chip_id;
> +
> + /*
> + * Get the chip_id from the firmware. Make sure that it is the
> + * right controller to do the firmware and config update.
> + */
> + fw_chip_id = get_unaligned_le16(fw->data + FW_CHIP_ID_OFFSET);
> +
> + if (fw_chip_id != 0x8640 && ps8640_check_chip_id(ps_bridge) == 0) {
> + dev_err(&client->dev,
> + "chip id mismatch: fw 0x%x vs. chip 0x8640\n",
> + fw_chip_id);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int ps8640_write_rom(struct ps8640 *ps_bridge, const struct firmware *fw)
> +{
> + struct i2c_client *client = ps_bridge->page[0];
> + struct device *dev = &client->dev;
> + struct i2c_client *client2 = ps_bridge->page[2];
> + struct i2c_client *client7 = ps_bridge->page[7];
> + size_t pos, cpy_len;
> + u8 buf[257];
> + int ret;
> +
> + ps8640_write_byte(client2, PAGE2_SPI_CFG3, I2C_TO_SPI_RESET);
> + msleep(100);
> + ps8640_write_byte(client2, PAGE2_SPI_CFG3, 0x00);
> +
> + for (pos = 0; pos < fw->size; pos += cpy_len) {
> + buf[0] = PAGE2_ROMADD_BYTE1;
> + buf[1] = pos >> 8;
> + buf[2] = pos >> 16;
> + ret = ps8640_write_bytes(client2, buf, 3);
> + if (ret)
> + goto error;
> + cpy_len = fw->size >= 256 + pos ? 256 : fw->size - pos;
> + buf[0] = 0;
> + memcpy(buf + 1, fw->data + pos, cpy_len);
> + ret = ps8640_write_bytes(client7, buf, cpy_len + 1);
> + if (ret)
> + goto error;
> +
> + dev_dbg(dev, "fw update completed %zu / %zu bytes\n", pos,
> + fw->size);
> + }
> + return 0;
> +
> +error:
> + dev_err(dev, "failed write external flash, %d\n", ret);
> + return ret;
> +}
> +
> +static int ps8640_spi_normal_mode(struct ps8640 *ps_bridge)
> +{
> + u8 cmd[2];
> + struct i2c_client *client = ps_bridge->page[2];
> +
> + /* Enable-Write-Status-Register */
> + cmd[0] = WRITE_ENABLE_CMD;
> + ps8640_spi_send_cmd(ps_bridge, cmd, 1);
> +
> + /* protect BPL/BP0/BP1 */
> + cmd[0] = WRITE_STATUS_REG_CMD;
> + cmd[1] = BLK_PROTECT_BITS | STATUS_REG_PROTECT;
> + ps8640_spi_send_cmd(ps_bridge, cmd, 2);
> +
> + /* wait for SPI rom ready */
> + ps8640_wait_rom_idle(ps_bridge);
> +
> + /* disable PS8640 mapping function */
> + ps8640_write_byte(client, PAGE2_ENCTLSPI_WR, 0x00);
> +
> + if (ps_bridge->gpio_mode_sel)
> + gpiod_set_value(ps_bridge->gpio_mode_sel, 1);
> + return 0;
> +}
> +
> +static int ps8640_enter_bl(struct ps8640 *ps_bridge)
> +{
> + ps_bridge->in_fw_update = true;
> + return ps8640_spi_dl_mode(ps_bridge);
> +}
> +
> +static void ps8640_exit_bl(struct ps8640 *ps_bridge, const struct firmware *fw)
> +{
> + ps8640_spi_normal_mode(ps_bridge);
> + ps_bridge->in_fw_update = false;
> +}
> +
> +static int ps8640_load_fw(struct ps8640 *ps_bridge, const struct firmware *fw)
> +{
> + struct i2c_client *client = ps_bridge->page[0];
> + struct device *dev = &client->dev;
> + int ret;
> + bool ps8640_status_backup = ps_bridge->enabled;
> +
> + ret = ps8640_validate_firmware(ps_bridge, fw);
> + if (ret)
> + return ret;
> +
> + mutex_lock(&ps_bridge->fw_mutex);
> + if (!ps_bridge->in_fw_update) {
> + if (!ps8640_status_backup)
> + ps8640_pre_enable(&ps_bridge->bridge);
> +
> + ret = ps8640_enter_bl(ps_bridge);
> + if (ret)
> + goto exit;
> + }
> +
> + ret = ps8640_rom_prepare(ps_bridge);
> + if (ret)
> + goto exit;
> +
> + ret = ps8640_write_rom(ps_bridge, fw);
> +
> +exit:
> + if (ret)
> + dev_err(dev, "Failed to load firmware, %d\n", ret);
> +
> + ps8640_exit_bl(ps_bridge, fw);
> + if (!ps8640_status_backup)
> + ps8640_post_disable(&ps_bridge->bridge);
> + mutex_unlock(&ps_bridge->fw_mutex);
> + return ret;
> +}
> +
> +static ssize_t ps8640_update_fw_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct ps8640 *ps_bridge = i2c_get_clientdata(client);
> + const struct firmware *fw;
> + int error;
> +
> + error = request_firmware(&fw, PS_FW_NAME, dev);
> + if (error) {
> + dev_err(dev, "Unable to open firmware %s: %d\n",
> + PS_FW_NAME, error);
> + return error;
> + }
> +
> + error = ps8640_load_fw(ps_bridge, fw);
> + if (error)
> + dev_err(dev, "The firmware update failed(%d)\n", error);
> + else
> + dev_info(dev, "The firmware update succeeded\n");
> +
> + release_firmware(fw);
> + return error ? error : count;
> +}
> +
> +static DEVICE_ATTR(fw_version, S_IRUGO, ps8640_fw_version_show, NULL);
> +static DEVICE_ATTR(hw_version, S_IRUGO, ps8640_hw_version_show, NULL);
> +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, ps8640_update_fw_store);
> +
> +static struct attribute *ps8640_attrs[] = {
> + &dev_attr_fw_version.attr,
> + &dev_attr_hw_version.attr,
> + &dev_attr_update_fw.attr,
> + NULL
> +};
> +
> +static const struct attribute_group ps8640_attr_group = {
> + .attrs = ps8640_attrs,
> +};
> +
> +static void ps8640_remove_sysfs_group(void *data)
> +{
> + struct ps8640 *ps_bridge = data;
> +
> + sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group);
> +}
> +
> +static int ps8640_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct device *dev = &client->dev;
> + struct ps8640 *ps_bridge;
> + struct device_node *np = dev->of_node;
> + struct device_node *port, *out_ep;
> + struct device_node *panel_node = NULL;
> + int ret;
> + u32 i;
> +
> + ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
> + if (!ps_bridge)
> + return -ENOMEM;
> +
> + /* port at 1 is ps8640 output port */
> + port = of_graph_get_port_by_id(np, 1);
> + if (port) {
> + out_ep = of_get_child_by_name(port, "endpoint");
> + of_node_put(port);
> + if (out_ep) {
> + panel_node = of_graph_get_remote_port_parent(out_ep);
> + of_node_put(out_ep);
> + }
> + }
> + if (panel_node) {
> + ps_bridge->panel = of_drm_find_panel(panel_node);
> + of_node_put(panel_node);
> + if (!ps_bridge->panel)
> + return -EPROBE_DEFER;
> + }
> +
> + mutex_init(&ps_bridge->fw_mutex);
> + ps_bridge->supplies[0].supply = "vdd33";
> + ps_bridge->supplies[1].supply = "vdd12";
> + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ps_bridge->supplies),
> + ps_bridge->supplies);
> + if (ret) {
> + dev_info(dev, "failed to get regulators: %d\n", ret);
> + return ret;
> + }
> +
> + ps_bridge->gpio_mode_sel = devm_gpiod_get_optional(&client->dev,
> + "mode-sel",
> + GPIOD_OUT_HIGH);
> + if (IS_ERR(ps_bridge->gpio_mode_sel)) {
> + ret = PTR_ERR(ps_bridge->gpio_mode_sel);
> + dev_err(dev, "cannot get mode-sel %d\n", ret);
> + return ret;
> + }
> +
> + ps_bridge->gpio_power_down = devm_gpiod_get(&client->dev, "sleep",
> + GPIOD_OUT_LOW);
> + if (IS_ERR(ps_bridge->gpio_power_down)) {
> + ret = PTR_ERR(ps_bridge->gpio_power_down);
> + dev_err(dev, "cannot get sleep: %d\n", ret);
> + return ret;
> + }
> +
> + /*
> + * Request the reset pin low to avoid the bridge being
> + * initialized prematurely
> + */
> + ps_bridge->gpio_reset = devm_gpiod_get(&client->dev, "reset",
> + GPIOD_OUT_LOW);
> + if (IS_ERR(ps_bridge->gpio_reset)) {
> + ret = PTR_ERR(ps_bridge->gpio_reset);
> + dev_err(dev, "cannot get reset: %d\n", ret);
> + return ret;
> + }
> +
> + ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
> + ps_bridge->bridge.of_node = dev->of_node;
> +
> + ps_bridge->page[0] = client;
> + ps_bridge->ddc_i2c = i2c_new_dummy(client->adapter, EDID_I2C_ADDR);
> + if (!ps_bridge->ddc_i2c) {
> + dev_err(dev, "failed ddc_i2c dummy device, address%02x\n",
> + EDID_I2C_ADDR);
> + return -EBUSY;
> + }
> + /*
> + * ps8640 uses multiple addresses, use dummy devices for them
> + * page[0]: for DP control
> + * page[1]: for VIDEO Bridge
> + * page[2]: for control top
> + * page[3]: for DSI Link Control1
> + * page[4]: for MIPI Phy
> + * page[5]: for VPLL
> + * page[6]: for DSI Link Control2
> + * page[7]: for spi rom mapping
> + */
> + for (i = 1; i < MAX_DEVS; i++) {
> + ps_bridge->page[i] = i2c_new_dummy(client->adapter,
> + client->addr + i);
> + if (!ps_bridge->page[i]) {
> + dev_err(dev, "failed i2c dummy device, address%02x\n",
> + client->addr + i);
> + ret = -EBUSY;
> + goto exit_dummy;
> + }
> + }
> + i2c_set_clientdata(client, ps_bridge);
> +
> + ret = sysfs_create_group(&client->dev.kobj, &ps8640_attr_group);
> + if (ret) {
> + dev_err(dev, "failed to create sysfs entries: %d\n", ret);
> + goto exit_dummy;
> + }
> +
> + ret = devm_add_action(dev, ps8640_remove_sysfs_group, ps_bridge);
> + if (ret) {
> + dev_err(dev, "failed to add sysfs cleanup action: %d\n", ret);
> + goto exit_remove_sysfs;
> + }
> +
> + ret = drm_bridge_add(&ps_bridge->bridge);
> + if (ret) {
> + dev_err(dev, "Failed to add bridge: %d\n", ret);
> + goto exit_remove_sysfs;
> + }
> + return 0;
> +
> +exit_remove_sysfs:
> + sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group);
> +exit_dummy:
> + while (--i)
> + i2c_unregister_device(ps_bridge->page[i]);
> + i2c_unregister_device(ps_bridge->ddc_i2c);
> + return ret;
> +}
> +
> +static int ps8640_remove(struct i2c_client *client)
> +{
> + struct ps8640 *ps_bridge = i2c_get_clientdata(client);
> + int i = MAX_DEVS;
> +
> + drm_bridge_remove(&ps_bridge->bridge);
> + sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group);
> + while (--i)
> + i2c_unregister_device(ps_bridge->page[i]);
> +
> + i2c_unregister_device(ps_bridge->ddc_i2c);
> + return 0;
> +}
> +
> +static const struct i2c_device_id ps8640_i2c_table[] = {
> + { "parade,ps8640", 0 },
> + {},
> +};
> +MODULE_DEVICE_TABLE(i2c, ps8640_i2c_table);
> +
> +static const struct of_device_id ps8640_match[] = {
> + { .compatible = "parade,ps8640" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, ps8640_match);
> +
> +static struct i2c_driver ps8640_driver = {
> + .id_table = ps8640_i2c_table,
> + .probe = ps8640_probe,
> + .remove = ps8640_remove,
> + .driver = {
> + .name = "parade,ps8640",
> + .of_match_table = ps8640_match,
> + },
> +};
> +module_i2c_driver(ps8640_driver);
> +
> +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> +MODULE_AUTHOR("CK Hu <ck.hu@mediatek.com>");
> +MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.9.5
>
>
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek
^ 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