* [PATCH] usb: chipidea: Properly mark little endian descriptors
From: Peter Chen @ 2016-09-14 2:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160913230631.6727-1-stephen.boyd@linaro.org>
On Tue, Sep 13, 2016 at 04:06:31PM -0700, Stephen Boyd wrote:
> The DMA descriptors are little endian, and we do a pretty good
> job of handling them with the proper le32_to_cpu() markings, but
> we don't actually mark them as __le32. This means checkers like
> sparse can't easily find new bugs. Let's mark the members of
> structures properly and fix the few places where we're missing
> conversions.
>
> Cc: Peter Chen <peter.chen@nxp.com>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
> ---
> drivers/usb/chipidea/udc.c | 6 +++---
> drivers/usb/chipidea/udc.h | 12 ++++++------
> 2 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
> index 6acf4dba395e..61a65d1d05f2 100644
> --- a/drivers/usb/chipidea/udc.c
> +++ b/drivers/usb/chipidea/udc.c
> @@ -364,7 +364,7 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
> if (hwreq->req.length == 0
> || hwreq->req.length % hwep->ep.maxpacket)
> mul++;
> - node->ptr->token |= mul << __ffs(TD_MULTO);
> + node->ptr->token |= cpu_to_le32(mul << __ffs(TD_MULTO));
> }
>
> temp = (u32) (hwreq->req.dma + hwreq->req.actual);
> @@ -503,7 +503,7 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
> if (hwreq->req.length == 0
> || hwreq->req.length % hwep->ep.maxpacket)
> mul++;
> - hwep->qh.ptr->cap |= mul << __ffs(QH_MULT);
> + hwep->qh.ptr->cap |= cpu_to_le32(mul << __ffs(QH_MULT));
> }
>
> wmb(); /* synchronize before ep prime */
> @@ -530,7 +530,7 @@ static void free_pending_td(struct ci_hw_ep *hwep)
> static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep,
> struct td_node *node)
> {
> - hwep->qh.ptr->td.next = node->dma;
> + hwep->qh.ptr->td.next = cpu_to_le32(node->dma);
> hwep->qh.ptr->td.token &=
> cpu_to_le32(~(TD_STATUS_HALTED | TD_STATUS_ACTIVE));
>
> diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h
> index e66df0020bd4..2ecd1174d66c 100644
> --- a/drivers/usb/chipidea/udc.h
> +++ b/drivers/usb/chipidea/udc.h
> @@ -22,11 +22,11 @@
> /* DMA layout of transfer descriptors */
> struct ci_hw_td {
> /* 0 */
> - u32 next;
> + __le32 next;
> #define TD_TERMINATE BIT(0)
> #define TD_ADDR_MASK (0xFFFFFFEUL << 5)
> /* 1 */
> - u32 token;
> + __le32 token;
> #define TD_STATUS (0x00FFUL << 0)
> #define TD_STATUS_TR_ERR BIT(3)
> #define TD_STATUS_DT_ERR BIT(5)
> @@ -36,7 +36,7 @@ struct ci_hw_td {
> #define TD_IOC BIT(15)
> #define TD_TOTAL_BYTES (0x7FFFUL << 16)
> /* 2 */
> - u32 page[5];
> + __le32 page[5];
> #define TD_CURR_OFFSET (0x0FFFUL << 0)
> #define TD_FRAME_NUM (0x07FFUL << 0)
> #define TD_RESERVED_MASK (0x0FFFUL << 0)
> @@ -45,18 +45,18 @@ struct ci_hw_td {
> /* DMA layout of queue heads */
> struct ci_hw_qh {
> /* 0 */
> - u32 cap;
> + __le32 cap;
> #define QH_IOS BIT(15)
> #define QH_MAX_PKT (0x07FFUL << 16)
> #define QH_ZLT BIT(29)
> #define QH_MULT (0x0003UL << 30)
> #define QH_ISO_MULT(x) ((x >> 11) & 0x03)
> /* 1 */
> - u32 curr;
> + __le32 curr;
> /* 2 - 8 */
> struct ci_hw_td td;
> /* 9 */
> - u32 RESERVED;
> + __le32 RESERVED;
> struct usb_ctrlrequest setup;
> } __attribute__ ((packed, aligned(4)));
>
> --
Good catch, thanks.
--
Best Regards,
Peter Chen
^ permalink raw reply
* [PATCH v4 22/22] phy: Add support for Qualcomm's USB HS phy
From: Peter Chen @ 2016-09-14 2:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <147379930484.27460.3849356366746983941@sboyd-linaro>
On Tue, Sep 13, 2016 at 01:41:44PM -0700, Stephen Boyd wrote:
> Quoting Peter Chen (2016-09-13 00:03:58)
> > On Wed, Sep 07, 2016 at 02:35:19PM -0700, Stephen Boyd wrote:
> > > The high-speed phy on qcom SoCs is controlled via the ULPI
> > > viewport.
> > >
> >
> > Hi Stephen, I am a little puzzled how this driver co-work with chipidea
> > driver. According to nxp IC guys, the ULPI PHY's clock needs to be enabled
> > before access portsc.pts (calling hw_phymode_configure), otherwise,
> > the system will hang. But I find you call hw_phymode_configure before
> > phy->power_on, doesn't your design have this requirement?
>
> Which clk needs to be enabled? The xcvr_clk? I believe that clk
> corresponds to the "core" clk that we enable in the msm glue driver
> layer. When that clk is enabled, the ULPI phy is able to respond to
> register read/writes via the ULPI viewport.
>
The input clock for ULPI PHY, maybe it is ref_clk at this PHY driver,
so in your platform, even PHY clock is gated, you can still access
portsc.pts to configure PHY mode at controller register?
> >
> > Besides, you read ulpi id before phy->power_on, how can read work before
> > phy power on?
> >
>
> I've found that even having the link clk enabled before phy->power_on
> doesn't mean it's possible to read the id registers though. That's
> because there can be other power supplies, like regulators, which need
> to be on for the phy to operate properly.
>
Then I am puzzled the current initialization for your case, in my mind,
it should like below:
qcom_usb_hs_phy_probe->qcom_usb_hs_phy_power_on->ci_ulpi_init
Like other PHYs, it should get PHY first, then power on it, after that,
you can access its register.
--
Best Regards,
Peter Chen
^ permalink raw reply
* [RFC/PATCH] usb: misc: Add a driver for TC7USB40MU
From: Stephen Boyd @ 2016-09-14 1:42 UTC (permalink / raw)
To: linux-arm-kernel
On the db410c 96boards platform we have a TC7USB40MU[1] on the
board to mux the D+/D- lines from the SoC between a micro usb
"device" port and a USB hub for "host" roles. Upon a role switch,
we need to change this mux to forward the D+/D- lines to either
the port or the hub. Therefore, introduce a driver for this
device that intercepts extcon USB_HOST events and logically
asserts a gpio to mux the "host" D+/D- lines when a host cable is
attached. When the cable goes away, it will logically deassert
the gpio and mux the "device" lines.
[1] https://toshiba.semicon-storage.com/ap-en/product/logic/bus-switch/detail.TC7USB40MU.html
Cc: MyungJoo Ham <myungjoo.ham@samsung.com>
Cc: Chanwoo Choi <cw00.choi@samsung.com>
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
Should I make the extcon part optional? I could see a case where there are two
"OTG" ports connected to the mux (or two hubs), and for some reason the
software may want to mux between them at runtime. If we mandate an extcon,
that won't be possible to support. Perhaps it would be better to have
the node, but connect it to the usb controller with a phandle (maybe of_graph
endpoints would be useful too) so that when the controller wants to mux over
a port it can do so.
Muxing the ports this way based on ID cable is pretty much a software
design decision. We could mux the ports during the role switch, and the
role switch can be entirely userspace driven with the chipidea controller
that I'm using (see the role switching support in the "role" file for
debugfs support in that driver). So extcon cables don't come into the picture
in that scenario.
.../devicetree/bindings/usb/toshiba,tc7usb40mu.txt | 34 +++++++
drivers/usb/misc/Kconfig | 9 ++
drivers/usb/misc/Makefile | 1 +
drivers/usb/misc/tc7usb40mu.c | 107 +++++++++++++++++++++
4 files changed, 151 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/toshiba,tc7usb40mu.txt
create mode 100644 drivers/usb/misc/tc7usb40mu.c
diff --git a/Documentation/devicetree/bindings/usb/toshiba,tc7usb40mu.txt b/Documentation/devicetree/bindings/usb/toshiba,tc7usb40mu.txt
new file mode 100644
index 000000000000..18e6607408fa
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/toshiba,tc7usb40mu.txt
@@ -0,0 +1,34 @@
+Toshiba TC7USB40MU
+
+This device muxes USB D+/D- lines between two outputs called 1D+/1D- and 2D+/2D-.
+When the switch pin is asserted, we mux out 2D+/2D-, and when it's deasserted we
+select 1D+/1D-.
+
+This can be used to mux USB D+/D- lines between a USB hub and an OTG port.
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should contain "toshiba,tc7usb40mu"
+
+- switch-gpios:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain the gpio used to toggle the switch. Logically
+ asserting the gpio will cause the device to mux the "host"
+ D+/D- lines instead of the "device" lines.
+
+- extcon:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain the extcon device for USB_HOST cable events
+
+Example:
+
+ usb-switch {
+ compatible = "toshiba,tc7usb40mu";
+ switch-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>;
+ extcon = <&usb_id>;
+ };
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 47b357760afc..3da568c751d2 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -46,6 +46,15 @@ config USB_SEVSEG
To compile this driver as a module, choose M here: the
module will be called usbsevseg.
+config USB_TC7USB40MU
+ tristate "TC7USB40MU USB mux support"
+ depends on (GPIOLIB && EXTCON) || COMPILE_TEST
+ help
+ Say Y here if you have a TC7USB40MU by Toshiba. If a USB ID cable is
+ present, a gpio will be asserted to mux out "host" D+/D- lines and when
+ the ID cable is removed, a gpio will be deasserted to mux out "device"
+ D+/D- lines.
+
config USB_RIO500
tristate "USB Diamond Rio500 support"
help
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 3d1992750da4..d8f9ad1dee13 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_EHSET_TEST_FIXTURE) += ehset.o
+obj-$(CONFIG_USB_TC7USB40MU) += tc7usb40mu.o
obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o
obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o
diff --git a/drivers/usb/misc/tc7usb40mu.c b/drivers/usb/misc/tc7usb40mu.c
new file mode 100644
index 000000000000..9edcfe577ae4
--- /dev/null
+++ b/drivers/usb/misc/tc7usb40mu.c
@@ -0,0 +1,107 @@
+/**
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * 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/platform_device.h>
+#include <linux/module.h>
+#include <linux/extcon.h>
+#include <linux/gpio/consumer.h>
+#include <linux/notifier.h>
+
+struct tc7usb40mu_drv {
+ struct gpio_desc *gpio;
+ struct extcon_dev *edev;
+ struct notifier_block notify;
+};
+
+static int tc7usb40mu_notify(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct tc7usb40mu_drv *drv;
+
+ drv = container_of(nb, struct tc7usb40mu_drv, notify);
+ if (event)
+ gpiod_set_value_cansleep(drv->gpio, 1); /* USB HUB */
+ else
+ gpiod_set_value_cansleep(drv->gpio, 0); /* device connector */
+
+ return NOTIFY_OK;
+}
+
+static int tc7usb40mu_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tc7usb40mu_drv *drv;
+ int state, ret;
+ enum gpiod_flags flags;
+
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ drv->edev = extcon_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(drv->edev))
+ return PTR_ERR(drv->edev);
+
+ /*
+ * TODO: This can race with extcon changing state before we request the
+ * gpio or the extcon changing state before we register the notifier
+ */
+ state = extcon_get_cable_state_(drv->edev, EXTCON_USB_HOST);
+ if (state)
+ flags = GPIOD_OUT_HIGH;
+ else
+ flags = GPIOD_OUT_LOW;
+
+ drv->gpio = devm_gpiod_get(dev, "switch", flags);
+ if (IS_ERR(drv->gpio))
+ return PTR_ERR(drv->gpio);
+
+ drv->notify.notifier_call = tc7usb40mu_notify;
+ ret = extcon_register_notifier(drv->edev, EXTCON_USB_HOST, &drv->notify);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, drv);
+
+ return 0;
+}
+
+static int tc7usb40mu_remove(struct platform_device *pdev)
+{
+ struct tc7usb40mu_drv *drv;
+
+ drv = platform_get_drvdata(pdev);
+ extcon_unregister_notifier(drv->edev, EXTCON_USB_HOST, &drv->notify);
+
+ return 0;
+}
+
+static const struct of_device_id tc7usb40mu_dt_match[] = {
+ { .compatible = "toshiba,tc7usb40mu", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tc7usb40mu_dt_match);
+
+static struct platform_driver tc7usb40mu_driver = {
+ .probe = tc7usb40mu_probe,
+ .remove = tc7usb40mu_remove,
+ .driver = {
+ .name = "tc7usb40mu",
+ .of_match_table = tc7usb40mu_dt_match,
+ },
+};
+module_platform_driver(tc7usb40mu_driver);
+
+MODULE_AUTHOR("Stephen Boyd <stephen.boyd@linaro.org>");
+MODULE_DESCRIPTION("TC7USB40MU USB multiplexer driver");
+MODULE_LICENSE("GPL");
--
2.9.0.rc2.8.ga28705d
^ permalink raw reply related
* [PATCH] ARM: dts: Add power button support for igepv5
From: Javier Martinez Canillas @ 2016-09-14 1:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909210729.20184-1-tony@atomide.com>
Hello Tony,
On 09/09/2016 05:07 PM, Tony Lindgren wrote:
> Add power button support for igepv5.
>
> Cc: Agust? Fontquerni i Gorchs <afontquerni@iseebcn.com>
> Cc: Enric Balletbo Serra <eballetbo@gmail.com>
> Cc: Javier Martinez Canillas <javier@osg.samsung.com>
> Cc: Pau Pajuel <ppajuel@gmail.com>
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> ---
I don't have a schematics for this board, but the patch looks good to me.
Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com>
Best regards,
--
Javier Martinez Canillas
Open Source Group
Samsung Research America
^ permalink raw reply
* [PATCH v5 0/3] arm64: hibernate: Resume when hibernate image created on non-boot CPU
From: Rafael J. Wysocki @ 2016-09-14 1:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1471438227-8747-1-git-send-email-james.morse@arm.com>
On Wednesday, August 17, 2016 01:50:24 PM James Morse wrote:
> Hi all,
>
> These patches allow arm64 to hibernate on any CPU saving the cpu-id in the
> arch header, then switch to it during resume using
> hibernate_resume_nonboot_cpu_disable().
>
> I hoped to avoid patch 1 by duplicating the logic in the arch code, but
> Lorenzo pointed out using cpu_down() indicates that tasks are not frozen,
> whereas in reality they are. [0]
> Patch 1 lets us specify which cpu disable_nonboot_cpus() should leave
> standing.
>
> (All three patches need to be merged together, this series doesn't conflict
> with [1].)
>
> Comments welcome,
>
> This series is based on v4.8-rc2 and can be retrieved from:
> git://linux-arm.org/linux-jm.git hibernate/cpuN/v5
>
> Changes since v4:
> * Added freeze_secondary_cpus().
> * Wired up hibernate_resume_nonboot_cpu_disable(), removing macros and
> kconfig symbols from previous approaches.
> * Added check for sleep_cpu being uninitialised when we come to save the
> arch header.
>
> Changes since v3:
> * Split series, at the PM/Hibernate patch, merged arm64 patches to remove
> dependencies.
> * Changed Kconfig symbol name.
> * Fixed logic error '<= 0' when testing for an unrecognised CPU.
>
> Changes since v2:
> * Split wrong-CPU logic into an earlier patch that just returns an error.
> * Changed core code patch to use macros instead of a weak function.
> CONFIG_ARCH_HIBERNATION_HEADER now implies an asm/suspend.h header.
> * Wording in error messages 'hibernate' not 'suspend'.
>
> Changes since v1:
> * Fixed 'Brining' typo.
>
>
>
> [v1] http://www.spinics.net/lists/arm-kernel/msg507805.html
> [v2] https://www.spinics.net/lists/arm-kernel/msg511654.html
> [v3] https://www.spinics.net/lists/arm-kernel/msg514644.html
> [v4] https://www.spinics.net/lists/arm-kernel/msg515978.html
>
> [0] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-July/441305.html
> [1] https://www.spinics.net/lists/arm-kernel/msg523933.html
> [2] hotplug cpu0, kexec, hibernate, resume
> -------------------------%<-------------------------
> root at ubuntu:~# echo disk > /sys/power/state
> [ 76.768682] PM: Syncing filesystems ... done.
> [ 76.774872] Freezing user space processes ... (elapsed 0.001 seconds) done.
> [ 76.783253] Double checking all user space processes after OOM killer disable
> ... (elapsed 0.000 seconds)
> [ 76.794214] PM: Preallocating image memory... done (allocated 84746 pages)
> [ 80.454878] PM: Allocated 338984 kbytes in 3.65 seconds (92.87 MB/s)
> [ 80.461269] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
> [ 80.491476] PM: freeze of devices complete after 21.037 msecs
> [ 80.498053] PM: late freeze of devices complete after 0.789 msecs
> [ 80.505563] PM: noirq freeze of devices complete after 1.387 msecs
> [ 80.511777] Disabling non-boot CPUs ...
> [ 80.540044] CPU1: shutdown
> [ 80.542755] psci: CPU1 killed.
> [ 80.611931] CPU2: shutdown
> [ 80.614646] psci: CPU2 killed.
> [ 80.687816] IRQ11 no longer affine to CPU3
> [ 80.687831] IRQ20 no longer affine to CPU3
> [ 80.687872] CPU3: shutdown
> [ 80.698806] psci: CPU3 killed.
> [ 80.725600] PM: Creating hibernation image:
> [ 80.725600] PM: Need to copy 84054 pages
> [ 80.725600] PM: Hibernation image created (84054 pages copied)
> [ 80.725609] Enabling non-boot CPUs ...
> [ 80.758320] Detected VIPT I-cache on CPU1
> [ 80.758381] CPU1: Booted secondary processor [410fd033]
> [ 80.758946] CPU1 is up
> [ 80.810889] Detected VIPT I-cache on CPU2
> [ 80.810932] CPU2: Booted secondary processor [410fd033]
> [ 80.811598] CPU2 is up
> [ 80.863596] Detected VIPT I-cache on CPU3
> [ 80.863640] CPU3: Booted secondary processor [410fd033]
> [ 80.864256] CPU3 is up
> [ 80.876712] PM: noirq thaw of devices complete after 0.802 msecs
> [ 80.883475] PM: early thaw of devices complete after 0.699 msecs
> [ 80.947177] PM: thaw of devices complete after 57.665 msecs
> [ 80.953222] hibernate: Hibernating on CPU 0 [mpidr:0x101]
> [ 80.961099] PM: Using 3 thread(s) for compression.
> [ 80.961099] PM: Compressing and saving image data (84219 pages)...
> [ 80.972154] PM: Image saving progress: 0%
> [ 81.270381] PM: Image saving progress: 10%
> [ 81.498242] PM: Image saving progress: 20%
> [ 81.789014] PM: Image saving progress: 30%
> [ 84.844984] PM: Image saving progress: 40%
> [ 87.657013] PM: Image saving progress: 50%
> [ 88.933987] PM: Image saving progress: 60%
> [ 90.597472] PM: Image saving progress: 70%
> [ 92.362916] PM: Image saving progress: 80%
> [ 93.622648] PM: Image saving progress: 90%
> [ 95.468855] PM: Image saving progress: 100%
> [ 99.073785] PM: Image saving done.
> [ 99.077240] PM: Wrote 336876 kbytes in 18.10 seconds (18.61 MB/s)
> [ 99.092099] PM: S|
> [ 99.370393] kvm: exiting hardware virtualization
> [ 99.535936] reboot: Restarting system
>
> [ ... ]
>
> [ 6.074235] Freezing user space processes ... (elapsed 0.000 seconds) done.
> [ 6.081540] Double checking all user space processes after OOM killer disable
> ... (elapsed 0.000 seconds)
> [ 6.119415] random: fast init done
> [ 6.133638] PM: Using 1 thread(s) for decompression.
> [ 6.133638] PM: Loading and decompressing image data (84219 pages)...
> [ 6.145079] hibernate: Hibernated on CPU 1 [mpidr:0x101]
> [ 6.150412] hibernate: Hibernated on a CPU that is offline! Bringing CPU up.
> [ 6.198971] Detected VIPT I-cache on CPU1
> [ 6.199036] CPU1: Booted secondary processor [410fd033]
> [ 7.177507] PM: Image loading progress: 0%
> [ 7.816126] PM: Image loading progress: 10%
> [ 8.176005] PM: Image loading progress: 20%
> [ 8.568329] PM: Image loading progress: 30%
> [ 8.991993] PM: Image loading progress: 40%
> [ 9.385313] PM: Image loading progress: 50%
> [ 9.708479] PM: Image loading progress: 60%
> [ 10.082700] PM: Image loading progress: 70%
> [ 10.478748] PM: Image loading progress: 80%
> [ 10.876730] PM: Image loading progress: 90%
> [ 11.275249] PM: Image loading progress: 100%
> [ 11.280021] PM: Image loading done.
> [ 11.283534] PM: Read 336876 kbytes in 5.13 seconds (65.66 MB/s)
> [ 11.316174] PM: quiesce of devices complete after 19.385 msecs
> [ 11.322864] PM: late quiesce of devices complete after 0.825 msecs
> [ 11.349479] PM: noirq quiesce of devices complete after 20.401 msecs
> [ 11.355864] Disabling non-boot CPUs ...
> [ 11.372531] IRQ7 no longer affine to CPU0
> [ 11.372537] IRQ9 no longer affine to CPU0
> [ 11.372544] IRQ11 no longer affine to CPU0
> [ 11.372559] IRQ20 no longer affine to CPU0
> [ 11.372595] CPU0: shutdown
> [ 11.391601] psci: CPU0 killed.
> [ 80.725613] Enabling non-boot CPUs ...
> [ 80.758314] Detected VIPT I-cache on CPU1
> [ 80.758375] CPU1: Booted secondary processor [410fd033]
> [ 80.758939] CPU1 is up
> [ 80.810877] Detected VIPT I-cache on CPU2
> [ 80.810917] CPU2: Booted secondary processor [410fd033]
> [ 80.811581] CPU2 is up
> [ 80.859588] Detected VIPT I-cache on CPU3
> [ 80.859628] CPU3: Booted secondary processor [410fd033]
> [ 80.860246] CPU3 is up
> [ 80.873337] PM: noirq restore of devices complete after 1.434 msecs
> [ 80.880381] PM: early restore of devices complete after 0.701 msecs
> [ 81.231471] PM: restore of devices complete after 71.157 msecs
> [ 81.238336] Restarting tasks ... done.
> root at ubuntu:~#
> -------------------------%<-------------------------
>
> James Morse (3):
> cpu/hotplug: Allow suspend/resume CPU to be specified
> arm64: hibernate: Resume when hibernate image created on non-boot CPU
> Revert "arm64: hibernate: Refuse to hibernate if the boot cpu is
> offline"
>
> arch/arm64/include/asm/suspend.h | 3 ++
> arch/arm64/kernel/hibernate.c | 67 +++++++++++++++++++++++++++-------------
> include/linux/cpu.h | 6 +++-
> kernel/cpu.c | 9 +++---
> 4 files changed, 59 insertions(+), 26 deletions(-)
What's the status of this?
Thanks,
Rafael
^ permalink raw reply
* [PATCH V2] cpufreq: create link to policy only for registered CPUs
From: Rafael J. Wysocki @ 2016-09-14 1:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <59daae7da84077fbf845b1aad2962933585f8e35.1473662181.git.viresh.kumar@linaro.org>
On Monday, September 12, 2016 12:07:05 PM Viresh Kumar wrote:
> If a cpufreq driver is registered very early in the boot stage (e.g.
> registered from postcore_initcall()), then cpufreq core may generate
> kernel warnings for it.
>
> In this case, the CPUs are brought online, then the cpufreq driver is
> registered, and then the CPU topology devices are registered. However,
> by the time cpufreq_add_dev() gets called, the cpu device isn't stored
> in the per-cpu variable (cpu_sys_devices,) which is read by
> get_cpu_device().
>
> So the cpufreq core fails to get device for the CPU, for which
> cpufreq_add_dev() was called in the first place and we will hit a
> WARN_ON(!cpu_dev).
>
> Even if we reuse the 'dev' parameter passed to cpufreq_add_dev() to
> avoid that warning, there might be other CPUs online that share the
> policy with the cpu for which cpufreq_add_dev() is called. Eventually
> get_cpu_device() will return NULL for them as well, and we will hit the
> same WARN_ON() again.
>
> In order to fix these issues, change cpufreq core to create links to the
> policy for a cpu only when cpufreq_add_dev() is called for that CPU.
>
> Reuse the 'real_cpus' mask to track that as well.
>
> Note that cpufreq_remove_dev() already handles removal of the links for
> individual CPUs and cpufreq_add_dev() has aligned with that now.
>
> Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
> Tested-by: Russell King <rmk+kernel@arm.linux.org.uk>
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Applied.
Thanks,
Rafael
^ permalink raw reply
* [PATCH 4/7] phy: meson: add USB2 PHY support for Meson8b and GXBB
From: Kevin Hilman @ 2016-09-14 0:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAFBinCC+izGS72TZuiiBu=DjtSmoZXRZ6r76M6rC8W7UTpSD6g@mail.gmail.com>
Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes:
> On Tue, Sep 13, 2016 at 5:28 PM, Philipp Zabel <p.zabel@pengutronix.de> wrote:
[...]
>>> I added Philipp and Hans to this thread - maybe they can comment on this.
>>> To sum it up, our problem is:
>>> - there are two separate USB PHYs on Meson GXBB
>>> - both are sharing the same reset line (provided by the reset-meson driver)
>>> - during initialization of the PHYs we must only call
>>> reset_control_reset(rstc) once (if we do it for the first *and* second
>>> PHY then the first PHY gets confused once the second PHY uses the
>>> reset because the first PHY's state is reset as well)
>>
>> If you have an initially asserted reset line and you can enable the
>> first module by deasserting the reset via reset_control_deassert (and
>> reset_control_assert to signal when the module may be disabled again
>> after use), shared resets are for you.
>>
>> If you need a reset pulse or have no direct control over the reset line,
>> (device_reset), the reset framework currently has no solution for this.
>> The ugly thing about reset_control_once would be that it can't re-reset
>> modules when unloading and reloading driver modules.
>
> The corresponding reset driver in question is reset-meson, which only
> implements reset (assert/deassert are not implemented). However, I
> don't know if this is due to hardware design.
> I think the hardware implements the latter, but maybe Neil can give
> more information here (I currently don't have access to my board so I
> cannot test how the hardware actually behaves).
It's implemented that way because the hardware only supports a reset
pulse.
Kevin
^ permalink raw reply
* [PATCH v3 4/7] palmetto: Request relevant mux functions in devicetree
From: Joel Stanley @ 2016-09-14 0:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473800295.8689.295.camel@kernel.crashing.org>
On Wed, Sep 14, 2016 at 6:28 AM, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> On Tue, 2016-09-13 at 22:11 +0930, Joel Stanley wrote:
>> It's not clear that all systems use these pins in that way. I will
>> not
>> include this one for now.
>
> Well, it has VGA so the VGA hsync, vsync and DDC should be there at
> least...
True. When we enable VGA in our tree these pins will be requested as
part of the VGA driver's device node, so they don't need to be hogged
by the pinctrl node like this.
The ones I'm not clear on are bmc_flack and bmc_int. Andrew, do you
have a recommendation here?
Cheers,
Joel
^ permalink raw reply
* [PATCH V2 00/10] PM / Domains: Add support for removing PM domains
From: Rafael J. Wysocki @ 2016-09-14 0:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473678074-15126-1-git-send-email-jonathanh@nvidia.com>
On Monday, September 12, 2016 12:01:04 PM Jon Hunter wrote:
> In order to safely remove PM domains there are a few changes that need to be
> made to ensure that no one is holding an external reference to a PM domain
> after it has been removed. One solution, implemented here, solves this by
> eliminating external references to PM domain.
>
> Changes from V1:
> - Added test when removing a PM domain to ensure the PM domain provider
> has been removed.
> - Re-organised patches 8-10 so that patch #8 adds the code to verify if
> the provider is present, patch #9 adds basic support for removing PM
> domains and patch #10 adds support for removing PM domains by provider.
>
> Changes from initial RFC:
> - Renamed functions made static per Ulf's feedback.
> - Added patch to clean-up provider/xlate APIs per Ulf's feedback
> - Re-worked and simplified the association between PM domains and PM
> domain providers. Dropped the 'provider_data' variable from the
> generic_pm_domain structure in favour of using the fwnode_handle.
> - Split patch for removing PM domains into multiple patches per Ulf's
> feedback.
>
> Jon Hunter (10):
> PM / Domains: Add new helper functions for device-tree
> ARM: EXYNOS: Remove calls to of_genpd_get_from_provider()
> staging: board: Remove calls to of_genpd_get_from_provider()
> PM / Domains: Don't expose generic_pm_domain structure to clients
> PM / Domains: Don't expose xlate and provider helper functions
> PM / Domains: Verify the PM domain is present when adding a provider
> PM / Domains: Prepare for adding support to remove PM domains
> PM / Domains: Store the provider in the PM domain structure
> PM / Domains: Add support for removing PM domains
> PM / Domains: Add support for removing nested PM domains by provider
All applied.
I'll expose a branch containing them after they've spent a couple of days in
linux-next.
Thanks,
Rafael
^ permalink raw reply
* [PATCH v3 2/2] ARM64: dts: meson-gxbb: add MMC support
From: Kevin Hilman @ 2016-09-14 0:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160914004314.682-1-khilman@baylibre.com>
Add binding and basic support for the SD/eMMC controller on Amlogic
S905/GXBB devices.
Signed-off-by: Kevin Hilman <khilman@baylibre.com>
---
.../devicetree/bindings/mmc/amlogic,meson-gxbb.txt | 29 ++++++++
.../arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 87 ++++++++++++++++++++++
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 78 +++++++++++++++++++
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 37 ++++++++-
4 files changed, 230 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/mmc/amlogic,meson-gxbb.txt
diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gxbb.txt b/Documentation/devicetree/bindings/mmc/amlogic,meson-gxbb.txt
new file mode 100644
index 000000000000..39cbe528b7de
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gxbb.txt
@@ -0,0 +1,29 @@
+Amlogic SD / eMMC controller for S905/GXBB family SoCs
+
+The MMC 5.1 compliant host controller on Amlogic provides the
+interface for SD, eMMC and SDIO devices.
+
+This file documents the properties in addition to those available in
+the MMC core bindings, documented by mmc.txt.
+
+Required properties:
+- compatible : contains "amlogic,meson-gxbb-mmc"
+- clocks : A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
+- clock-names: Should contain the following:
+ "core" - Main peripheral bus clock
+ "clkin0" - Parent clock of internal mux
+ "clkin1" - Other parent clock of internal mux
+ The driver has an interal mux clock which switches between clkin0 and clkin1 depending on the
+ clock rate requested by the MMC core.
+
+Example:
+
+ sd_emmc_a: mmc at 70000 {
+ compatible = "amlogic,meson-gxbb-mmc";
+ reg = <0x0 0x70000 0x0 0x2000>;
+ interrupts = < GIC_SPI 216 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc CLKID_SD_EMMC_A>, <&xtal>, <&clkc CLKID_FCLK_DIV2>;
+ clock-names = "core", "clkin0", "clkin1";
+ pinctrl-0 = <&emmc_pins>;
+ };
+
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index 90a84c514d3d..2a9303e7fab8 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -73,6 +73,56 @@
default-state = "off";
};
};
+
+ tflash_vdd: regulator-tflash_vdd {
+ /*
+ * signal name from schematics: TFLASH_VDD_EN
+ */
+ compatible = "regulator-fixed";
+
+ regulator-name = "TFLASH_VDD";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpio = <&gpio_ao GPIOAO_12 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ tf_io: gpio-regulator-tf_io {
+ compatible = "regulator-gpio";
+
+ regulator-name = "TF_IO";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+
+ /*
+ * signal name from schematics: TF_3V3N_1V8_EN
+ */
+ gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>;
+ gpios-states = <0>;
+
+ states = <3300000 0
+ 1800000 1>;
+ };
+
+ vcc1v8: regulator-vcc1v8 {
+ compatible = "regulator-fixed";
+ regulator-name = "VCC1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ vcc3v3: regulator-vcc3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "VCC3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ emmc_pwrseq: emmc-pwrseq {
+ compatible = "mmc-pwrseq-emmc";
+ reset-gpios = <&gpio BOOT_9 GPIO_ACTIVE_LOW>;
+ };
};
&uart_AO {
@@ -87,3 +137,40 @@
pinctrl-names = "default";
};
+/* SD */
+&sd_emmc_b {
+ status = "okay";
+ pinctrl-0 = <&sdcard_pins>;
+ pinctrl-names = "default";
+
+ bus-width = <4>;
+ cap-sd-highspeed;
+ max-frequency = <100000000>;
+ disable-wp;
+
+ cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
+ cd-inverted;
+
+ vmmc-supply = <&tflash_vdd>;
+ vqmmc-supply = <&tf_io>;
+};
+
+/* eMMC */
+&sd_emmc_c {
+ status = "okay";
+ pinctrl-0 = <&emmc_pins>;
+ pinctrl-names = "default";
+
+ bus-width = <8>;
+ cap-sd-highspeed;
+ max-frequency = <200000000>;
+ non-removable;
+ disable-wp;
+ cap-mmc-highspeed;
+ mmc-ddr-1_8v;
+ mmc-hs200-1_8v;
+
+ mmc-pwrseq = <&emmc_pwrseq>;
+ vmmc-supply = <&vcc3v3>;
+ vqmmc-supply = <&vcc1v8>;
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
index f4f30f674b4c..77c4d5eb48a2 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
@@ -57,6 +57,47 @@
device_type = "memory";
reg = <0x0 0x0 0x0 0x40000000>;
};
+
+ vddio_card: gpio-regulator {
+ compatible = "regulator-gpio";
+
+ regulator-name = "VDDIO_CARD";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpios = <&gpio_ao GPIOAO_5 GPIO_ACTIVE_HIGH>;
+ gpios-states = <1>;
+
+ /* Based on P200 schematics, signal CARD_1.8V/3.3V_CTR */
+ states = <1800000 0
+ 3300000 1>;
+ };
+
+ vddio_boot: regulator-vddio_boot {
+ compatible = "regulator-fixed";
+ regulator-name = "VDDIO_BOOT";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ vddao_3v3: regulator-vddao_3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "VDDAO_3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ vcc_3v3: regulator-vcc_3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "VCC_3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ emmc_pwrseq: emmc-pwrseq {
+ compatible = "mmc-pwrseq-emmc";
+ reset-gpios = <&gpio BOOT_9 GPIO_ACTIVE_LOW>;
+ };
};
/* This UART is brought out to the DB9 connector */
@@ -72,3 +113,40 @@
pinctrl-names = "default";
};
+/* SD card */
+&sd_emmc_b {
+ status = "okay";
+ pinctrl-0 = <&sdcard_pins>;
+ pinctrl-names = "default";
+
+ bus-width = <4>;
+ cap-sd-highspeed;
+ max-frequency = <100000000>;
+ disable-wp;
+
+ cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
+ cd-inverted;
+
+ vmmc-supply = <&vddao_3v3>;
+ vqmmc-supply = <&vddio_card>;
+};
+
+/* eMMC */
+&sd_emmc_c {
+ status = "okay";
+ pinctrl-0 = <&emmc_pins>;
+ pinctrl-names = "default";
+
+ bus-width = <8>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ max-frequency = <200000000>;
+ non-removable;
+ disable-wp;
+ mmc-ddr-1_8v;
+ mmc-hs200-1_8v;
+
+ mmc-pwrseq = <&emmc_pwrseq>;
+ vmmc-supply = <&vcc_3v3>;
+ vqmmc-supply = <&vddio_boot>;
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index e502c24b0ac7..3723007146ac 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -45,6 +45,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/gpio/meson-gxbb-gpio.h>
#include <dt-bindings/reset/amlogic,meson-gxbb-reset.h>
+#include <dt-bindings/clock/gxbb-clkc.h>
/ {
compatible = "amlogic,meson-gxbb";
@@ -246,7 +247,8 @@
mux {
groups = "emmc_nand_d07",
"emmc_cmd",
- "emmc_clk";
+ "emmc_clk",
+ "emmc_ds";
function = "emmc";
};
};
@@ -329,6 +331,39 @@
#address-cells = <2>;
#size-cells = <2>;
ranges = <0x0 0x0 0x0 0xd0000000 0x0 0x200000>;
+
+ sd_emmc_a: mmc at 70000 {
+ compatible = "amlogic,meson-gxbb-mmc";
+ reg = <0x0 0x70000 0x0 0x2000>;
+ interrupts = <GIC_SPI 216 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc CLKID_SD_EMMC_A>,
+ <&xtal>,
+ <&clkc CLKID_FCLK_DIV2>;
+ clock-names = "core", "clkin0", "clkin1";
+ status = "disabled";
+ };
+
+ sd_emmc_b: mmc at 72000 {
+ compatible = "amlogic,meson-gxbb-mmc";
+ reg = <0x0 0x72000 0x0 0x2000>;
+ interrupts = <GIC_SPI 217 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc CLKID_SD_EMMC_B>,
+ <&xtal>,
+ <&clkc CLKID_FCLK_DIV2>;
+ clock-names = "core", "clkin0", "clkin1";
+ status = "disabled";
+ };
+
+ sd_emmc_c: mmc at 74000 {
+ compatible = "amlogic,meson-gxbb-mmc";
+ reg = <0x0 0x74000 0x0 0x2000>;
+ interrupts = <GIC_SPI 218 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc CLKID_SD_EMMC_C>,
+ <&xtal>,
+ <&clkc CLKID_FCLK_DIV2>;
+ clock-names = "core", "clkin0", "clkin1";
+ status = "disabled";
+ };
};
ethmac: ethernet at c9410000 {
--
2.9.3
^ permalink raw reply related
* [PATCH v3 1/2] MMC: meson: initial support for GXBB platforms
From: Kevin Hilman @ 2016-09-14 0:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160914004314.682-1-khilman@baylibre.com>
Initial support for the SD/eMMC controller in the Amlogic S905/GXBB
family of SoCs.
Currently working for the SD and eMMC interfaces, but not yet tested
for SDIO.
Signed-off-by: Kevin Hilman <khilman@baylibre.com>
---
MAINTAINERS | 1 +
drivers/mmc/host/Kconfig | 10 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/meson-gxbb.c | 838 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 850 insertions(+)
create mode 100644 drivers/mmc/host/meson-gxbb.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 20bb1d00098c..c4dd5b2a346d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -999,6 +999,7 @@ F: arch/arm/mach-meson/
F: arch/arm/boot/dts/meson*
F: arch/arm64/boot/dts/amlogic/
F: drivers/pinctrl/meson/
+F: drivers/mmc/host/meson*
N: meson
ARM/Annapurna Labs ALPINE ARCHITECTURE
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5274f503a39a..d3b3a08c2361 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -322,6 +322,16 @@ config MMC_SDHCI_IPROC
If unsure, say N.
+config MMC_MESON_GXBB
+ tristate "Amlogic S905/GXBB SD/MMC Host Controller support"
+ depends on ARCH_MESON && MMC
+ help
+ This selects support for the Amlogic SD/MMC Host Controller
+ found on the S905/GXBB family of SoCs. This controller is
+ MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces.
+
+ If you have a controller with this interface, say Y here.
+
config MMC_MOXART
tristate "MOXART SD/MMC Host Controller support"
depends on ARCH_MOXART && MMC
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e2bdaaf43184..fef21586b4fb 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
obj-$(CONFIG_MMC_USHC) += ushc.o
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
+obj-$(CONFIG_MMC_MESON_GXBB) += meson-gxbb.o
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o
diff --git a/drivers/mmc/host/meson-gxbb.c b/drivers/mmc/host/meson-gxbb.c
new file mode 100644
index 000000000000..c73c62178764
--- /dev/null
+++ b/drivers/mmc/host/meson-gxbb.c
@@ -0,0 +1,838 @@
+/*
+ * Amlogic SD/eMMC driver for the GX/S905 family SoCs
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Kevin Hilman <khilman@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public 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/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/regulator/consumer.h>
+
+#define DRIVER_NAME "meson-gxbb-mmc"
+
+#define SD_EMMC_CLOCK 0x0
+#define CLK_DIV_SHIFT 0
+#define CLK_DIV_WIDTH 6
+#define CLK_DIV_MASK 0x3f
+#define CLK_DIV_MAX 63
+#define CLK_SRC_SHIFT 6
+#define CLK_SRC_WIDTH 2
+#define CLK_SRC_MASK 0x3
+#define CLK_SRC_XTAL 0 /* external crystal */
+#define CLK_SRC_XTAL_RATE 24000000
+#define CLK_SRC_PLL 1 /* FCLK_DIV2 */
+#define CLK_SRC_PLL_RATE 1000000000
+#define CLK_PHASE_SHIFT 8
+#define CLK_PHASE_MASK 0x3
+#define CLK_PHASE_0 0
+#define CLK_PHASE_90 1
+#define CLK_PHASE_180 2
+#define CLK_PHASE_270 3
+#define CLK_ALWAYS_ON BIT(24)
+
+#define SD_EMMC_DElAY 0x4
+#define SD_EMMC_ADJUST 0x8
+#define SD_EMMC_CALOUT 0x10
+#define SD_EMMC_START 0x40
+#define START_DESC_INIT BIT(0)
+#define START_DESC_BUSY BIT(1)
+#define START_DESC_ADDR_SHIFT 2
+#define START_DESC_ADDR_MASK (~0x3)
+
+#define SD_EMMC_CFG 0x44
+#define CFG_BUS_WIDTH_SHIFT 0
+#define CFG_BUS_WIDTH_MASK 0x3
+#define CFG_BUS_WIDTH_1 0x0
+#define CFG_BUS_WIDTH_4 0x1
+#define CFG_BUS_WIDTH_8 0x2
+#define CFG_DDR BIT(2)
+#define CFG_BLK_LEN_SHIFT 4
+#define CFG_BLK_LEN_MASK 0xf
+#define CFG_RESP_TIMEOUT_SHIFT 8
+#define CFG_RESP_TIMEOUT_MASK 0xf
+#define CFG_RC_CC_SHIFT 12
+#define CFG_RC_CC_MASK 0xf
+#define CFG_STOP_CLOCK BIT(22)
+#define CFG_CLK_ALWAYS_ON BIT(18)
+#define CFG_AUTO_CLK BIT(23)
+
+#define SD_EMMC_STATUS 0x48
+#define STATUS_BUSY BIT(31)
+
+#define SD_EMMC_IRQ_EN 0x4c
+#define IRQ_EN_MASK 0x3fff
+#define IRQ_RXD_ERR_SHIFT 0
+#define IRQ_RXD_ERR_MASK 0xff
+#define IRQ_TXD_ERR BIT(8)
+#define IRQ_DESC_ERR BIT(9)
+#define IRQ_RESP_ERR BIT(10)
+#define IRQ_RESP_TIMEOUT BIT(11)
+#define IRQ_DESC_TIMEOUT BIT(12)
+#define IRQ_END_OF_CHAIN BIT(13)
+#define IRQ_RESP_STATUS BIT(14)
+#define IRQ_SDIO BIT(15)
+
+#define SD_EMMC_CMD_CFG 0x50
+#define SD_EMMC_CMD_ARG 0x54
+#define SD_EMMC_CMD_DAT 0x58
+#define SD_EMMC_CMD_RSP 0x5c
+#define SD_EMMC_CMD_RSP1 0x60
+#define SD_EMMC_CMD_RSP2 0x64
+#define SD_EMMC_CMD_RSP3 0x68
+
+#define SD_EMMC_RXD 0x94
+#define SD_EMMC_TXD 0x94
+#define SD_EMMC_LAST_REG SD_EMMC_TXD
+
+#define SD_EMMC_CFG_BLK_SIZE 512 /* internal buffer max: 512 bytes */
+#define SD_EMMC_CFG_RESP_TIMEOUT 256 /* in clock cycles */
+#define SD_EMMC_CFG_CMD_GAP 16 /* in clock cycles */
+#define MUX_CLK_NUM_PARENTS 2
+
+struct meson_host {
+ struct device *dev;
+ struct mmc_host *mmc;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+
+ spinlock_t lock;
+ void __iomem *regs;
+ int irq;
+ u32 ocr_mask;
+ struct clk *core_clk;
+ struct clk_mux mux;
+ struct clk *mux_clk;
+ struct clk *mux_parent[MUX_CLK_NUM_PARENTS];
+ unsigned long mux_parent_rate[MUX_CLK_NUM_PARENTS];
+
+ struct clk_divider cfg_div;
+ struct clk *cfg_div_clk;
+
+ unsigned int bounce_buf_size;
+ void *bounce_buf;
+ dma_addr_t bounce_dma_addr;
+
+ bool vqmmc_enabled;
+};
+
+struct sd_emmc_desc {
+ u32 cmd_cfg;
+ u32 cmd_arg;
+ u32 cmd_data;
+ u32 cmd_resp;
+};
+#define CMD_CFG_LENGTH_SHIFT 0
+#define CMD_CFG_LENGTH_MASK 0x1ff
+#define CMD_CFG_BLOCK_MODE BIT(9)
+#define CMD_CFG_R1B BIT(10)
+#define CMD_CFG_END_OF_CHAIN BIT(11)
+#define CMD_CFG_TIMEOUT_SHIFT 12
+#define CMD_CFG_TIMEOUT_MASK 0xf
+#define CMD_CFG_NO_RESP BIT(16)
+#define CMD_CFG_NO_CMD BIT(17)
+#define CMD_CFG_DATA_IO BIT(18)
+#define CMD_CFG_DATA_WR BIT(19)
+#define CMD_CFG_RESP_NOCRC BIT(20)
+#define CMD_CFG_RESP_128 BIT(21)
+#define CMD_CFG_RESP_NUM BIT(22)
+#define CMD_CFG_DATA_NUM BIT(23)
+#define CMD_CFG_CMD_INDEX_SHIFT 24
+#define CMD_CFG_CMD_INDEX_MASK 0x3f
+#define CMD_CFG_ERROR BIT(30)
+#define CMD_CFG_OWNER BIT(31)
+
+#define CMD_DATA_MASK (~0x3)
+#define CMD_DATA_BIG_ENDIAN BIT(1)
+#define CMD_DATA_SRAM BIT(0)
+#define CMD_RESP_MASK (~0x1)
+#define CMD_RESP_SRAM BIT(0)
+
+static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate)
+{
+ struct mmc_host *mmc = host->mmc;
+ int ret = 0;
+ u32 cfg;
+
+ if (clk_rate) {
+ if (WARN_ON(clk_rate > mmc->f_max))
+ clk_rate = mmc->f_max;
+ else if (WARN_ON(clk_rate < mmc->f_min))
+ clk_rate = mmc->f_min;
+ }
+
+ if (clk_rate == mmc->actual_clock)
+ return 0;
+
+ /* stop clock */
+ cfg = readl(host->regs + SD_EMMC_CFG);
+ if (!(cfg & CFG_STOP_CLOCK)) {
+ cfg |= CFG_STOP_CLOCK;
+ writel(cfg, host->regs + SD_EMMC_CFG);
+ }
+
+ dev_dbg(host->dev, "change clock rate %u -> %lu\n",
+ mmc->actual_clock, clk_rate);
+ ret = clk_set_rate(host->cfg_div_clk, clk_rate);
+ if (clk_rate && clk_rate != clk_get_rate(host->cfg_div_clk))
+ dev_warn(host->dev, "divider requested rate %lu != actual rate %lu: ret=%d\n",
+ clk_rate, clk_get_rate(host->cfg_div_clk), ret);
+ else
+ mmc->actual_clock = clk_rate;
+
+ /* (re)start clock, if non-zero */
+ if (clk_rate) {
+ cfg = readl(host->regs + SD_EMMC_CFG);
+ cfg &= ~CFG_STOP_CLOCK;
+ writel(cfg, host->regs + SD_EMMC_CFG);
+ }
+
+ return ret;
+}
+
+/*
+ * The SD/eMMC IP block has an internal mux and divider used for
+ * generating the MMC clock. Use the clock framework to create and
+ * manage these clocks.
+ */
+static int meson_mmc_clk_init(struct meson_host *host)
+{
+ struct clk_init_data init;
+ char clk_name[32];
+ int i, ret = 0;
+ const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
+ unsigned int mux_parent_count = 0;
+ const char *clk_div_parents[1];
+ unsigned int f_min = UINT_MAX;
+ u32 clk_reg, cfg;
+
+ /* get the mux parents */
+ for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
+ char name[16];
+
+ snprintf(name, sizeof(name), "clkin%d", i);
+ host->mux_parent[i] = devm_clk_get(host->dev, name);
+ if (IS_ERR(host->mux_parent[i])) {
+ ret = PTR_ERR(host->mux_parent[i]);
+ if (PTR_ERR(host->mux_parent[i]) != -EPROBE_DEFER)
+ dev_err(host->dev, "Missing clock %s\n", name);
+ host->mux_parent[i] = NULL;
+ return ret;
+ }
+
+ host->mux_parent_rate[i] = clk_get_rate(host->mux_parent[i]);
+ mux_parent_names[i] = __clk_get_name(host->mux_parent[i]);
+ mux_parent_count++;
+ if (host->mux_parent_rate[i] < f_min)
+ f_min = host->mux_parent_rate[i];
+ }
+
+ /* cacluate f_min based on input clocks, and max divider value */
+ if (f_min != UINT_MAX)
+ f_min = DIV_ROUND_UP(CLK_SRC_XTAL_RATE, CLK_DIV_MAX);
+ else
+ f_min = 4000000; /* default min: 400 MHz */
+ host->mmc->f_min = f_min;
+
+ /* create the mux */
+ snprintf(clk_name, sizeof(clk_name), "%s#mux", dev_name(host->dev));
+ init.name = clk_name;
+ init.ops = &clk_mux_ops;
+ init.flags = 0;
+ init.parent_names = mux_parent_names;
+ init.num_parents = mux_parent_count;
+
+ host->mux.reg = host->regs + SD_EMMC_CLOCK;
+ host->mux.shift = CLK_SRC_SHIFT;
+ host->mux.mask = CLK_SRC_MASK;
+ host->mux.flags = 0;
+ host->mux.table = NULL;
+ host->mux.hw.init = &init;
+
+ host->mux_clk = devm_clk_register(host->dev, &host->mux.hw);
+ if (WARN_ON(IS_ERR(host->mux_clk)))
+ return PTR_ERR(host->mux_clk);
+
+ /* create the divider */
+ snprintf(clk_name, sizeof(clk_name), "%s#div", dev_name(host->dev));
+ init.name = devm_kstrdup(host->dev, clk_name, GFP_KERNEL);
+ init.ops = &clk_divider_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ clk_div_parents[0] = __clk_get_name(host->mux_clk);
+ init.parent_names = clk_div_parents;
+ init.num_parents = ARRAY_SIZE(clk_div_parents);
+
+ host->cfg_div.reg = host->regs + SD_EMMC_CLOCK;
+ host->cfg_div.shift = CLK_DIV_SHIFT;
+ host->cfg_div.width = CLK_DIV_WIDTH;
+ host->cfg_div.hw.init = &init;
+ host->cfg_div.flags = CLK_DIVIDER_ONE_BASED |
+ CLK_DIVIDER_ROUND_CLOSEST | CLK_DIVIDER_ALLOW_ZERO;
+
+ host->cfg_div_clk = devm_clk_register(host->dev, &host->cfg_div.hw);
+ if (WARN_ON(PTR_ERR_OR_ZERO(host->cfg_div_clk)))
+ return PTR_ERR(host->cfg_div_clk);
+
+ /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
+ clk_reg = 0;
+ clk_reg |= CLK_PHASE_180 << CLK_PHASE_SHIFT;
+ clk_reg |= CLK_SRC_XTAL << CLK_SRC_SHIFT;
+ clk_reg |= CLK_DIV_MAX << CLK_DIV_SHIFT;
+ clk_reg &= ~CLK_ALWAYS_ON;
+ writel(clk_reg, host->regs + SD_EMMC_CLOCK);
+
+ /* Ensure clock starts in "auto" mode, not "always on" */
+ cfg = readl(host->regs + SD_EMMC_CFG);
+ cfg &= ~CFG_CLK_ALWAYS_ON;
+ cfg |= CFG_AUTO_CLK;
+ writel(cfg, host->regs + SD_EMMC_CFG);
+
+ ret = clk_prepare_enable(host->cfg_div_clk);
+ if (!ret)
+ ret = meson_mmc_clk_set(host, f_min);
+
+ return ret;
+}
+
+static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct meson_host *host = mmc_priv(mmc);
+ u32 bus_width;
+ u32 val, orig;
+
+ /*
+ * GPIO regulator, only controls switching between 1v8 and
+ * 3v3, doesn't support MMC_POWER_OFF, MMC_POWER_ON.
+ */
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+
+ if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
+ regulator_disable(mmc->supply.vqmmc);
+ host->vqmmc_enabled = false;
+ }
+
+ break;
+
+ case MMC_POWER_UP:
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
+ break;
+
+ case MMC_POWER_ON:
+ if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
+ int ret = regulator_enable(mmc->supply.vqmmc);
+ if (ret < 0)
+ dev_err(mmc_dev(mmc),
+ "failed to enable vqmmc regulator\n");
+ else
+ host->vqmmc_enabled = true;
+ }
+
+ break;
+ }
+
+
+ meson_mmc_clk_set(host, ios->clock);
+
+ /* Bus width */
+ val = readl(host->regs + SD_EMMC_CFG);
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ bus_width = CFG_BUS_WIDTH_1;
+ break;
+ case MMC_BUS_WIDTH_4:
+ bus_width = CFG_BUS_WIDTH_4;
+ break;
+ case MMC_BUS_WIDTH_8:
+ bus_width = CFG_BUS_WIDTH_8;
+ break;
+ default:
+ dev_err(host->dev, "Invalid ios->bus_width: %u. Setting to 4.\n",
+ ios->bus_width);
+ bus_width = CFG_BUS_WIDTH_4;
+ return;
+ }
+
+ val = readl(host->regs + SD_EMMC_CFG);
+ orig = val;
+
+ val &= ~(CFG_BUS_WIDTH_MASK << CFG_BUS_WIDTH_SHIFT);
+ val |= bus_width << CFG_BUS_WIDTH_SHIFT;
+
+ val &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
+ val |= ilog2(SD_EMMC_CFG_BLK_SIZE) << CFG_BLK_LEN_SHIFT;
+
+ val &= ~(CFG_RESP_TIMEOUT_MASK << CFG_RESP_TIMEOUT_SHIFT);
+ val |= ilog2(SD_EMMC_CFG_RESP_TIMEOUT) << CFG_RESP_TIMEOUT_SHIFT;
+
+ val &= ~(CFG_RC_CC_MASK << CFG_RC_CC_SHIFT);
+ val |= ilog2(SD_EMMC_CFG_CMD_GAP) << CFG_RC_CC_SHIFT;
+
+ writel(val, host->regs + SD_EMMC_CFG);
+
+ if (val != orig)
+ dev_dbg(host->dev, "%s: SD_EMMC_CFG: 0x%08x -> 0x%08x\n",
+ __func__, orig, val);
+}
+
+static int meson_mmc_request_done(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct meson_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd = host->cmd;
+
+ WARN_ON(host->mrq != mrq);
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+ mmc_request_done(host->mmc, mrq);
+
+ return 0;
+}
+
+static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
+{
+ struct meson_host *host = mmc_priv(mmc);
+ struct sd_emmc_desc *desc, desc_tmp;
+ u32 cfg;
+ u8 blk_len, cmd_cfg_timeout;
+ unsigned int xfer_bytes = 0;
+
+ /* Setup descriptors */
+ dma_rmb();
+ desc = &desc_tmp;
+ memset(desc, 0, sizeof(struct sd_emmc_desc));
+
+ desc->cmd_cfg |= (cmd->opcode & CMD_CFG_CMD_INDEX_MASK) << CMD_CFG_CMD_INDEX_SHIFT;
+ desc->cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */
+ desc->cmd_arg = cmd->arg;
+
+ /* Response */
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ desc->cmd_cfg &= ~CMD_CFG_NO_RESP;
+ if (cmd->flags & MMC_RSP_136)
+ desc->cmd_cfg |= CMD_CFG_RESP_128;
+ desc->cmd_cfg |= CMD_CFG_RESP_NUM;
+ desc->cmd_resp = 0;
+
+ if (!(cmd->flags & MMC_RSP_CRC))
+ desc->cmd_cfg |= CMD_CFG_RESP_NOCRC;
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ desc->cmd_cfg |= CMD_CFG_R1B;
+ } else {
+ desc->cmd_cfg |= CMD_CFG_NO_RESP;
+ }
+
+ /* data? */
+ if (cmd->data) {
+ desc->cmd_cfg |= CMD_CFG_DATA_IO;
+ if (cmd->data->blocks > 1) {
+ desc->cmd_cfg |= CMD_CFG_BLOCK_MODE;
+ desc->cmd_cfg |= (cmd->data->blocks & CMD_CFG_LENGTH_MASK) << CMD_CFG_LENGTH_SHIFT;
+
+ /* check if block-size matches, if not update */
+ cfg = readl(host->regs + SD_EMMC_CFG);
+ blk_len = cfg & (CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
+ blk_len >>= CFG_BLK_LEN_SHIFT;
+ if (blk_len != ilog2(cmd->data->blksz)) {
+ dev_warn(host->dev, "%s: update blk_len %d -> %d\n",
+ __func__, blk_len,
+ ilog2(cmd->data->blksz));
+ blk_len = ilog2(cmd->data->blksz);
+ cfg &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
+ cfg |= blk_len << CFG_BLK_LEN_SHIFT;
+ writel(cfg, host->regs + SD_EMMC_CFG);
+ }
+ } else {
+ desc->cmd_cfg &= ~CMD_CFG_BLOCK_MODE;
+ desc->cmd_cfg |= (cmd->data->blksz & CMD_CFG_LENGTH_MASK) << CMD_CFG_LENGTH_SHIFT;
+ }
+
+ cmd->data->bytes_xfered = 0;
+ xfer_bytes = cmd->data->blksz * cmd->data->blocks;
+ if (cmd->data->flags & MMC_DATA_WRITE) {
+ desc->cmd_cfg |= CMD_CFG_DATA_WR;
+ WARN_ON(xfer_bytes > host->bounce_buf_size);
+ sg_copy_to_buffer(cmd->data->sg, cmd->data->sg_len,
+ host->bounce_buf, xfer_bytes);
+ cmd->data->bytes_xfered = xfer_bytes;
+ dma_wmb();
+ } else {
+ desc->cmd_cfg &= ~CMD_CFG_DATA_WR;
+ }
+
+ if (xfer_bytes > 0) {
+ desc->cmd_cfg &= ~CMD_CFG_DATA_NUM;
+ desc->cmd_data = host->bounce_dma_addr & CMD_DATA_MASK;
+ } else {
+ /* write data to data_addr */
+ desc->cmd_cfg |= CMD_CFG_DATA_NUM;
+ desc->cmd_data = 0;
+ }
+
+ cmd_cfg_timeout = 12;
+ } else {
+ desc->cmd_cfg &= ~CMD_CFG_DATA_IO;
+ cmd_cfg_timeout = 10;
+ }
+ desc->cmd_cfg |= (cmd_cfg_timeout & CMD_CFG_TIMEOUT_MASK) << CMD_CFG_TIMEOUT_SHIFT;
+
+ host->cmd = cmd;
+
+ /* Last descriptor */
+ desc->cmd_cfg |= CMD_CFG_END_OF_CHAIN;
+ writel(desc->cmd_cfg, host->regs + SD_EMMC_CMD_CFG);
+ writel(desc->cmd_data, host->regs + SD_EMMC_CMD_DAT);
+ writel(desc->cmd_resp, host->regs + SD_EMMC_CMD_RSP);
+ wmb(); /* ensure descriptor is written before kicked */
+ writel(desc->cmd_arg, host->regs + SD_EMMC_CMD_ARG);
+}
+
+static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct meson_host *host = mmc_priv(mmc);
+
+ WARN_ON(host->mrq != NULL);
+
+ /* Stop execution */
+ writel(0, host->regs + SD_EMMC_START);
+
+ /* clear, ack, enable all interrupts */
+ writel(0, host->regs + SD_EMMC_IRQ_EN);
+ writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
+ writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN);
+
+ host->mrq = mrq;
+
+ if (mrq->sbc)
+ meson_mmc_start_cmd(mmc, mrq->sbc);
+ else
+ meson_mmc_start_cmd(mmc, mrq->cmd);
+}
+
+static int meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd)
+{
+ struct meson_host *host = mmc_priv(mmc);
+
+ if (cmd->flags & MMC_RSP_136) {
+ cmd->resp[0] = readl(host->regs + SD_EMMC_CMD_RSP3);
+ cmd->resp[1] = readl(host->regs + SD_EMMC_CMD_RSP2);
+ cmd->resp[2] = readl(host->regs + SD_EMMC_CMD_RSP1);
+ cmd->resp[3] = readl(host->regs + SD_EMMC_CMD_RSP);
+ } else if (cmd->flags & MMC_RSP_PRESENT) {
+ cmd->resp[0] = readl(host->regs + SD_EMMC_CMD_RSP);
+ }
+
+ return 0;
+}
+
+static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
+{
+ struct meson_host *host = dev_id;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd = host->cmd;
+ u32 irq_en, status, raw_status;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ if (WARN_ON(!host))
+ return IRQ_NONE;
+
+ mrq = host->mrq;
+
+ if (WARN_ON(!mrq))
+ return IRQ_NONE;
+
+ if (WARN_ON(!cmd))
+ return IRQ_NONE;
+
+ spin_lock(&host->lock);
+ irq_en = readl(host->regs + SD_EMMC_IRQ_EN);
+ raw_status = readl(host->regs + SD_EMMC_STATUS);
+ status = raw_status & irq_en;
+
+ if (!status) {
+ dev_warn(host->dev, "Spurious IRQ! status=0x%08x, irq_en=0x%08x\n",
+ raw_status, irq_en);
+ ret = IRQ_NONE;
+ goto out;
+ }
+
+ cmd->error = 0;
+ if (status & IRQ_RXD_ERR_MASK) {
+ dev_dbg(host->dev, "Unhandled IRQ: RXD error\n");
+ cmd->error = -EILSEQ;
+ }
+ if (status & IRQ_TXD_ERR) {
+ dev_dbg(host->dev, "Unhandled IRQ: TXD error\n");
+ cmd->error = -EILSEQ;
+ }
+ if (status & IRQ_DESC_ERR)
+ dev_dbg(host->dev, "Unhandled IRQ: Descriptor error\n");
+ if (status & IRQ_RESP_ERR) {
+ dev_dbg(host->dev, "Unhandled IRQ: Response error\n");
+ cmd->error = -EILSEQ;
+ }
+ if (status & IRQ_RESP_TIMEOUT) {
+ dev_dbg(host->dev, "Unhandled IRQ: Response timeout\n");
+ cmd->error = -ETIMEDOUT;
+ }
+ if (status & IRQ_DESC_TIMEOUT) {
+ dev_dbg(host->dev, "Unhandled IRQ: Descriptor timeout\n");
+ cmd->error = -ETIMEDOUT;
+ }
+ if (status & IRQ_SDIO)
+ dev_dbg(host->dev, "Unhandled IRQ: SDIO.\n");
+
+ if (status & (IRQ_END_OF_CHAIN | IRQ_RESP_STATUS))
+ ret = IRQ_WAKE_THREAD;
+ else {
+ dev_warn(host->dev, "Unknown IRQ! status=0x%04x: MMC CMD%u arg=0x%08x flags=0x%08x stop=%d\n",
+ status, cmd->opcode, cmd->arg,
+ cmd->flags, mrq->stop ? 1 : 0);
+ if (cmd->data) {
+ struct mmc_data *data = cmd->data;
+
+ dev_warn(host->dev, "\tblksz %u blocks %u flags 0x%08x (%s%s)",
+ data->blksz, data->blocks, data->flags,
+ data->flags & MMC_DATA_WRITE ? "write" : "",
+ data->flags & MMC_DATA_READ ? "read" : "");
+ }
+ }
+
+out:
+ /* ack all (enabled) interrupts */
+ writel(status, host->regs + SD_EMMC_STATUS);
+
+ if (ret == IRQ_HANDLED) {
+ meson_mmc_read_resp(host->mmc, cmd);
+ meson_mmc_request_done(host->mmc, cmd->mrq);
+ }
+
+ spin_unlock(&host->lock);
+ return ret;
+}
+
+static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
+{
+ struct meson_host *host = dev_id;
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_command *cmd = host->cmd;
+ struct mmc_data *data;
+ unsigned int xfer_bytes;
+ int ret = IRQ_HANDLED;
+
+ if (WARN_ON(!mrq))
+ ret = IRQ_NONE;
+
+ if (WARN_ON(!cmd))
+ ret = IRQ_NONE;
+
+ data = cmd->data;
+ if (data) {
+ xfer_bytes = data->blksz * data->blocks;
+ if (data->flags & MMC_DATA_READ) {
+ WARN_ON(xfer_bytes > host->bounce_buf_size);
+ sg_copy_from_buffer(data->sg, data->sg_len,
+ host->bounce_buf, xfer_bytes);
+ data->bytes_xfered = xfer_bytes;
+ }
+ }
+
+ meson_mmc_read_resp(host->mmc, cmd);
+ if (!data || !data->stop || mrq->sbc)
+ meson_mmc_request_done(host->mmc, mrq);
+ else
+ meson_mmc_start_cmd(host->mmc, data->stop);
+
+ return ret;
+}
+
+/*
+ * NOTE: we only need this until the GPIO/pinctrl driver can handle
+ * interrupts. For now, the MMC core will use this for polling.
+ */
+static int meson_mmc_get_cd(struct mmc_host *mmc)
+{
+ int status = mmc_gpio_get_cd(mmc);
+
+ if (status == -ENOSYS)
+ return 1; /* assume present */
+
+ return status;
+}
+
+static const struct mmc_host_ops meson_mmc_ops = {
+ .request = meson_mmc_request,
+ .set_ios = meson_mmc_set_ios,
+ .get_cd = meson_mmc_get_cd,
+};
+
+static int meson_mmc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct meson_host *host;
+ struct mmc_host *mmc;
+ int ret;
+
+ mmc = mmc_alloc_host(sizeof(struct meson_host), &pdev->dev);
+ if (!mmc)
+ return -ENOMEM;
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, host);
+
+ spin_lock_init(&host->lock);
+
+ host->core_clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(host->core_clk)) {
+ ret = PTR_ERR(host->core_clk);
+ goto free_host;
+ }
+
+ /* Get regulators and the supported OCR mask */
+ host->vqmmc_enabled = false;
+ ret = mmc_regulator_get_supply(mmc);
+ if (ret == -EPROBE_DEFER)
+ goto free_host;
+
+ ret = mmc_of_parse(mmc);
+ if (ret) {
+ dev_warn(&pdev->dev, "error parsing DT: %d\n", ret);
+ goto free_host;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->regs)) {
+ ret = PTR_ERR(host->regs);
+ goto free_host;
+ }
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq == 0) {
+ dev_err(&pdev->dev, "failed to get interrupt resource.\n");
+ ret = -EINVAL;
+ goto free_host;
+ }
+
+ ret = clk_prepare_enable(host->core_clk);
+ if (ret)
+ goto free_host;
+
+ ret = meson_mmc_clk_init(host);
+ if (ret)
+ goto free_host;
+
+ /* Stop execution */
+ writel(0, host->regs + SD_EMMC_START);
+
+ /* clear, ack, enable all interrupts */
+ writel(0, host->regs + SD_EMMC_IRQ_EN);
+ writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
+
+ ret = devm_request_threaded_irq(&pdev->dev, host->irq,
+ meson_mmc_irq, meson_mmc_irq_thread,
+ IRQF_SHARED, DRIVER_NAME, host);
+ if (ret)
+ goto free_host;
+
+ /* data bounce buffer */
+ host->bounce_buf_size = SZ_512K;
+ host->bounce_buf =
+ dma_alloc_coherent(host->dev, host->bounce_buf_size,
+ &host->bounce_dma_addr, GFP_KERNEL);
+ if (host->bounce_buf == NULL) {
+ dev_err(host->dev, "Unable to map allocate DMA bounce buffer.\n");
+ ret = -ENOMEM;
+ goto free_host;
+ }
+
+ mmc->ops = &meson_mmc_ops;
+ mmc_add_host(mmc);
+
+ return 0;
+
+free_host:
+ dev_dbg(host->dev, "Failed to probe: ret=%d\n", ret);
+ if (host->core_clk)
+ clk_disable_unprepare(host->core_clk);
+ mmc_free_host(mmc);
+ return ret;
+}
+
+static int meson_mmc_remove(struct platform_device *pdev)
+{
+ struct meson_host *host = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!host))
+ return 0;
+
+ if (host->bounce_buf)
+ dma_free_coherent(host->dev, host->bounce_buf_size,
+ host->bounce_buf, host->bounce_dma_addr);
+
+ if (host->cfg_div_clk)
+ clk_disable_unprepare(host->cfg_div_clk);
+
+ if (host->core_clk)
+ clk_disable_unprepare(host->core_clk);
+
+ mmc_free_host(host->mmc);
+ return 0;
+}
+
+static const struct of_device_id meson_mmc_of_match[] = {
+ {
+ .compatible = "amlogic,meson-gxbb-mmc",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, meson_mmc_of_match);
+
+static struct platform_driver meson_mmc_driver = {
+ .probe = meson_mmc_probe,
+ .remove = meson_mmc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(meson_mmc_of_match),
+ },
+};
+
+module_platform_driver(meson_mmc_driver);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_DESCRIPTION("Amlogic S905/GXBB SD/eMMC driver");
+MODULE_AUTHOR("Kevin Hilman <khilman@baylibre.com>");
+MODULE_LICENSE("GPL v2");
+
--
2.9.3
^ permalink raw reply related
* [PATCH v3 0/2] MMC: add driver for Amlogic GXBB SoCs
From: Kevin Hilman @ 2016-09-14 0:43 UTC (permalink / raw)
To: linux-arm-kernel
Here's an updated series for the Amlogic MMC driver.
Changes since v2:
- rework voltage regulator support
- drop read/write accessor macros
- drop descriptor struct (had endian issues)
- fix big-endian support, and tested big-endian build
This version is included in the v4.8/integ branch of my git tree
https://git.kernel.org/cgit/linux/kernel/git/khilman/linux-amlogic.git/
Also some RFC quetsions:
I think this is the exact same IP on the GXL family of SoC, so I'm
thinking about renaming all occurances of GXBB to just GX. We can
have compatibles to match meson-gxbb and meson-gxl etc. though.
Thoughts?
Kevin Hilman (2):
MMC: meson: initial support for GXBB platforms
ARM64: dts: meson-gxbb: add MMC support
.../devicetree/bindings/mmc/amlogic,meson-gxbb.txt | 29 +
MAINTAINERS | 1 +
.../arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 87 +++
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 78 ++
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 37 +-
drivers/mmc/host/Kconfig | 10 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/meson-gxbb.c | 838 +++++++++++++++++++++
8 files changed, 1080 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/mmc/amlogic,meson-gxbb.txt
create mode 100644 drivers/mmc/host/meson-gxbb.c
--
2.9.3
^ permalink raw reply
* [PATCH v3 0/2] MMC: add driver for Amlogic GXBB SoCs
From: Kevin Hilman @ 2016-09-14 0:42 UTC (permalink / raw)
To: linux-arm-kernel
Here's an updated series for the Amlogic MMC driver.
Changes since v2:
- rework voltage regulator support
- drop read/write accessor macros
- drop descriptor struct (had endian issues)
- fix big-endian support, and tested big-endian build
This version is included in the v4.8/integ branch of my git tree
https://git.kernel.org/cgit/linux/kernel/git/khilman/linux-amlogic.git/
Also some RFC quetsions:
I think this is the exact same IP on the GXL family of SoC, so I'm
thinking about renaming all occurances of GXBB to just GX. We can
have compatibles to match meson-gxbb and meson-gxl etc. though.
Thoughts?
Kevin Hilman (2):
MMC: meson: initial support for GXBB platforms
ARM64: dts: meson-gxbb: add MMC support
.../devicetree/bindings/mmc/amlogic,meson-gxbb.txt | 29 +
MAINTAINERS | 1 +
.../arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 87 +++
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 78 ++
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 37 +-
drivers/mmc/host/Kconfig | 10 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/meson-gxbb.c | 838 +++++++++++++++++++++
8 files changed, 1080 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/mmc/amlogic,meson-gxbb.txt
create mode 100644 drivers/mmc/host/meson-gxbb.c
--
2.9.3
^ permalink raw reply
* [RFC/PATCH] usb: chipidea: Emulate OTGSC interrupt enable path
From: Stephen Boyd @ 2016-09-14 0:00 UTC (permalink / raw)
To: linux-arm-kernel
In the case of an extcon-usb-gpio device being used with the
chipidea driver we'll sometimes miss the BSVIS event in the OTGSC
register. Consider the case where we don't have a cable attached
and the id pin is indicating "host" mode. When we plug in the usb
cable for "device" mode a gpio goes high and indicates that we
should do the role switch and that vbus is high. When we're in
"host" mode the OTGSC register doesn't have BSVIE set.
The following scenario can happen:
CPU0
----
<extcon notifier chain>
ci_cable_notifier()
update id cable state
ci_irq()
if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { // true
ci->id_event = true;
ci_otg_queue_work()
schedule()
<extcon notifier event> // same task as before
ci_cable_notifier()
update vbus cable state
ci_irq()
if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) // false
return IRQ_NONE
ci_otg_work() // switch task to the workqueue now
if (ci->id_event)
ci_handle_id_switch()
ci_role_stop()
host_stop()
hw_wait_vbus_lower_bsv(ci); // this times out because vbus is already set
ci_role_start()
udc_id_switch_for_device()
hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, OTGSC_BSVIS | OTGSC_BSVIE);
At this point, we don't replay the vbus connect event because the
vbus event has already happened. This causes things like gadget
instances to never see vbus appear, and thus the gadget is never
started. Furthermore, we see timeout messages like:
timeout waiting for 0000800 in OTGSC
Let's workaround this by skiping the wait for BSV when we're
using an extcon for the vbus notification and let's properly
emulate the BSVIS event that would happen when we enable the
vbus interrupt while enabling "device" mode.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
This is on top of my patch series that modifies how we handle the extcon
events here. The extcon handling looks racy in this driver even after
this patch. I think we may need to add a spinlock around the cable
state changes and the otgsc register read/write functions. The driver
doesn't seem to expect that the extcon notifiers could run in parallel
with the OTG state machine, and emulating the interrupts is "weird"
in the sense that most of the irq handler in core.c assumes that there's
only one interrupt and so we couldn't possibly be in the irq handler
at the same time on different CPUs (which it can!).
Also, can we just remove the BSV waiting part? From what I can tell, that
is racy if someone can get the workqueue to be delayed significantly enough
to have vbus go high (again) during the role switch. I understand that we're
doing it to prevent a vbus event from happening when we switch to the device
role even though there isn't a cable attached. It just doesn't seem like it's
safe to assume a high-low-high transition won't happen and be reflected in
the status bits. It's really easy to trigger this with an extcon-usb-gpio device
like can be found on 96boards platforms like db410c.
drivers/usb/chipidea/ci.h | 2 ++
drivers/usb/chipidea/core.c | 23 +++++++++++++++++------
drivers/usb/chipidea/otg.c | 31 ++++++++++++++++++++++++-------
3 files changed, 43 insertions(+), 13 deletions(-)
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 59e22389c10b..e099b8bc79e2 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -437,6 +437,8 @@ static inline void ci_ulpi_exit(struct ci_hdrc *ci) { }
static inline int ci_ulpi_resume(struct ci_hdrc *ci) { return 0; }
#endif
+irqreturn_t __ci_irq(int irq, struct ci_hdrc *ci);
+
u32 hw_read_intr_enable(struct ci_hdrc *ci);
u32 hw_read_intr_status(struct ci_hdrc *ci);
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index ba3a43bbe0ea..fbef1c961572 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -528,9 +528,8 @@ int hw_device_reset(struct ci_hdrc *ci)
return 0;
}
-static irqreturn_t ci_irq(int irq, void *data)
+irqreturn_t __ci_irq(int irq, struct ci_hdrc *ci)
{
- struct ci_hdrc *ci = data;
irqreturn_t ret = IRQ_NONE;
u32 otgsc = 0;
@@ -574,9 +573,20 @@ static irqreturn_t ci_irq(int irq, void *data)
return IRQ_HANDLED;
}
- /* Handle device/host interrupt */
- if (ci->role != CI_ROLE_END)
- ret = ci_role(ci)->irq(ci);
+ return ret;
+}
+
+static irqreturn_t ci_irq(int irq, void *data)
+{
+ irqreturn_t ret;
+ struct ci_hdrc *ci = data;
+
+ ret = __ci_irq(irq, ci);
+ if (ret == IRQ_NONE) {
+ /* Handle device/host interrupt */
+ if (ci->role != CI_ROLE_END)
+ ret = ci_role(ci)->irq(ci);
+ }
return ret;
}
@@ -590,7 +600,8 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
cbl->connected = event;
cbl->changed = true;
- ci_irq(ci->irq, ci);
+ __ci_irq(ci->irq, ci);
+
return NOTIFY_DONE;
}
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 695f3fe3ae21..4420428c0bfd 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -84,36 +84,44 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
{
struct ci_hdrc_cable *cable;
+ bool raise_irq = false;
cable = &ci->platdata->vbus_extcon;
if (!IS_ERR(cable->edev)) {
- if (data & mask & OTGSC_BSVIS)
- cable->changed = false;
-
/* Don't enable vbus interrupt if using external notifier */
if (data & mask & OTGSC_BSVIE) {
+ if (cable->enabled == false && cable->changed == true)
+ raise_irq = true;
cable->enabled = true;
data &= ~OTGSC_BSVIE;
} else if (mask & OTGSC_BSVIE) {
cable->enabled = false;
}
+
+ if (data & mask & OTGSC_BSVIS)
+ cable->changed = false;
}
cable = &ci->platdata->id_extcon;
if (!IS_ERR(cable->edev)) {
- if (data & mask & OTGSC_IDIS)
- cable->changed = false;
-
/* Don't enable id interrupt if using external notifier */
if (data & mask & OTGSC_IDIE) {
+ if (cable->enabled == false && cable->changed == true)
+ raise_irq = true;
cable->enabled = true;
data &= ~OTGSC_IDIE;
} else if (mask & OTGSC_IDIE) {
cable->enabled = false;
}
+
+ if (data & mask & OTGSC_IDIS)
+ cable->changed = false;
}
hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data);
+
+ if (raise_irq)
+ __ci_irq(ci->irq, ci);
}
/**
@@ -175,7 +183,16 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
ci_role_stop(ci);
- if (role == CI_ROLE_GADGET)
+ /*
+ * BSV could be set "immediately" if we're using extcon for
+ * VBUS because sometimes it's a single GPIO for ID and VBUS
+ * like in the case of extcon-usb-gpio. In that case we ignore
+ * waiting for a BSV transition. Really we can't tell when BSV
+ * is low and the cable is connected, all we know is that the
+ * BSV is high when we update BSV state.
+ */
+ if (role == CI_ROLE_GADGET &&
+ IS_ERR(ci->platdata->vbus_extcon.edev))
/*
* wait vbus lower than OTGSC_BSV before connecting
* to host
--
2.9.0.rc2.8.ga28705d
^ permalink raw reply related
* [PATCH 1/2] PCI: save and restore device state during bus reset
From: Sinan Kaya @ 2016-09-13 23:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <46a47bf2-ab39-0ee2-0faf-2f4050f2a89d@codeaurora.org>
On 9/13/2016 7:20 PM, Sinan Kaya wrote:
> On 9/13/2016 5:53 PM, Bjorn Helgaas wrote:
>>> + pci_bus_restore(dev->bus);
>> This path eventually writes the Bridge Control register:
>>
>> pci_reset_bridge_secondary_bus
>> pcibios_reset_secondary_bus
>> pci_reset_secondary_bus
>> pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl)
>>
>> But I think it'd be easy to call this on a non-bridge device, and I
>> don't think there's anything in the path that checks whether this is
>> actually a bridge. I wonder if we should check that somewhere, or
>> maybe even change the interface so it takes a struct pci_bus instead
>> of a pci_dev.
>>
>
> I'll change it to pci_bus.
>
>>>> +}
>
>
Changing the API seems to become a bigger mess. pcibios_reset_secondary_bus has
weaker friends in different architectures.
I can move the check in pci_parent_bus_reset
if (pci_is_root_bus(dev->bus) || dev->subordinate ||
!dev->bus->self || dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)
return -ENOTTY;
inside pci_reset_bridge_secondary_bus instead.
--
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH V5] perf tools: adding support for address filters
From: Masami Hiramatsu @ 2016-09-13 23:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CANLsYkz_rQ5uhJQ0x9EDi6vcx4xHrbjjSdwxA=X1ssHq4ucALQ@mail.gmail.com>
On Tue, 13 Sep 2016 08:18:10 -0600
Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
> On 13 September 2016 at 04:01, Adrian Hunter <adrian.hunter@intel.com> wrote:
> > On 12/09/16 20:53, Mathieu Poirier wrote:
> >> This patch makes it possible to use the current filter
> >> framework with address filters. That way address filters for
> >> HW tracers such as CoreSight and IntelPT can be communicated
> >> to the kernel drivers.
> >>
> >> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>
> >> ---
> >> Changes for V5:
> >> - Modified perf_evsel__append_filter() to take a string format
> >> rather than an operation.
> >
> > Hope I'm not being a pain, but aren't there other places calling
> > perf_evsel__append_filter() that need to be changed. Might make
> > sense as a separate patch.
>
> No no, you're right - I completely overlooked that.
>
> But shouldn't it be in the same patch? That way a git bisect would
> stay consistent...
You're right. Caller and callee should be changed in atomic.
BTW, could you add document updates how the perf command line
will be changed, and also show the result in the patch description?
Thank you,
>
> >
> >>
> >> Changes for V4:
> >> - Added support for address filters over more than one
> >> nibble.
> >> - Removed Jiri's ack, this version is too different from
> >> what was reviewed.
> >>
> >> Changes for V3:
> >> - Added Jiri's ack.
> >> - Rebased to v4.8-rc5.
> >>
> >> Changes for V2:
> >> - Rebased to v4.8-rc4.
> >> - Revisited error path.
> >>
> >>
> >> tools/perf/util/evsel.c | 4 ++--
> >> tools/perf/util/evsel.h | 2 +-
> >> tools/perf/util/parse-events.c | 40 +++++++++++++++++++++++++++++++++++-----
> >> 3 files changed, 38 insertions(+), 8 deletions(-)
> >>
> >> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> >> index d40f852d2de2..a9bb277f221f 100644
> >> --- a/tools/perf/util/evsel.c
> >> +++ b/tools/perf/util/evsel.c
> >> @@ -1047,14 +1047,14 @@ int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter)
> >> }
> >>
> >> int perf_evsel__append_filter(struct perf_evsel *evsel,
> >> - const char *op, const char *filter)
> >> + const char *fmt, const char *filter)
> >> {
> >> char *new_filter;
> >>
> >> if (evsel->filter == NULL)
> >> return perf_evsel__set_filter(evsel, filter);
> >>
> >> - if (asprintf(&new_filter,"(%s) %s (%s)", evsel->filter, op, filter) > 0) {
> >> + if (asprintf(&new_filter, fmt, evsel->filter, filter) > 0) {
> >> free(evsel->filter);
> >> evsel->filter = new_filter;
> >> return 0;
> >> diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
> >> index 8ceb7ebb51f5..50595c8c7207 100644
> >> --- a/tools/perf/util/evsel.h
> >> +++ b/tools/perf/util/evsel.h
> >> @@ -236,7 +236,7 @@ void perf_evsel__set_sample_id(struct perf_evsel *evsel,
> >>
> >> int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter);
> >> int perf_evsel__append_filter(struct perf_evsel *evsel,
> >> - const char *op, const char *filter);
> >> + const char *fmt, const char *filter);
> >> int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
> >> const char *filter);
> >> int perf_evsel__apply_drv_configs(struct perf_evsel *evsel,
> >> diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
> >> index 2eb8b1ed4cc8..8e683979ccd8 100644
> >> --- a/tools/perf/util/parse-events.c
> >> +++ b/tools/perf/util/parse-events.c
> >> @@ -1760,20 +1760,50 @@ foreach_evsel_in_last_glob(struct perf_evlist *evlist,
> >> static int set_filter(struct perf_evsel *evsel, const void *arg)
> >> {
> >> const char *str = arg;
> >> + bool found = false;
> >> + int nr_addr_filters = 0;
> >> + struct perf_pmu *pmu = NULL;
> >>
> >> - if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) {
> >> - fprintf(stderr,
> >> - "--filter option should follow a -e tracepoint option\n");
> >> - return -1;
> >> + if (evsel == NULL)
> >> + goto err;
> >> +
> >> + if (evsel->attr.type == PERF_TYPE_TRACEPOINT) {
> >> + if (perf_evsel__append_filter(evsel,
> >> + "(%s) && (%s)", str) < 0) {
> >> + fprintf(stderr,
> >> + "not enough memory to hold filter string\n");
> >> + return -1;
> >> + }
> >> +
> >> + return 0;
> >> }
> >>
> >> - if (perf_evsel__append_filter(evsel, "&&", str) < 0) {
> >> + while ((pmu = perf_pmu__scan(pmu)) != NULL)
> >> + if (pmu->type == evsel->attr.type) {
> >> + found = true;
> >> + break;
> >> + }
> >> +
> >> + if (found)
> >> + perf_pmu__scan_file(pmu, "nr_addr_filters",
> >> + "%d", &nr_addr_filters);
> >> +
> >> + if (!nr_addr_filters)
> >> + goto err;
> >> +
> >> + if (perf_evsel__append_filter(evsel, "%s,%s", str) < 0) {
> >> fprintf(stderr,
> >> "not enough memory to hold filter string\n");
> >> return -1;
> >> }
> >>
> >> return 0;
> >> +
> >> +err:
> >> + fprintf(stderr,
> >> + "--filter option should follow a -e tracepoint or HW tracer option\n");
> >> +
> >> + return -1;
> >> }
> >>
> >> int parse_filter(const struct option *opt, const char *str,
> >>
> >
--
Masami Hiramatsu <mhiramat@kernel.org>
^ permalink raw reply
* [PATCH 1/2] PCI: save and restore device state during bus reset
From: Sinan Kaya @ 2016-09-13 23:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160913215315.GJ4138@localhost>
On 9/13/2016 5:53 PM, Bjorn Helgaas wrote:
>> + pci_bus_restore(dev->bus);
> This path eventually writes the Bridge Control register:
>
> pci_reset_bridge_secondary_bus
> pcibios_reset_secondary_bus
> pci_reset_secondary_bus
> pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl)
>
> But I think it'd be easy to call this on a non-bridge device, and I
> don't think there's anything in the path that checks whether this is
> actually a bridge. I wonder if we should check that somewhere, or
> maybe even change the interface so it takes a struct pci_bus instead
> of a pci_dev.
>
I'll change it to pci_bus.
>> > +}
--
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH] usb: chipidea: Properly mark little endian descriptors
From: Stephen Boyd @ 2016-09-13 23:06 UTC (permalink / raw)
To: linux-arm-kernel
The DMA descriptors are little endian, and we do a pretty good
job of handling them with the proper le32_to_cpu() markings, but
we don't actually mark them as __le32. This means checkers like
sparse can't easily find new bugs. Let's mark the members of
structures properly and fix the few places where we're missing
conversions.
Cc: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/udc.c | 6 +++---
drivers/usb/chipidea/udc.h | 12 ++++++------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 6acf4dba395e..61a65d1d05f2 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -364,7 +364,7 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
if (hwreq->req.length == 0
|| hwreq->req.length % hwep->ep.maxpacket)
mul++;
- node->ptr->token |= mul << __ffs(TD_MULTO);
+ node->ptr->token |= cpu_to_le32(mul << __ffs(TD_MULTO));
}
temp = (u32) (hwreq->req.dma + hwreq->req.actual);
@@ -503,7 +503,7 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
if (hwreq->req.length == 0
|| hwreq->req.length % hwep->ep.maxpacket)
mul++;
- hwep->qh.ptr->cap |= mul << __ffs(QH_MULT);
+ hwep->qh.ptr->cap |= cpu_to_le32(mul << __ffs(QH_MULT));
}
wmb(); /* synchronize before ep prime */
@@ -530,7 +530,7 @@ static void free_pending_td(struct ci_hw_ep *hwep)
static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep,
struct td_node *node)
{
- hwep->qh.ptr->td.next = node->dma;
+ hwep->qh.ptr->td.next = cpu_to_le32(node->dma);
hwep->qh.ptr->td.token &=
cpu_to_le32(~(TD_STATUS_HALTED | TD_STATUS_ACTIVE));
diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h
index e66df0020bd4..2ecd1174d66c 100644
--- a/drivers/usb/chipidea/udc.h
+++ b/drivers/usb/chipidea/udc.h
@@ -22,11 +22,11 @@
/* DMA layout of transfer descriptors */
struct ci_hw_td {
/* 0 */
- u32 next;
+ __le32 next;
#define TD_TERMINATE BIT(0)
#define TD_ADDR_MASK (0xFFFFFFEUL << 5)
/* 1 */
- u32 token;
+ __le32 token;
#define TD_STATUS (0x00FFUL << 0)
#define TD_STATUS_TR_ERR BIT(3)
#define TD_STATUS_DT_ERR BIT(5)
@@ -36,7 +36,7 @@ struct ci_hw_td {
#define TD_IOC BIT(15)
#define TD_TOTAL_BYTES (0x7FFFUL << 16)
/* 2 */
- u32 page[5];
+ __le32 page[5];
#define TD_CURR_OFFSET (0x0FFFUL << 0)
#define TD_FRAME_NUM (0x07FFUL << 0)
#define TD_RESERVED_MASK (0x0FFFUL << 0)
@@ -45,18 +45,18 @@ struct ci_hw_td {
/* DMA layout of queue heads */
struct ci_hw_qh {
/* 0 */
- u32 cap;
+ __le32 cap;
#define QH_IOS BIT(15)
#define QH_MAX_PKT (0x07FFUL << 16)
#define QH_ZLT BIT(29)
#define QH_MULT (0x0003UL << 30)
#define QH_ISO_MULT(x) ((x >> 11) & 0x03)
/* 1 */
- u32 curr;
+ __le32 curr;
/* 2 - 8 */
struct ci_hw_td td;
/* 9 */
- u32 RESERVED;
+ __le32 RESERVED;
struct usb_ctrlrequest setup;
} __attribute__ ((packed, aligned(4)));
--
2.9.0.rc2.8.ga28705d
^ permalink raw reply related
* [PATCH 2/2] PCI: add CRS support to error handling path
From: Sinan Kaya @ 2016-09-13 22:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160913214709.GI4138@localhost>
On 9/13/2016 5:47 PM, Bjorn Helgaas wrote:
> On Tue, Sep 13, 2016 at 05:04:49PM -0400, Sinan Kaya wrote:
>> On 9/13/2016 4:01 PM, Bjorn Helgaas wrote:
>>> On Thu, Sep 01, 2016 at 07:00:01PM -0400, Sinan Kaya wrote:
>>>> The PCIE spec allows an endpoint device to extend the initialization time
>>>> beyond 1 second by issuing Configuration Request Retry Status (CRS) for a
>>>> vendor ID read request.
>>>>
>>>> This basically means "I'm busy now, please call me back later".
>>>>
>>>> There are two moving parts to CRS support from the SW perspective. One part
>>>> is to determine if CRS is supported or not. The second part is to set the
>>>> CRS visibility register.
>>>>
>>>> As part of the probe, the Linux kernel sets the above two conditions in
>>>> pci_enable_crs function. The kernel is also honoring the returned CRS in
>>>> pci_bus_read_dev_vendor_id function if supported. The function will poll up
>>>> to specified amount of time while endpoint is returning CRS response.
>>>>
>>>> The PCIe spec also allows CRS to be issued during cold, warm, hot and FLR
>>>> resets.
>>>>
>>>> The hot reset is initiated by starting a secondary bus reset. This patch is
>>>> adding vendor ID read immediately after a bus reset so that the
>>>> initialization procedure can be extended by the amount of time endpoint
>>>> requires.
>>>>
>>>> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
>>>> ---
>>>> drivers/pci/pci.c | 39 +++++++++++++++++++++++++++++++++++++++
>>>> 1 file changed, 39 insertions(+)
>>>>
>>>> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
>>>> index b209378..ebd0fc6 100644
>>>> --- a/drivers/pci/pci.c
>>>> +++ b/drivers/pci/pci.c
>>>> @@ -3829,6 +3829,44 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
>>>> return 0;
>>>> }
>>>>
>>>> +/*
>>>> + * Mostly copy paste from pci_walk_bus with the exceptions of hard coded
>>>> + * work and removed locks.
>>>> + */
>>>> +static void pci_bus_probe_crs(struct pci_bus *top)
>>>> +{
>>>> + struct pci_dev *dev;
>>>> + struct pci_bus *bus;
>>>> + struct list_head *next;
>>>> + int retval;
>>>> + u32 l;
>>>> +
>>>> + bus = top;
>>>> + next = top->devices.next;
>>>> + for (;;) {
>>>> + if (next == &bus->devices) {
>>>> + /* end of this bus, go up or finish */
>>>> + if (bus == top)
>>>> + break;
>>>> + next = bus->self->bus_list.next;
>>>> + bus = bus->self->bus;
>>>> + continue;
>>>> + }
>>>> + dev = list_entry(next, struct pci_dev, bus_list);
>>>> + if (dev->subordinate) {
>>>> + /* this is a pci-pci bridge, do its devices next */
>>>> + next = dev->subordinate->devices.next;
>>>> + bus = dev->subordinate;
>>>> + } else
>>>> + next = dev->bus_list.next;
>>>> +
>>>> + retval = pci_bus_read_dev_vendor_id(dev->bus, dev->devfn, &l,
>>>> + 60 * 1000);
>>>> + if (retval)
>>>> + break;
>>>> + }
>>>> +}
>>>
>>> Sigh. Man, this is ugly. Maybe we're locked into the current
>>> strategy and don't really have a choice, but I really don't like it.
>>
>> I can add a locked version of the walkbus API.
>> Then, I can minimize this code to a couple of lines. How does that sound?
>
> I didn't mean that, I meant the whole idea of having to walk the whole
> hierarchy and touch each device. It's sort of like we're enumerating
> things, but not really, so this checking is kinda sorta parallel to
> the enumeration path.
Well, we have to do this to run the CRS algorithm against all the devices
that are issuing the CRS. Hot reset is a broadcast message. There could
be multiple devices issuing a CRS in the tree. We should not start talking
to the device before CRS procedure is finished for the device.
The restore_bus routine blindly assumes that all devices under this tree
are accessible.
However, I take your concern about save and restore. We could potentially
get rid of both save, restore and CRS code and then, try to re-enumerate.
I am afraid that can also interfere with the AER error handling notifications
present in the AER driver. A device driver wouldn't get a chance to quiesce
itself before re-enumeration and can leave dangling threads around.
I'm curious if there is any way we can rescan the bus without involving
the endpoint drivers. If we can do that then this could potentially work.
Re-enumeration can also potentially assign different resources that the endpoint
already has mapped. I see even a bigger problem there.
>
>>> You mentioned several kinds of reset where CRS is allowed. Doesn't this
>>> fix only one of them? I know we support at least FLR reset also.
>>
>> The CRS is for hot reset, warm reset and FLR reset. There is nothing we can do in SW
>> for warm reset. This patch is to address hot reset caused by SBR.
>>
>> I was hoping that Alex would help us for directions on the FLR reset later.
>
> What sort of help from Alex were you hoping for? Is fixing the FLR
> path harder than this one? If we're going to fix one path, I'd prefer
> to fix them all at the same time rather than tripping over this again
> later.
>
I don't mind taking a stab at all paths if possible. Last time I checked,
there is already some code trying to find the endpoint. Alex is on the CC.
he can always review my change.
--
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH v4 2/5] ARM: dts: imx6q: Add Engicam i.CoreM6 Quad/Dual initial support
From: Fabio Estevam @ 2016-09-13 22:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473618641-10159-3-git-send-email-jagan@amarulasolutions.com>
On Sun, Sep 11, 2016 at 3:30 PM, Jagan Teki <jagannadh.teki@gmail.com> wrote:
> + reg_3p3v: regulator-3p3v {
> + compatible = "regulator-fixed";
> + regulator-name = "3P3V";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-boot-on;
> + regulator-always-on;
You should remove " regulator-boot-on" and "regulator-always-on" as
the reg_3p3v regulator is controlled by the can nodes.
^ permalink raw reply
* [PATCH 03/21] ARM: OMAP: wakeupgen: Convert to hotplug state machine
From: Tony Lindgren @ 2016-09-13 22:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160906170457.32393-4-bigeasy@linutronix.de>
* Sebastian Andrzej Siewior <bigeasy@linutronix.de> [160906 10:06]:
> Install the callbacks via the state machine.
Assuming this will get merged with the series:
Acked-by: Tony Lindgren <tony@atomide.com>
If you want me to pick it, please let me know.
Regards,
Tony
^ permalink raw reply
* [v11, 5/8] soc: fsl: add GUTS driver for QorIQ platforms
From: Scott Wood @ 2016-09-13 22:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <HE1PR04MB08892E63EB2D579D6F0A7550F8FE0@HE1PR04MB0889.eurprd04.prod.outlook.com>
On Tue, 2016-09-13 at 07:23 +0000, Y.B. Lu wrote:
> >
> >
> > -----Original Message-----
> > From: linux-mmc-owner at vger.kernel.org [mailto:linux-mmc-
> > owner at vger.kernel.org] On Behalf Of Scott Wood
> > Sent: Tuesday, September 13, 2016 7:25 AM
> > To: Y.B. Lu; linux-mmc at vger.kernel.org; ulf.hansson at linaro.org; Arnd
> > Bergmann
> > Cc: linuxppc-dev at lists.ozlabs.org; devicetree at vger.kernel.org; linux-arm-
> > kernel at lists.infradead.org; linux-kernel at vger.kernel.org; linux-
> > clk at vger.kernel.org; linux-i2c at vger.kernel.org; iommu at lists.linux-
> > foundation.org; netdev at vger.kernel.org; Mark Rutland; Rob Herring;
> > Russell King; Jochen Friedrich; Joerg Roedel; Claudiu Manoil; Bhupesh
> > Sharma; Qiang Zhao; Kumar Gala; Santosh Shilimkar; Leo Li; X.B. Xie
> > Subject: Re: [v11, 5/8] soc: fsl: add GUTS driver for QorIQ platforms
> >
> > BTW, aren't ls2080a and ls2085a the same die? ?And is there no non-E
> > version of LS2080A/LS2040A?
> [Lu Yangbo-B47093] I checked all the svr values in chip errata doc "Revision
> level to part marking cross-reference" table.
> I found ls2080a and ls2085a were in two separate doc. And I didn?t find non-
> E version of LS2080A/LS2040A in chip errata doc.
> Do you know is there any other doc we can confirm this?
No. ?Traditionally we've always had E and non-E versions of each chip, but I
have no knowledge of whether that has changed (I do note that the way that E-
status is indicated in SVR has changed).
But please label LS2080A and LS2085A as the same die (or provide strong
evidence that they are not).
>
> >
> >
> > >
> > > > >
> > > > > + do {
> > > > > + if (!matches->soc_id)
> > > > > + return NULL;
> > > > > + if (glob_match(svr_match, matches->soc_id))
> > > > > + break;
> > > > > + } while (matches++);
> > > > Are you expecting "matches++" to ever evaluate as false?
> > > [Lu Yangbo-B47093] Yes, this is used to match the soc we use in
> > > qoriq_soc array until getting true.
> > > We need to get the name and die information defined in array.
> > I'm not asking whether the glob_match will ever return true. ?I'm saying
> > that "matches++" will never become NULL.
> [Lu Yangbo-B47093] The matches++ will never become NULL while it will return
> NULL after matching for all the members in array.
"matches++" will never "return NULL". ?It's just an incrementing address. ?It
won't be null until you wrap around the address space, and even if the other
loop terminators never kicked in you'd crash long before that happens.
Please rewrite the loop as something like:
while (matches->soc_id) {
if (glob_match(...))
return matches;
matches++;
}
return NULL;
> > > > > + /* Register soc device */
> > > > > + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
> > > > > + if (!soc_dev_attr) {
> > > > > + ret = -ENOMEM;
> > > > > + goto out_unmap;
> > > > > + }
> > > > Couldn't this be statically allocated?
> > > [Lu Yangbo-B47093] Do you mean we define this struct statically ?
> > >
> > > static struct soc_device_attribute soc_dev_attr;
> > Yes.
> >
> [Lu Yangbo-B47093] It's ok to define it statically. Is there any need to do
> that?
It's simpler.
-Scott
^ permalink raw reply
* [PATCH V2 0/2] ARM: dts: Introduce support for rev BeagleBoard-X15 B1
From: Tony Lindgren @ 2016-09-13 22:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160902171445.27757-1-nm@ti.com>
* Nishanth Menon <nm@ti.com> [160902 10:14]:
> Hi,
>
> Please find the series to cleanup and support Production version of
> Beagleboard-X15 rev B1 support.
Applying into omap-for-v4.9/dt thanks.
Tony
^ permalink raw reply
* [PATCH v2 0/3] ARM: dts: Add operating-points-v2 tables for TI platforms
From: Tony Lindgren @ 2016-09-13 22:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160901024816.32390-1-d-gerlach@ti.com>
Hi,
* Dave Gerlach <d-gerlach@ti.com> [160831 19:49]:
> Hi,
> This is v2 of the series to add operating-points-v2 tables for am335x,
> am437x, dra7, and am57x platforms, but really just updates the already
> merged v1 found here [1].
>
> It seems v1 was merged without the binding being finalized so this series
> just updates the nodes that are already present in order to work with the
> new bindings coming in v2 of the ti-cpufreq driver series which will follow
> shortly.
Assuming this will have to wait based on what you mentioned about
am335x cpufreq regression. I guess you'll instead send few patches
to revert?
Regards,
Tony
> [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-May/430205.html
>
> Dave Gerlach (3):
> ARM: dts: am33xx: Change operating-points-v2 table to am3352 version
> ARM: dts: am4372: Change operating-points-v2 table to am4372 version
> ARM: dts: dra7: Change operating-points-v2 table to dra7 version
>
> arch/arm/boot/dts/am33xx.dtsi | 2 +-
> arch/arm/boot/dts/am4372.dtsi | 2 +-
> arch/arm/boot/dts/dra7.dtsi | 2 +-
> 3 files changed, 3 insertions(+), 3 deletions(-)
>
> --
> 2.9.0
>
^ permalink raw reply
* [PATCH 1/2] PCI: save and restore device state during bus reset
From: Bjorn Helgaas @ 2016-09-13 21:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1472770801-30671-1-git-send-email-okaya@codeaurora.org>
On Thu, Sep 01, 2016 at 07:00:00PM -0400, Sinan Kaya wrote:
> A secondary bus reset causes settings to be lost by all downstream
> devices on the tree. The code is currently saving and restoring device
> states only when called from the VFIO path via pci_probe_reset_bus
> and pci_reset_bus functions.
>
> Moving the save and restore into pci_reset_bridge_secondary_bus
> so that all users of the API have the same behavior.
>
> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
> ---
> drivers/pci/pci.c | 36 +++++++++++++++---------------------
> 1 file changed, 15 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index aab9d51..b209378 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -3860,19 +3860,6 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
> pci_reset_secondary_bus(dev);
> }
>
> -/**
> - * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge.
> - * @dev: Bridge device
> - *
> - * Use the bridge control register to assert reset on the secondary bus.
> - * Devices on the secondary bus are left in power-on state.
> - */
> -void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
> -{
> - pcibios_reset_secondary_bus(dev);
> -}
> -EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
> -
> static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
> {
> struct pci_dev *pdev;
> @@ -4362,6 +4349,21 @@ static void pci_slot_restore(struct pci_slot *slot)
> }
> }
>
> +/**
> + * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge.
> + * @dev: Bridge device
> + *
> + * Use the bridge control register to assert reset on the secondary bus.
> + * Devices on the secondary bus are left in power-on state.
> + */
> +void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
> +{
> + pci_bus_save_and_disable(dev->bus);
> + pcibios_reset_secondary_bus(dev);
> + pci_bus_restore(dev->bus);
This path eventually writes the Bridge Control register:
pci_reset_bridge_secondary_bus
pcibios_reset_secondary_bus
pci_reset_secondary_bus
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl)
But I think it'd be easy to call this on a non-bridge device, and I
don't think there's anything in the path that checks whether this is
actually a bridge. I wonder if we should check that somewhere, or
maybe even change the interface so it takes a struct pci_bus instead
of a pci_dev.
> +}
> +EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
> +
> static int pci_slot_reset(struct pci_slot *slot, int probe)
> {
> int rc;
> @@ -4504,12 +4506,8 @@ int pci_reset_bus(struct pci_bus *bus)
> if (rc)
> return rc;
>
> - pci_bus_save_and_disable(bus);
> -
> rc = pci_bus_reset(bus, 0);
>
> - pci_bus_restore(bus);
> -
> return rc;
> }
> EXPORT_SYMBOL_GPL(pci_reset_bus);
> @@ -4528,8 +4526,6 @@ int pci_try_reset_bus(struct pci_bus *bus)
> if (rc)
> return rc;
>
> - pci_bus_save_and_disable(bus);
> -
> if (pci_bus_trylock(bus)) {
> might_sleep();
> pci_reset_bridge_secondary_bus(bus->self);
> @@ -4537,8 +4533,6 @@ int pci_try_reset_bus(struct pci_bus *bus)
> } else
> rc = -EAGAIN;
>
> - pci_bus_restore(bus);
> -
> return rc;
> }
> EXPORT_SYMBOL_GPL(pci_try_reset_bus);
> --
> 1.9.1
>
^ 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