From mboxrd@z Thu Jan 1 00:00:00 1970 From: Tony Lindgren Subject: [PATCH] phy: mapphone-mdm6600: Add USB PHY driver for MDM6600 on Droid 4 Date: Sat, 17 Feb 2018 13:07:23 -0800 Message-ID: <20180217210723.7013-1-tony@atomide.com> Return-path: Sender: linux-usb-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org To: Kishon Vijay Abraham I Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-omap-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Mark Rutland , Marcel Partap , Michael Scott , Rob Herring , Sebastian Reichel List-Id: linux-omap@vger.kernel.org Let's add support for the GPIO controlled USB PHY on the MDM6600 modem. It is used on some Motorola Mapphone series of phones and tablets such as Droid 4. The MDM6600 is hardwired to the first OHCI port in the Droid 4 case, and is controlled by several GPIOs. The USB PHY is integrated into the MDM6600 device it seems. We know this as we get L3 errors from omap-usb-host if trying to use the PHY before MDM6600 is configured. The GPIOs controlling MDM6600 are used to power MDM660 on and off, to configure the USB start-up mode (normal mode versus USB flashing), and they also tell the state of the MDM6600 device. The two start-up mode GPIOs are dual-purposed and used for out of band (OOB) wake-up for USB and TS 27.010 serial mux. But we need to configure the USB start-up mode first to get MDM6600 booted in the right mode to be usable in the first place. For now, this driver just gives up the two start-up mode GPIOs after the modem has been configured to boot in normal mode. One of them we may want to configure for USB OOB wake in this driver later on, but that's a separate series of patches and needs more work. Note that the Motorola Mapphone Linux kernel tree has a "radio-ctrl" driver for modems. But it really does not control the radio at all, it just controls the modem power and start-up mode for USB. So I came to the conclusion that we're better off having this done in the USB PHY driver. For adding support for USB flashing mode, we can later on add a kernel module option for flash_mode=1 or something similar. Also note that currently there is no PM runtime support for the OHCI on omap variant SoCs. So for low(er) power idle states, currenty both ohci-platform and phy-mapphone-mdm6600 must be unloaded or unbound. For reference here is what I measured for total power consumption on an idle Droid 4 with and without USB related MDM6600 modules: idle lcd off phy-mapphone-mdm6600 ohci-platform 153mW 284mW 344mW So it seems that MDM6600 is currently not yet idling even with it's radio turned off, but that's something that is beyond the control of this USB PHY driver. Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org Cc: Mark Rutland Cc: Marcel Partap Cc: Michael Scott Cc: Rob Herring Cc: Sebastian Reichel Signed-off-by: Tony Lindgren --- .../bindings/phy/phy-mapphone-mdm6600.txt | 30 ++ drivers/phy/motorola/Kconfig | 9 + drivers/phy/motorola/Makefile | 1 + drivers/phy/motorola/phy-mapphone-mdm6600.c | 490 +++++++++++++++++++++ 4 files changed, 530 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt create mode 100644 drivers/phy/motorola/phy-mapphone-mdm6600.c diff --git a/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt b/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt new file mode 100644 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt @@ -0,0 +1,30 @@ +Device tree binding documentation for Motorola Mapphone MDM6600 USB PHY + +Required properties: +- compatible Must be "motorola,mapphone-mdm6600" +- enable-gpios GPIO to enable the USB PHY +- power-gpios GPIO to power on the device +- reset-gpios GPIO to reset the device +- mode-gpios Two GPIOs to configure MDM6600 USB start-up mode for + normal mode versus USB flashing mode +- status-gpios Three GPIOs to read the power state of the MDM6600 +- cmd-gpios Three GPIOs to control the power state of the MDM6600 + +Example: + +fsusb1_phy: fsusb1_phy { + compatible = "motorola,mapphone-mdm6600"; + enable-gpios = <&gpio3 31 GPIO_ACTIVE_LOW>; /* gpio_95 */ + power-gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>; /* gpio_54 */ + reset-gpios = <&gpio2 17 GPIO_ACTIVE_HIGH>; /* gpio_49 */ + mode-gpios = <&gpio5 20 GPIO_ACTIVE_HIGH>, /* gpio_148 */ + <&gpio5 21 GPIO_ACTIVE_HIGH>; /* gpio_149 */ + status-gpios = <&gpio2 23 GPIO_ACTIVE_HIGH>, /* gpio_55 */ + <&gpio2 21 GPIO_ACTIVE_HIGH>, /* gpio_53 */ + <&gpio2 20 GPIO_ACTIVE_HIGH>; /* gpio_52 */ + cmd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>, /* gpio_103 */ + <&gpio4 8 GPIO_ACTIVE_HIGH>, /* gpio_104 */ + <&gpio5 14 GPIO_ACTIVE_HIGH>; /* gpio_142 */ + #phy-cells = <0>; +}; + diff --git a/drivers/phy/motorola/Kconfig b/drivers/phy/motorola/Kconfig --- a/drivers/phy/motorola/Kconfig +++ b/drivers/phy/motorola/Kconfig @@ -10,3 +10,12 @@ config PHY_CPCAP_USB help Enable this for USB to work on Motorola phones and tablets such as Droid 4. + +config PHY_MAPPHONE_MDM6600 + tristate "Motorola Mapphone MDM6600 modem USB PHY driver" + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this for MDM6600 USB modem to work on Motorola phones + and tablets such as Droid 4. diff --git a/drivers/phy/motorola/Makefile b/drivers/phy/motorola/Makefile --- a/drivers/phy/motorola/Makefile +++ b/drivers/phy/motorola/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_PHY_CPCAP_USB) += phy-cpcap-usb.o +obj-$(CONFIG_PHY_MAPPHONE_MDM6600) += phy-mapphone-mdm6600.o diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c new file mode 100644 --- /dev/null +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Motorola Mapphone MDM6600 modem GPIO controlled USB PHY driver + * Copyright (C) 2018 Tony Lindgren + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PHY_MDM6600_STARTUP_DELAY_MS 3000 /* A least 2.2s usually */ + +/* + * MDM6600 status codes. These are copied from Motorola Mapphone Linux + * kernel tree. The BB naming here refers to "BaseBand" for modem. + */ +enum phy_mdm6600_status { + BP_STATUS_PANIC, /* Seems to be really off state */ + BP_STATUS_PANIC_BUSY_WAIT, + BP_STATUS_QC_DLOAD, + BP_STATUS_RAM_DOWNLOADER, /* MDM6600 USB flashing mode */ + BP_STATUS_PHONE_CODE_AWAKE, /* MDM6600 normal USB mode */ + BP_STATUS_PHONE_CODE_ASLEEP, + BP_STATUS_SHUTDOWN_ACK, + BP_STATUS_UNDEFINED, +}; + +static const char * const +phy_mdm6600_status_name[] = { + "off", "busy", "qc_dl", "ram_dl", "awake", + "asleep", "shutdown", "undefined", +}; + +/* + * MDM6600 command codes. These are copied from Motorola Mapphone Linux + * kernel tree. The AP naming here refers to "Application Processor". + */ +enum phy_mdm6600_cmd { + AP_STATUS_BP_PANIC_ACK, + AP_STATUS_DATA_ONLY_BYPASS, /* Reroute USB to CPCAP PHY */ + AP_STATUS_FULL_BYPASS, /* Reroute USB to CPCAP PHY */ + AP_STATUS_NO_BYPASS, /* Request normal start-up mode */ + AP_STATUS_BP_SHUTDOWN_REQ, /* Request device power off */ + AP_STATUS_BP_UNKNOWN_5, + AP_STATUS_BP_UNKNOWN_6, + AP_STATUS_UNDEFINED, +}; + +enum phy_mdm6600_lines { + PHY_MDM6600_ENABLE, /* USB PHY enable */ + PHY_MDM6600_POWER, /* Device power */ + PHY_MDM6600_RESET, /* Device reset */ + PHY_MDM6600_MODE0, /* USB boot mode flashing vs normal */ + PHY_MDM6600_MODE1, /* USB boot mode flashing vs normal */ + PHY_MDM6600_STATUS0, /* Device state */ + PHY_MDM6600_STATUS1, /* Device state */ + PHY_MDM6600_STATUS2, /* Device state */ + PHY_MDM6600_CMD0, /* Device command */ + PHY_MDM6600_CMD1, /* Device command */ + PHY_MDM6600_CMD2, /* Device command */ + PHY_MDM6600_NR_LINES, +}; + +struct phy_mdm6600 { + struct device *dev; + struct usb_phy phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct gpio_desc *gpio[PHY_MDM6600_NR_LINES]; + struct delayed_work bootup_work; + struct delayed_work status_work; + struct completion ack; + bool enabled; + int status; +}; + +static int phy_mdm6600_init(struct phy *x) +{ + struct phy_mdm6600 *ddata = phy_get_drvdata(x); + struct gpio_desc *enable_gpio = ddata->gpio[PHY_MDM6600_ENABLE]; + + if (!ddata->enabled) + return -EPROBE_DEFER; + + gpiod_set_value_cansleep(enable_gpio, 0); + + return 0; +} + +static int phy_mdm6600_power_on(struct phy *x) +{ + struct phy_mdm6600 *ddata = phy_get_drvdata(x); + struct gpio_desc *enable_gpio = ddata->gpio[PHY_MDM6600_ENABLE]; + + if (!ddata->enabled) + return -ENODEV; + + gpiod_set_value_cansleep(enable_gpio, 1); + + return 0; +} + +static int phy_mdm6600_power_off(struct phy *x) +{ + struct phy_mdm6600 *ddata = phy_get_drvdata(x); + struct gpio_desc *enable_gpio = ddata->gpio[PHY_MDM6600_ENABLE]; + + if (!ddata->enabled) + return -ENODEV; + + gpiod_set_value_cansleep(enable_gpio, 0); + + return 0; +} + +static const struct phy_ops gpio_usb_ops = { + .init = phy_mdm6600_init, + .power_on = phy_mdm6600_power_on, + .power_off = phy_mdm6600_power_off, + .owner = THIS_MODULE, +}; + +struct phy_mdm6600_map { + const char *name; + int nr_gpios; + int direction; +}; + +static const struct phy_mdm6600_map +phy_mdm6600_line_map[PHY_MDM6600_NR_LINES] = { + { "enable", 1, GPIOD_OUT_LOW, }, /* low = disabled */ + { "power", 1, GPIOD_OUT_LOW, }, /* low = off */ + { "reset", 1, GPIOD_OUT_HIGH, }, /* high = reset */ + { "mode", 2, GPIOD_OUT_LOW, }, + { "status", 3, GPIOD_IN, }, + { "cmd", 3, GPIOD_OUT_LOW, }, /* low = no command */ +}; + +/** + * phy_mdm6600_cmd() - send a command request to mdm6600 + * @ddata: device driver data + * + * Configures the three command request GPIOs to the specified value. + */ +static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) +{ + int i; + + val &= 0x7; + + for (i = PHY_MDM6600_CMD0; + i <= PHY_MDM6600_CMD2; i++) { + struct gpio_desc *gpio = ddata->gpio[i]; + int shift = (2 - (i - PHY_MDM6600_CMD0)); + + if (IS_ERR(gpio)) + return; + + gpiod_set_value_cansleep(gpio, (val & BIT(shift)) >> shift); + } +} + +/** + * phy_mdm6600_status() - read mdm6600 status lines + * @ddata: device driver data + */ +static void phy_mdm6600_status(struct work_struct *work) +{ + struct phy_mdm6600 *ddata; + struct device *dev; + int i, val; + + ddata = container_of(work, struct phy_mdm6600, status_work.work); + dev = ddata->dev; + + for (i = PHY_MDM6600_STATUS0; + i <= PHY_MDM6600_STATUS2; i++) { + struct gpio_desc *gpio = ddata->gpio[i]; + int shift = (2 - (i - PHY_MDM6600_STATUS0)); + + if (IS_ERR(ddata->gpio[i])) + continue; + val = gpiod_get_value_cansleep(gpio); + ddata->status &= ~(BIT(shift)); + ddata->status |= (val << shift); + } + dev_info(dev, "modem status: %i %s\n", + ddata->status, + phy_mdm6600_status_name[ddata->status & 7]); + complete(&ddata->ack); +} + +static irqreturn_t phy_mdm6600_irq_thread(int irq, void *data) +{ + struct phy_mdm6600 *ddata = data; + + schedule_delayed_work(&ddata->status_work, msecs_to_jiffies(10)); + + return IRQ_HANDLED; +} + +/** + * phy_mdm6600_init_irq() - initialize mdm6600 status IRQ lines + * @ddata: device driver data + */ +static void phy_mdm6600_init_irq(struct phy_mdm6600 *ddata) +{ + struct device *dev = ddata->dev; + int i, error, irq; + + for (i = PHY_MDM6600_STATUS0; + i <= PHY_MDM6600_STATUS2; i++) { + if (IS_ERR(ddata->gpio[i])) + continue; + + irq = gpiod_to_irq(ddata->gpio[i]); + if (irq <= 0) + continue; + + error = devm_request_threaded_irq(dev, irq, NULL, + phy_mdm6600_irq_thread, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "mdm6600", + ddata); + if (error) + dev_warn(dev, "no modem status irq%i: %i\n", + irq, error); + } +} + +/** + * phy_mdm6600_init_lines() - initialize mdm6600 GPIO lines + * @ddata: device driver data + */ +static int phy_mdm6600_init_lines(struct phy_mdm6600 *ddata) +{ + struct device *dev = ddata->dev; + int i, j, nr_gpio = 0; + + for (i = 0; i < ARRAY_SIZE(phy_mdm6600_line_map); i++) { + const struct phy_mdm6600_map *map = + &phy_mdm6600_line_map[i]; + + for (j = 0; j < map->nr_gpios; j++) { + struct gpio_desc **gpio = &ddata->gpio[nr_gpio]; + + *gpio = devm_gpiod_get_index(dev, + map->name, j, + map->direction); + if (IS_ERR(*gpio)) { + dev_info(dev, + "gpio %s error %li, already taken?\n", + map->name, PTR_ERR(*gpio)); + return PTR_ERR(*gpio); + } + nr_gpio++; + } + } + + return 0; +} + +/** + * phy_mdm6600_device_power_on() - power on mdm6600 device + * @ddata: device driver data + * + * To get the integrated USB phy in MDM6600 takes some hoops. We must ensure + * the shared USB bootmode GPIOs are configured, then request modem start-up, + * reset and power-up.. And then we need to give up the shared USB bootmode + * GPIOs as they are also used for Out of Band (OOB) wake for the USB and + * TS 27.010 serial mux. + */ +static int phy_mdm6600_device_power_on(struct phy_mdm6600 *ddata) +{ + struct gpio_desc *mode_gpio0 = ddata->gpio[PHY_MDM6600_MODE0]; + struct gpio_desc *mode_gpio1 = ddata->gpio[PHY_MDM6600_MODE1]; + struct gpio_desc *reset_gpio = ddata->gpio[PHY_MDM6600_RESET]; + struct gpio_desc *power_gpio = ddata->gpio[PHY_MDM6600_POWER]; + int error = 0; + + /* + * Shared GPIOs must be low for normal USB mode. After booting, + * we don't need them. These can be also used to configure USB + * flashing mode later on based on a module parameter. + */ + gpiod_set_value_cansleep(mode_gpio0, 0); + gpiod_set_value_cansleep(mode_gpio1, 0); + + /* Request start-up mode */ + phy_mdm6600_cmd(ddata, AP_STATUS_NO_BYPASS); + + /* Request a reset first */ + gpiod_set_value_cansleep(reset_gpio, 0); + msleep(100); + + /* Toggle power GPIO to request mdm6600 to start */ + gpiod_set_value_cansleep(power_gpio, 1); + msleep(100); + gpiod_set_value_cansleep(power_gpio, 0); + + /* + * Looks like the USB PHY is at least 2.2 seconds. + * If we try to use it before that, we will get L3 errors + * from omap-usb-host trying to access the PHY. See also + * phy_mdm6600_init() for -EPROBE_DEFER. + */ + msleep(PHY_MDM6600_STARTUP_DELAY_MS); + ddata->enabled = true; + + /* Booting up the rest of MDM6600 will take total about 8 seconds */ + dev_info(ddata->dev, "Waiting for power up request to complete..\n"); + if (wait_for_completion_timeout(&ddata->ack, + msecs_to_jiffies(8000))) { + dev_info(ddata->dev, "Powered up OK\n"); + } else { + ddata->enabled = false; + error = -ETIMEDOUT; + dev_err(ddata->dev, "Timed out powering up\n"); + } + + /* Give up shared GPIOs now, they will be used for OOB wake */ + devm_gpiod_put(ddata->dev, mode_gpio0); + ddata->gpio[PHY_MDM6600_MODE0] = ERR_PTR(-ENODEV); + devm_gpiod_put(ddata->dev, mode_gpio1); + ddata->gpio[PHY_MDM6600_MODE0] = ERR_PTR(-ENODEV); + + return error; +} + +/** + * phy_mdm6600_device_power_off() - power off mdm6600 device + * @ddata: device driver data + */ +static void phy_mdm6600_device_power_off(struct phy_mdm6600 *ddata) +{ + struct gpio_desc *reset_gpio = + ddata->gpio[PHY_MDM6600_RESET]; + + ddata->enabled = false; + phy_mdm6600_cmd(ddata, AP_STATUS_BP_SHUTDOWN_REQ); + msleep(100); + + if (reset_gpio >= 0) + gpiod_set_value_cansleep(reset_gpio, 1); + + dev_info(ddata->dev, "Waiting for power down request to complete.. "); + if (wait_for_completion_timeout(&ddata->ack, + msecs_to_jiffies(5000))) { + dev_info(ddata->dev, "Powered down OK\n"); + } else { + dev_err(ddata->dev, "Timed out powering down\n"); + } +} + +static void phy_mdm6600_deferred_power_on(struct work_struct *work) +{ + struct phy_mdm6600 *ddata; + int error; + + ddata = container_of(work, struct phy_mdm6600, bootup_work.work); + + error = phy_mdm6600_device_power_on(ddata); + if (error) + dev_err(ddata->dev, "Device not functional\n"); +} + +#ifdef CONFIG_OF +static const struct of_device_id phy_mdm6600_id_table[] = { + { .compatible = "motorola,mapphone-mdm6600", }, + {}, +}; +MODULE_DEVICE_TABLE(of, phy_mdm6600_id_table); +#endif + +static int phy_mdm6600_probe(struct platform_device *pdev) +{ + struct phy_mdm6600 *ddata; + struct usb_otg *otg; + const struct of_device_id *of_id; + int error; + + of_id = of_match_device(of_match_ptr(phy_mdm6600_id_table), + &pdev->dev); + if (!of_id) + return -EINVAL; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + INIT_DELAYED_WORK(&ddata->bootup_work, + phy_mdm6600_deferred_power_on); + INIT_DELAYED_WORK(&ddata->status_work, phy_mdm6600_status); + init_completion(&ddata->ack); + + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + ddata->dev = &pdev->dev; + ddata->phy.dev = ddata->dev; + ddata->phy.label = "phy_mdm6600"; + ddata->phy.otg = otg; + ddata->phy.type = USB_PHY_TYPE_USB2; + otg->usb_phy = &ddata->phy; + + platform_set_drvdata(pdev, ddata); + + error = phy_mdm6600_init_lines(ddata); + if (error) + return error; + + phy_mdm6600_init_irq(ddata); + + ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops); + if (IS_ERR(ddata->generic_phy)) { + error = PTR_ERR(ddata->generic_phy); + goto cleanup; + } + + phy_set_drvdata(ddata->generic_phy, ddata); + + ddata->phy_provider = + devm_of_phy_provider_register(ddata->dev, + of_phy_simple_xlate); + if (IS_ERR(ddata->phy_provider)) { + error = PTR_ERR(ddata->phy_provider); + goto cleanup; + } + + schedule_delayed_work(&ddata->bootup_work, 0); + + /* + * See phy_mdm6600_device_power_on(). We should be able + * to remove this eventually when ohci-platform can deal + * with -EPROBE_DEFER. + */ + msleep(PHY_MDM6600_STARTUP_DELAY_MS + 500); + + usb_add_phy_dev(&ddata->phy); + + return 0; + +cleanup: + phy_mdm6600_device_power_off(ddata); + return error; +} + +static int phy_mdm6600_remove(struct platform_device *pdev) +{ + struct phy_mdm6600 *ddata = platform_get_drvdata(pdev); + struct gpio_desc *reset_gpio = ddata->gpio[PHY_MDM6600_RESET]; + + if (!IS_ERR(reset_gpio)) + gpiod_set_value_cansleep(reset_gpio, 1); + + phy_mdm6600_device_power_off(ddata); + + cancel_delayed_work_sync(&ddata->bootup_work); + cancel_delayed_work_sync(&ddata->status_work); + + return 0; +} + +static struct platform_driver phy_mdm6600_driver = { + .probe = phy_mdm6600_probe, + .remove = phy_mdm6600_remove, + .driver = { + .name = "phy-mapphone-mdm6600", + .of_match_table = of_match_ptr(phy_mdm6600_id_table), + }, +}; + +module_platform_driver(phy_mdm6600_driver); + +MODULE_ALIAS("platform:gpio_usb"); +MODULE_AUTHOR("Tony Lindgren "); +MODULE_DESCRIPTION("generic gpio usb phy driver"); +MODULE_LICENSE("GPL v2"); -- 2.16.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: phy: mapphone-mdm6600: Add USB PHY driver for MDM6600 on Droid 4 From: Tony Lindgren Message-Id: <20180217210723.7013-1-tony@atomide.com> Date: Sat, 17 Feb 2018 13:07:23 -0800 To: Kishon Vijay Abraham I Cc: linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, linux-omap@vger.kernel.org, devicetree@vger.kernel.org, Mark Rutland , Marcel Partap , Michael Scott , Rob Herring , Sebastian Reichel List-ID: TGV0J3MgYWRkIHN1cHBvcnQgZm9yIHRoZSBHUElPIGNvbnRyb2xsZWQgVVNCIFBIWSBvbiB0aGUg TURNNjYwMCBtb2RlbS4KSXQgaXMgdXNlZCBvbiBzb21lIE1vdG9yb2xhIE1hcHBob25lIHNlcmll cyBvZiBwaG9uZXMgYW5kIHRhYmxldHMgc3VjaAphcyBEcm9pZCA0LgoKVGhlIE1ETTY2MDAgaXMg aGFyZHdpcmVkIHRvIHRoZSBmaXJzdCBPSENJIHBvcnQgaW4gdGhlIERyb2lkIDQgY2FzZSwgYW5k CmlzIGNvbnRyb2xsZWQgYnkgc2V2ZXJhbCBHUElPcy4gVGhlIFVTQiBQSFkgaXMgaW50ZWdyYXRl ZCBpbnRvIHRoZSBNRE02NjAwCmRldmljZSBpdCBzZWVtcy4gV2Uga25vdyB0aGlzIGFzIHdlIGdl dCBMMyBlcnJvcnMgZnJvbSBvbWFwLXVzYi1ob3N0IGlmCnRyeWluZyB0byB1c2UgdGhlIFBIWSBi ZWZvcmUgTURNNjYwMCBpcyBjb25maWd1cmVkLgoKVGhlIEdQSU9zIGNvbnRyb2xsaW5nIE1ETTY2 MDAgYXJlIHVzZWQgdG8gcG93ZXIgTURNNjYwIG9uIGFuZCBvZmYsIHRvCmNvbmZpZ3VyZSB0aGUg VVNCIHN0YXJ0LXVwIG1vZGUgKG5vcm1hbCBtb2RlIHZlcnN1cyBVU0IgZmxhc2hpbmcpLCBhbmQK dGhleSBhbHNvIHRlbGwgdGhlIHN0YXRlIG9mIHRoZSBNRE02NjAwIGRldmljZS4KClRoZSB0d28g c3RhcnQtdXAgbW9kZSBHUElPcyBhcmUgZHVhbC1wdXJwb3NlZCBhbmQgdXNlZCBmb3Igb3V0IG9m IGJhbmQKKE9PQikgd2FrZS11cCBmb3IgVVNCIGFuZCBUUyAyNy4wMTAgc2VyaWFsIG11eC4gQnV0 IHdlIG5lZWQgdG8gY29uZmlndXJlCnRoZSBVU0Igc3RhcnQtdXAgbW9kZSBmaXJzdCB0byBnZXQg TURNNjYwMCBib290ZWQgaW4gdGhlIHJpZ2h0IG1vZGUgdG8KYmUgdXNhYmxlIGluIHRoZSBmaXJz dCBwbGFjZS4KCkZvciBub3csIHRoaXMgZHJpdmVyIGp1c3QgZ2l2ZXMgdXAgdGhlIHR3byBzdGFy dC11cCBtb2RlIEdQSU9zIGFmdGVyIHRoZQptb2RlbSBoYXMgYmVlbiBjb25maWd1cmVkIHRvIGJv b3QgaW4gbm9ybWFsIG1vZGUuIE9uZSBvZiB0aGVtIHdlIG1heQp3YW50IHRvIGNvbmZpZ3VyZSBm b3IgVVNCIE9PQiB3YWtlIGluIHRoaXMgZHJpdmVyIGxhdGVyIG9uLCBidXQgdGhhdCdzIGEKc2Vw YXJhdGUgc2VyaWVzIG9mIHBhdGNoZXMgYW5kIG5lZWRzIG1vcmUgd29yay4KCk5vdGUgdGhhdCB0 aGUgTW90b3JvbGEgTWFwcGhvbmUgTGludXgga2VybmVsIHRyZWUgaGFzIGEgInJhZGlvLWN0cmwi CmRyaXZlciBmb3IgbW9kZW1zLiBCdXQgaXQgcmVhbGx5IGRvZXMgbm90IGNvbnRyb2wgdGhlIHJh ZGlvIGF0IGFsbCwgaXQKanVzdCBjb250cm9scyB0aGUgbW9kZW0gcG93ZXIgYW5kIHN0YXJ0LXVw IG1vZGUgZm9yIFVTQi4gU28gSSBjYW1lIHRvCnRoZSBjb25jbHVzaW9uIHRoYXQgd2UncmUgYmV0 dGVyIG9mZiBoYXZpbmcgdGhpcyBkb25lIGluIHRoZSBVU0IgUEhZCmRyaXZlci4gRm9yIGFkZGlu ZyBzdXBwb3J0IGZvciBVU0IgZmxhc2hpbmcgbW9kZSwgd2UgY2FuIGxhdGVyIG9uIGFkZAphIGtl cm5lbCBtb2R1bGUgb3B0aW9uIGZvciBmbGFzaF9tb2RlPTEgb3Igc29tZXRoaW5nIHNpbWlsYXIu CgpBbHNvIG5vdGUgdGhhdCBjdXJyZW50bHkgdGhlcmUgaXMgbm8gUE0gcnVudGltZSBzdXBwb3J0 IGZvciB0aGUgT0hDSQpvbiBvbWFwIHZhcmlhbnQgU29Dcy4gU28gZm9yIGxvdyhlcikgcG93ZXIg aWRsZSBzdGF0ZXMsIGN1cnJlbnR5IGJvdGgKb2hjaS1wbGF0Zm9ybSBhbmQgcGh5LW1hcHBob25l LW1kbTY2MDAgbXVzdCBiZSB1bmxvYWRlZCBvciB1bmJvdW5kLgoKRm9yIHJlZmVyZW5jZSBoZXJl IGlzIHdoYXQgSSBtZWFzdXJlZCBmb3IgdG90YWwgcG93ZXIgY29uc3VtcHRpb24gb24KYW4gaWRs ZSBEcm9pZCA0IHdpdGggYW5kIHdpdGhvdXQgVVNCIHJlbGF0ZWQgTURNNjYwMCBtb2R1bGVzOgoK aWRsZSBsY2Qgb2ZmCXBoeS1tYXBwaG9uZS1tZG02NjAwCW9oY2ktcGxhdGZvcm0KMTUzbVcJCTI4 NG1XCQkJMzQ0bVcKClNvIGl0IHNlZW1zIHRoYXQgTURNNjYwMCBpcyBjdXJyZW50bHkgbm90IHll dCBpZGxpbmcgZXZlbiB3aXRoIGl0J3MKcmFkaW8gdHVybmVkIG9mZiwgYnV0IHRoYXQncyBzb21l dGhpbmcgdGhhdCBpcyBiZXlvbmQgdGhlIGNvbnRyb2wgb2YKdGhpcyBVU0IgUEhZIGRyaXZlci4K CkNjOiBkZXZpY2V0cmVlQHZnZXIua2VybmVsLm9yZwpDYzogTWFyayBSdXRsYW5kIDxtYXJrLnJ1 dGxhbmRAYXJtLmNvbT4KQ2M6IE1hcmNlbCBQYXJ0YXAgPG1wYXJ0YXBAZ214Lm5ldD4KQ2M6IE1p Y2hhZWwgU2NvdHQgPG1pY2hhZWwuc2NvdHRAbGluYXJvLm9yZz4KQ2M6IFJvYiBIZXJyaW5nIDxy b2JoK2R0QGtlcm5lbC5vcmc+CkNjOiBTZWJhc3RpYW4gUmVpY2hlbCA8c3JlQGtlcm5lbC5vcmc+ ClNpZ25lZC1vZmYtYnk6IFRvbnkgTGluZGdyZW4gPHRvbnlAYXRvbWlkZS5jb20+Ci0tLQogLi4u L2JpbmRpbmdzL3BoeS9waHktbWFwcGhvbmUtbWRtNjYwMC50eHQgICAgICAgICAgfCAgMzAgKysK IGRyaXZlcnMvcGh5L21vdG9yb2xhL0tjb25maWcgICAgICAgICAgICAgICAgICAgICAgIHwgICA5 ICsKIGRyaXZlcnMvcGh5L21vdG9yb2xhL01ha2VmaWxlICAgICAgICAgICAgICAgICAgICAgIHwg ICAxICsKIGRyaXZlcnMvcGh5L21vdG9yb2xhL3BoeS1tYXBwaG9uZS1tZG02NjAwLmMgICAgICAg IHwgNDkwICsrKysrKysrKysrKysrKysrKysrKwogNCBmaWxlcyBjaGFuZ2VkLCA1MzAgaW5zZXJ0 aW9ucygrKQogY3JlYXRlIG1vZGUgMTAwNjQ0IERvY3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5k aW5ncy9waHkvcGh5LW1hcHBob25lLW1kbTY2MDAudHh0CiBjcmVhdGUgbW9kZSAxMDA2NDQgZHJp dmVycy9waHkvbW90b3JvbGEvcGh5LW1hcHBob25lLW1kbTY2MDAuYwoKZGlmZiAtLWdpdCBhL0Rv Y3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5kaW5ncy9waHkvcGh5LW1hcHBob25lLW1kbTY2MDAu dHh0IGIvRG9jdW1lbnRhdGlvbi9kZXZpY2V0cmVlL2JpbmRpbmdzL3BoeS9waHktbWFwcGhvbmUt bWRtNjYwMC50eHQKbmV3IGZpbGUgbW9kZSAxMDA2NDQKLS0tIC9kZXYvbnVsbAorKysgYi9Eb2N1 bWVudGF0aW9uL2RldmljZXRyZWUvYmluZGluZ3MvcGh5L3BoeS1tYXBwaG9uZS1tZG02NjAwLnR4 dApAQCAtMCwwICsxLDMwIEBACitEZXZpY2UgdHJlZSBiaW5kaW5nIGRvY3VtZW50YXRpb24gZm9y IE1vdG9yb2xhIE1hcHBob25lIE1ETTY2MDAgVVNCIFBIWQorCitSZXF1aXJlZCBwcm9wZXJ0aWVz OgorLSBjb21wYXRpYmxlCU11c3QgYmUgIm1vdG9yb2xhLG1hcHBob25lLW1kbTY2MDAiCistIGVu YWJsZS1ncGlvcwlHUElPIHRvIGVuYWJsZSB0aGUgVVNCIFBIWQorLSBwb3dlci1ncGlvcwlHUElP IHRvIHBvd2VyIG9uIHRoZSBkZXZpY2UKKy0gcmVzZXQtZ3Bpb3MJR1BJTyB0byByZXNldCB0aGUg ZGV2aWNlCistIG1vZGUtZ3Bpb3MJVHdvIEdQSU9zIHRvIGNvbmZpZ3VyZSBNRE02NjAwIFVTQiBz dGFydC11cCBtb2RlIGZvcgorCQlub3JtYWwgbW9kZSB2ZXJzdXMgVVNCIGZsYXNoaW5nIG1vZGUK Ky0gc3RhdHVzLWdwaW9zCVRocmVlIEdQSU9zIHRvIHJlYWQgdGhlIHBvd2VyIHN0YXRlIG9mIHRo ZSBNRE02NjAwCistIGNtZC1ncGlvcwlUaHJlZSBHUElPcyB0byBjb250cm9sIHRoZSBwb3dlciBz dGF0ZSBvZiB0aGUgTURNNjYwMAorCitFeGFtcGxlOgorCitmc3VzYjFfcGh5OiBmc3VzYjFfcGh5 IHsKKwljb21wYXRpYmxlID0gIm1vdG9yb2xhLG1hcHBob25lLW1kbTY2MDAiOworCWVuYWJsZS1n cGlvcyA9IDwmZ3BpbzMgMzEgR1BJT19BQ1RJVkVfTE9XPjsgICAgIC8qIGdwaW9fOTUgKi8KKwlw b3dlci1ncGlvcyA9IDwmZ3BpbzIgMjIgR1BJT19BQ1RJVkVfSElHSD47CS8qIGdwaW9fNTQgKi8K KwlyZXNldC1ncGlvcyA9IDwmZ3BpbzIgMTcgR1BJT19BQ1RJVkVfSElHSD47CS8qIGdwaW9fNDkg Ki8KKwltb2RlLWdwaW9zID0gPCZncGlvNSAyMCBHUElPX0FDVElWRV9ISUdIPiwJLyogZ3Bpb18x NDggKi8KKwkJICAgICA8JmdwaW81IDIxIEdQSU9fQUNUSVZFX0hJR0g+OwkvKiBncGlvXzE0OSAq LworCXN0YXR1cy1ncGlvcyA9IDwmZ3BpbzIgMjMgR1BJT19BQ1RJVkVfSElHSD4sCS8qIGdwaW9f NTUgKi8KKwkJICAgICAgIDwmZ3BpbzIgMjEgR1BJT19BQ1RJVkVfSElHSD4sCS8qIGdwaW9fNTMg Ki8KKwkJICAgICAgIDwmZ3BpbzIgMjAgR1BJT19BQ1RJVkVfSElHSD47CS8qIGdwaW9fNTIgKi8K KwljbWQtZ3Bpb3MgPSA8JmdwaW80IDcgR1BJT19BQ1RJVkVfSElHSD4sCS8qIGdwaW9fMTAzICov CisJCSAgICA8JmdwaW80IDggR1BJT19BQ1RJVkVfSElHSD4sCS8qIGdwaW9fMTA0ICovCisJCSAg ICA8JmdwaW81IDE0IEdQSU9fQUNUSVZFX0hJR0g+OwkvKiBncGlvXzE0MiAqLworCSNwaHktY2Vs bHMgPSA8MD47Cit9OworCmRpZmYgLS1naXQgYS9kcml2ZXJzL3BoeS9tb3Rvcm9sYS9LY29uZmln IGIvZHJpdmVycy9waHkvbW90b3JvbGEvS2NvbmZpZwotLS0gYS9kcml2ZXJzL3BoeS9tb3Rvcm9s YS9LY29uZmlnCisrKyBiL2RyaXZlcnMvcGh5L21vdG9yb2xhL0tjb25maWcKQEAgLTEwLDMgKzEw LDEyIEBAIGNvbmZpZyBQSFlfQ1BDQVBfVVNCCiAJaGVscAogCSAgRW5hYmxlIHRoaXMgZm9yIFVT QiB0byB3b3JrIG9uIE1vdG9yb2xhIHBob25lcyBhbmQgdGFibGV0cwogCSAgc3VjaCBhcyBEcm9p ZCA0LgorCitjb25maWcgUEhZX01BUFBIT05FX01ETTY2MDAKKwl0cmlzdGF0ZSAiTW90b3JvbGEg TWFwcGhvbmUgTURNNjYwMCBtb2RlbSBVU0IgUEhZIGRyaXZlciIKKwlkZXBlbmRzIG9uIFVTQl9T VVBQT1JUCisJc2VsZWN0IEdFTkVSSUNfUEhZCisJc2VsZWN0IFVTQl9QSFkKKwloZWxwCisJICBF bmFibGUgdGhpcyBmb3IgTURNNjYwMCBVU0IgbW9kZW0gdG8gd29yayBvbiBNb3Rvcm9sYSBwaG9u ZXMKKwkgIGFuZCB0YWJsZXRzIHN1Y2ggYXMgRHJvaWQgNC4KZGlmZiAtLWdpdCBhL2RyaXZlcnMv cGh5L21vdG9yb2xhL01ha2VmaWxlIGIvZHJpdmVycy9waHkvbW90b3JvbGEvTWFrZWZpbGUKLS0t IGEvZHJpdmVycy9waHkvbW90b3JvbGEvTWFrZWZpbGUKKysrIGIvZHJpdmVycy9waHkvbW90b3Jv bGEvTWFrZWZpbGUKQEAgLTMsMyArMyw0IEBACiAjCiAKIG9iai0kKENPTkZJR19QSFlfQ1BDQVBf VVNCKQkJKz0gcGh5LWNwY2FwLXVzYi5vCitvYmotJChDT05GSUdfUEhZX01BUFBIT05FX01ETTY2 MDApCSs9IHBoeS1tYXBwaG9uZS1tZG02NjAwLm8KZGlmZiAtLWdpdCBhL2RyaXZlcnMvcGh5L21v dG9yb2xhL3BoeS1tYXBwaG9uZS1tZG02NjAwLmMgYi9kcml2ZXJzL3BoeS9tb3Rvcm9sYS9waHkt bWFwcGhvbmUtbWRtNjYwMC5jCm5ldyBmaWxlIG1vZGUgMTAwNjQ0Ci0tLSAvZGV2L251bGwKKysr IGIvZHJpdmVycy9waHkvbW90b3JvbGEvcGh5LW1hcHBob25lLW1kbTY2MDAuYwpAQCAtMCwwICsx LDQ5MCBAQAorLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQTC0yLjAKKy8qCisgKiBNb3Rv cm9sYSBNYXBwaG9uZSBNRE02NjAwIG1vZGVtIEdQSU8gY29udHJvbGxlZCBVU0IgUEhZIGRyaXZl cgorICogQ29weXJpZ2h0IChDKSAyMDE4IFRvbnkgTGluZGdyZW4gPHRvbnlAYXRvbWlkZS5jb20+ CisgKi8KKworI2luY2x1ZGUgPGxpbnV4L2RlbGF5Lmg+CisjaW5jbHVkZSA8bGludXgvZXJyLmg+ CisjaW5jbHVkZSA8bGludXgvaW8uaD4KKyNpbmNsdWRlIDxsaW51eC9tb2R1bGUuaD4KKyNpbmNs dWRlIDxsaW51eC9vZi5oPgorI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPgorI2lu Y2x1ZGUgPGxpbnV4L3NsYWIuaD4KKworI2luY2x1ZGUgPGxpbnV4L2dwaW8vY29uc3VtZXIuaD4K KyNpbmNsdWRlIDxsaW51eC9vZl9wbGF0Zm9ybS5oPgorI2luY2x1ZGUgPGxpbnV4L3BoeS9waHku aD4KKyNpbmNsdWRlIDxsaW51eC91c2IvcGh5X2NvbXBhbmlvbi5oPgorCisjZGVmaW5lIFBIWV9N RE02NjAwX1NUQVJUVVBfREVMQVlfTVMJMzAwMAkvKiBBIGxlYXN0IDIuMnMgdXN1YWxseSAqLwor CisvKgorICogTURNNjYwMCBzdGF0dXMgY29kZXMuIFRoZXNlIGFyZSBjb3BpZWQgZnJvbSBNb3Rv cm9sYSBNYXBwaG9uZSBMaW51eAorICoga2VybmVsIHRyZWUuIFRoZSBCQiBuYW1pbmcgaGVyZSBy ZWZlcnMgdG8gIkJhc2VCYW5kIiBmb3IgbW9kZW0uCisgKi8KK2VudW0gcGh5X21kbTY2MDBfc3Rh dHVzIHsKKwlCUF9TVEFUVVNfUEFOSUMsCQkvKiBTZWVtcyB0byBiZSByZWFsbHkgb2ZmIHN0YXRl ICovCisJQlBfU1RBVFVTX1BBTklDX0JVU1lfV0FJVCwKKwlCUF9TVEFUVVNfUUNfRExPQUQsCisJ QlBfU1RBVFVTX1JBTV9ET1dOTE9BREVSLAkvKiBNRE02NjAwIFVTQiBmbGFzaGluZyBtb2RlICov CisJQlBfU1RBVFVTX1BIT05FX0NPREVfQVdBS0UsCS8qIE1ETTY2MDAgbm9ybWFsIFVTQiBtb2Rl ICovCisJQlBfU1RBVFVTX1BIT05FX0NPREVfQVNMRUVQLAorCUJQX1NUQVRVU19TSFVURE9XTl9B Q0ssCisJQlBfU1RBVFVTX1VOREVGSU5FRCwKK307CisKK3N0YXRpYyBjb25zdCBjaGFyICogY29u c3QKK3BoeV9tZG02NjAwX3N0YXR1c19uYW1lW10gPSB7CisJIm9mZiIsICJidXN5IiwgInFjX2Rs IiwgInJhbV9kbCIsICJhd2FrZSIsCisJImFzbGVlcCIsICJzaHV0ZG93biIsICJ1bmRlZmluZWQi LAorfTsKKworLyoKKyAqIE1ETTY2MDAgY29tbWFuZCBjb2Rlcy4gVGhlc2UgYXJlIGNvcGllZCBm cm9tIE1vdG9yb2xhIE1hcHBob25lIExpbnV4CisgKiBrZXJuZWwgdHJlZS4gVGhlIEFQIG5hbWlu ZyBoZXJlIHJlZmVycyB0byAiQXBwbGljYXRpb24gUHJvY2Vzc29yIi4KKyAqLworZW51bSBwaHlf bWRtNjYwMF9jbWQgeworCUFQX1NUQVRVU19CUF9QQU5JQ19BQ0ssCisJQVBfU1RBVFVTX0RBVEFf T05MWV9CWVBBU1MsCS8qIFJlcm91dGUgVVNCIHRvIENQQ0FQIFBIWSAqLworCUFQX1NUQVRVU19G VUxMX0JZUEFTUywJCS8qIFJlcm91dGUgVVNCIHRvIENQQ0FQIFBIWSAqLworCUFQX1NUQVRVU19O T19CWVBBU1MsCQkvKiBSZXF1ZXN0IG5vcm1hbCBzdGFydC11cCBtb2RlICovCisJQVBfU1RBVFVT X0JQX1NIVVRET1dOX1JFUSwJLyogUmVxdWVzdCBkZXZpY2UgcG93ZXIgb2ZmICovCisJQVBfU1RB VFVTX0JQX1VOS05PV05fNSwKKwlBUF9TVEFUVVNfQlBfVU5LTk9XTl82LAorCUFQX1NUQVRVU19V TkRFRklORUQsCit9OworCitlbnVtIHBoeV9tZG02NjAwX2xpbmVzIHsKKwlQSFlfTURNNjYwMF9F TkFCTEUsCQkvKiBVU0IgUEhZIGVuYWJsZSAqLworCVBIWV9NRE02NjAwX1BPV0VSLAkJLyogRGV2 aWNlIHBvd2VyICovCisJUEhZX01ETTY2MDBfUkVTRVQsCQkvKiBEZXZpY2UgcmVzZXQgKi8KKwlQ SFlfTURNNjYwMF9NT0RFMCwJCS8qIFVTQiBib290IG1vZGUgZmxhc2hpbmcgdnMgbm9ybWFsICov CisJUEhZX01ETTY2MDBfTU9ERTEsCQkvKiBVU0IgYm9vdCBtb2RlIGZsYXNoaW5nIHZzIG5vcm1h bCAqLworCVBIWV9NRE02NjAwX1NUQVRVUzAsCQkvKiBEZXZpY2Ugc3RhdGUgKi8KKwlQSFlfTURN NjYwMF9TVEFUVVMxLAkJLyogRGV2aWNlIHN0YXRlICovCisJUEhZX01ETTY2MDBfU1RBVFVTMiwJ CS8qIERldmljZSBzdGF0ZSAqLworCVBIWV9NRE02NjAwX0NNRDAsCQkvKiBEZXZpY2UgY29tbWFu ZCAqLworCVBIWV9NRE02NjAwX0NNRDEsCQkvKiBEZXZpY2UgY29tbWFuZCAqLworCVBIWV9NRE02 NjAwX0NNRDIsCQkvKiBEZXZpY2UgY29tbWFuZCAqLworCVBIWV9NRE02NjAwX05SX0xJTkVTLAor fTsKKworc3RydWN0IHBoeV9tZG02NjAwIHsKKwlzdHJ1Y3QgZGV2aWNlICpkZXY7CisJc3RydWN0 IHVzYl9waHkgcGh5OworCXN0cnVjdCBwaHkgKmdlbmVyaWNfcGh5OworCXN0cnVjdCBwaHlfcHJv dmlkZXIgKnBoeV9wcm92aWRlcjsKKwlzdHJ1Y3QgZ3Bpb19kZXNjICpncGlvW1BIWV9NRE02NjAw X05SX0xJTkVTXTsKKwlzdHJ1Y3QgZGVsYXllZF93b3JrIGJvb3R1cF93b3JrOworCXN0cnVjdCBk ZWxheWVkX3dvcmsgc3RhdHVzX3dvcms7CisJc3RydWN0IGNvbXBsZXRpb24gYWNrOworCWJvb2wg ZW5hYmxlZDsKKwlpbnQgc3RhdHVzOworfTsKKworc3RhdGljIGludCBwaHlfbWRtNjYwMF9pbml0 KHN0cnVjdCBwaHkgKngpCit7CisJc3RydWN0IHBoeV9tZG02NjAwICpkZGF0YSA9IHBoeV9nZXRf ZHJ2ZGF0YSh4KTsKKwlzdHJ1Y3QgZ3Bpb19kZXNjICplbmFibGVfZ3BpbyA9IGRkYXRhLT5ncGlv W1BIWV9NRE02NjAwX0VOQUJMRV07CisKKwlpZiAoIWRkYXRhLT5lbmFibGVkKQorCQlyZXR1cm4g LUVQUk9CRV9ERUZFUjsKKworCWdwaW9kX3NldF92YWx1ZV9jYW5zbGVlcChlbmFibGVfZ3Bpbywg MCk7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGludCBwaHlfbWRtNjYwMF9wb3dlcl9vbihz dHJ1Y3QgcGh5ICp4KQoreworCXN0cnVjdCBwaHlfbWRtNjYwMCAqZGRhdGEgPSBwaHlfZ2V0X2Ry dmRhdGEoeCk7CisJc3RydWN0IGdwaW9fZGVzYyAqZW5hYmxlX2dwaW8gPSBkZGF0YS0+Z3Bpb1tQ SFlfTURNNjYwMF9FTkFCTEVdOworCisJaWYgKCFkZGF0YS0+ZW5hYmxlZCkKKwkJcmV0dXJuIC1F Tk9ERVY7CisKKwlncGlvZF9zZXRfdmFsdWVfY2Fuc2xlZXAoZW5hYmxlX2dwaW8sIDEpOworCisJ cmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgcGh5X21kbTY2MDBfcG93ZXJfb2ZmKHN0cnVjdCBw aHkgKngpCit7CisJc3RydWN0IHBoeV9tZG02NjAwICpkZGF0YSA9IHBoeV9nZXRfZHJ2ZGF0YSh4 KTsKKwlzdHJ1Y3QgZ3Bpb19kZXNjICplbmFibGVfZ3BpbyA9IGRkYXRhLT5ncGlvW1BIWV9NRE02 NjAwX0VOQUJMRV07CisKKwlpZiAoIWRkYXRhLT5lbmFibGVkKQorCQlyZXR1cm4gLUVOT0RFVjsK KworCWdwaW9kX3NldF92YWx1ZV9jYW5zbGVlcChlbmFibGVfZ3BpbywgMCk7CisKKwlyZXR1cm4g MDsKK30KKworc3RhdGljIGNvbnN0IHN0cnVjdCBwaHlfb3BzIGdwaW9fdXNiX29wcyA9IHsKKwku aW5pdCA9IHBoeV9tZG02NjAwX2luaXQsCisJLnBvd2VyX29uID0gcGh5X21kbTY2MDBfcG93ZXJf b24sCisJLnBvd2VyX29mZiA9IHBoeV9tZG02NjAwX3Bvd2VyX29mZiwKKwkub3duZXIgPSBUSElT X01PRFVMRSwKK307CisKK3N0cnVjdCBwaHlfbWRtNjYwMF9tYXAgeworCWNvbnN0IGNoYXIgKm5h bWU7CisJaW50IG5yX2dwaW9zOworCWludCBkaXJlY3Rpb247Cit9OworCitzdGF0aWMgY29uc3Qg c3RydWN0IHBoeV9tZG02NjAwX21hcAorcGh5X21kbTY2MDBfbGluZV9tYXBbUEhZX01ETTY2MDBf TlJfTElORVNdID0geworCXsgImVuYWJsZSIsIDEsIEdQSU9EX09VVF9MT1csIH0sCS8qIGxvdyA9 IGRpc2FibGVkICovCisJeyAicG93ZXIiLCAxLCBHUElPRF9PVVRfTE9XLCB9LAkJLyogbG93ID0g b2ZmICovCisJeyAicmVzZXQiLCAxLCBHUElPRF9PVVRfSElHSCwgfSwJLyogaGlnaCA9IHJlc2V0 ICovCisJeyAibW9kZSIsIDIsIEdQSU9EX09VVF9MT1csIH0sCisJeyAic3RhdHVzIiwgMywgR1BJ T0RfSU4sIH0sCisJeyAiY21kIiwgMywgR1BJT0RfT1VUX0xPVywgfSwJCS8qIGxvdyA9IG5vIGNv bW1hbmQgKi8KK307CisKKy8qKgorICogcGh5X21kbTY2MDBfY21kKCkgLSBzZW5kIGEgY29tbWFu ZCByZXF1ZXN0IHRvIG1kbTY2MDAKKyAqIEBkZGF0YTogZGV2aWNlIGRyaXZlciBkYXRhCisgKgor ICogQ29uZmlndXJlcyB0aGUgdGhyZWUgY29tbWFuZCByZXF1ZXN0IEdQSU9zIHRvIHRoZSBzcGVj aWZpZWQgdmFsdWUuCisgKi8KK3N0YXRpYyB2b2lkIHBoeV9tZG02NjAwX2NtZChzdHJ1Y3QgcGh5 X21kbTY2MDAgKmRkYXRhLCBpbnQgdmFsKQoreworCWludCBpOworCisJdmFsICY9IDB4NzsKKwor CWZvciAoaSA9IFBIWV9NRE02NjAwX0NNRDA7CisJICAgICBpIDw9IFBIWV9NRE02NjAwX0NNRDI7 IGkrKykgeworCQlzdHJ1Y3QgZ3Bpb19kZXNjICpncGlvID0gZGRhdGEtPmdwaW9baV07CisJCWlu dCBzaGlmdCA9ICgyIC0gKGkgLSBQSFlfTURNNjYwMF9DTUQwKSk7CisKKwkJaWYgKElTX0VSUihn cGlvKSkKKwkJCXJldHVybjsKKworCQlncGlvZF9zZXRfdmFsdWVfY2Fuc2xlZXAoZ3BpbywgKHZh bCAmIEJJVChzaGlmdCkpID4+IHNoaWZ0KTsKKwl9Cit9CisKKy8qKgorICogcGh5X21kbTY2MDBf c3RhdHVzKCkgLSByZWFkIG1kbTY2MDAgc3RhdHVzIGxpbmVzCisgKiBAZGRhdGE6IGRldmljZSBk cml2ZXIgZGF0YQorICovCitzdGF0aWMgdm9pZCBwaHlfbWRtNjYwMF9zdGF0dXMoc3RydWN0IHdv cmtfc3RydWN0ICp3b3JrKQoreworCXN0cnVjdCBwaHlfbWRtNjYwMCAqZGRhdGE7CisJc3RydWN0 IGRldmljZSAqZGV2OworCWludCBpLCB2YWw7CisKKwlkZGF0YSA9IGNvbnRhaW5lcl9vZih3b3Jr LCBzdHJ1Y3QgcGh5X21kbTY2MDAsIHN0YXR1c193b3JrLndvcmspOworCWRldiA9IGRkYXRhLT5k ZXY7CisKKwlmb3IgKGkgPSBQSFlfTURNNjYwMF9TVEFUVVMwOworCSAgICAgaSA8PSBQSFlfTURN NjYwMF9TVEFUVVMyOyBpKyspIHsKKwkJc3RydWN0IGdwaW9fZGVzYyAqZ3BpbyA9IGRkYXRhLT5n cGlvW2ldOworCQlpbnQgc2hpZnQgPSAoMiAtIChpIC0gUEhZX01ETTY2MDBfU1RBVFVTMCkpOwor CisJCWlmIChJU19FUlIoZGRhdGEtPmdwaW9baV0pKQorCQkJY29udGludWU7CisJCXZhbCA9IGdw aW9kX2dldF92YWx1ZV9jYW5zbGVlcChncGlvKTsKKwkJZGRhdGEtPnN0YXR1cyAmPSB+KEJJVChz aGlmdCkpOworCQlkZGF0YS0+c3RhdHVzIHw9ICh2YWwgPDwgc2hpZnQpOworCX0KKwlkZXZfaW5m byhkZXYsICJtb2RlbSBzdGF0dXM6ICVpICVzXG4iLAorCQkgZGRhdGEtPnN0YXR1cywKKwkJIHBo eV9tZG02NjAwX3N0YXR1c19uYW1lW2RkYXRhLT5zdGF0dXMgJiA3XSk7CisJY29tcGxldGUoJmRk YXRhLT5hY2spOworfQorCitzdGF0aWMgaXJxcmV0dXJuX3QgcGh5X21kbTY2MDBfaXJxX3RocmVh ZChpbnQgaXJxLCB2b2lkICpkYXRhKQoreworCXN0cnVjdCBwaHlfbWRtNjYwMCAqZGRhdGEgPSBk YXRhOworCisJc2NoZWR1bGVfZGVsYXllZF93b3JrKCZkZGF0YS0+c3RhdHVzX3dvcmssIG1zZWNz X3RvX2ppZmZpZXMoMTApKTsKKworCXJldHVybiBJUlFfSEFORExFRDsKK30KKworLyoqCisgKiBw aHlfbWRtNjYwMF9pbml0X2lycSgpIC0gaW5pdGlhbGl6ZSBtZG02NjAwIHN0YXR1cyBJUlEgbGlu ZXMKKyAqIEBkZGF0YTogZGV2aWNlIGRyaXZlciBkYXRhCisgKi8KK3N0YXRpYyB2b2lkIHBoeV9t ZG02NjAwX2luaXRfaXJxKHN0cnVjdCBwaHlfbWRtNjYwMCAqZGRhdGEpCit7CisJc3RydWN0IGRl dmljZSAqZGV2ID0gZGRhdGEtPmRldjsKKwlpbnQgaSwgZXJyb3IsIGlycTsKKworCWZvciAoaSA9 IFBIWV9NRE02NjAwX1NUQVRVUzA7CisJICAgICBpIDw9IFBIWV9NRE02NjAwX1NUQVRVUzI7IGkr KykgeworCQlpZiAoSVNfRVJSKGRkYXRhLT5ncGlvW2ldKSkKKwkJCWNvbnRpbnVlOworCisJCWly cSA9IGdwaW9kX3RvX2lycShkZGF0YS0+Z3Bpb1tpXSk7CisJCWlmIChpcnEgPD0gMCkKKwkJCWNv bnRpbnVlOworCisJCWVycm9yID0gZGV2bV9yZXF1ZXN0X3RocmVhZGVkX2lycShkZXYsIGlycSwg TlVMTCwKKwkJCQkJcGh5X21kbTY2MDBfaXJxX3RocmVhZCwKKwkJCQkJSVJRRl9UUklHR0VSX1JJ U0lORyB8CisJCQkJCUlSUUZfVFJJR0dFUl9GQUxMSU5HIHwKKwkJCQkJSVJRRl9PTkVTSE9ULAor CQkJCQkibWRtNjYwMCIsCisJCQkJCWRkYXRhKTsKKwkJaWYgKGVycm9yKQorCQkJZGV2X3dhcm4o ZGV2LCAibm8gbW9kZW0gc3RhdHVzIGlycSVpOiAlaVxuIiwKKwkJCQkgaXJxLCBlcnJvcik7CisJ fQorfQorCisvKioKKyAqIHBoeV9tZG02NjAwX2luaXRfbGluZXMoKSAtIGluaXRpYWxpemUgbWRt NjYwMCBHUElPIGxpbmVzCisgKiBAZGRhdGE6IGRldmljZSBkcml2ZXIgZGF0YQorICovCitzdGF0 aWMgaW50IHBoeV9tZG02NjAwX2luaXRfbGluZXMoc3RydWN0IHBoeV9tZG02NjAwICpkZGF0YSkK K3sKKwlzdHJ1Y3QgZGV2aWNlICpkZXYgPSBkZGF0YS0+ZGV2OworCWludCBpLCBqLCBucl9ncGlv ID0gMDsKKworCWZvciAoaSA9IDA7IGkgPCBBUlJBWV9TSVpFKHBoeV9tZG02NjAwX2xpbmVfbWFw KTsgaSsrKSB7CisJCWNvbnN0IHN0cnVjdCBwaHlfbWRtNjYwMF9tYXAgKm1hcCA9CisJCQkmcGh5 X21kbTY2MDBfbGluZV9tYXBbaV07CisKKwkJZm9yIChqID0gMDsgaiA8IG1hcC0+bnJfZ3Bpb3M7 IGorKykgeworCQkJc3RydWN0IGdwaW9fZGVzYyAqKmdwaW8gPSAmZGRhdGEtPmdwaW9bbnJfZ3Bp b107CisKKwkJCSpncGlvID0gZGV2bV9ncGlvZF9nZXRfaW5kZXgoZGV2LAorCQkJCQkJICAgICBt YXAtPm5hbWUsIGosCisJCQkJCQkgICAgIG1hcC0+ZGlyZWN0aW9uKTsKKwkJCWlmIChJU19FUlIo KmdwaW8pKSB7CisJCQkJZGV2X2luZm8oZGV2LAorCQkJCQkgImdwaW8gJXMgZXJyb3IgJWxpLCBh bHJlYWR5IHRha2VuP1xuIiwKKwkJCQkJIG1hcC0+bmFtZSwgUFRSX0VSUigqZ3BpbykpOworCQkJ CXJldHVybiBQVFJfRVJSKCpncGlvKTsKKwkJCX0KKwkJCW5yX2dwaW8rKzsKKwkJfQorCX0KKwor CXJldHVybiAwOworfQorCisvKioKKyAqIHBoeV9tZG02NjAwX2RldmljZV9wb3dlcl9vbigpIC0g cG93ZXIgb24gbWRtNjYwMCBkZXZpY2UKKyAqIEBkZGF0YTogZGV2aWNlIGRyaXZlciBkYXRhCisg KgorICogVG8gZ2V0IHRoZSBpbnRlZ3JhdGVkIFVTQiBwaHkgaW4gTURNNjYwMCB0YWtlcyBzb21l IGhvb3BzLiBXZSBtdXN0IGVuc3VyZQorICogdGhlIHNoYXJlZCBVU0IgYm9vdG1vZGUgR1BJT3Mg YXJlIGNvbmZpZ3VyZWQsIHRoZW4gcmVxdWVzdCBtb2RlbSBzdGFydC11cCwKKyAqIHJlc2V0IGFu ZCBwb3dlci11cC4uIEFuZCB0aGVuIHdlIG5lZWQgdG8gZ2l2ZSB1cCB0aGUgc2hhcmVkIFVTQiBi b290bW9kZQorICogR1BJT3MgYXMgdGhleSBhcmUgYWxzbyB1c2VkIGZvciBPdXQgb2YgQmFuZCAo T09CKSB3YWtlIGZvciB0aGUgVVNCIGFuZAorICogVFMgMjcuMDEwIHNlcmlhbCBtdXguCisgKi8K K3N0YXRpYyBpbnQgcGh5X21kbTY2MDBfZGV2aWNlX3Bvd2VyX29uKHN0cnVjdCBwaHlfbWRtNjYw MCAqZGRhdGEpCit7CisJc3RydWN0IGdwaW9fZGVzYyAqbW9kZV9ncGlvMCA9IGRkYXRhLT5ncGlv W1BIWV9NRE02NjAwX01PREUwXTsKKwlzdHJ1Y3QgZ3Bpb19kZXNjICptb2RlX2dwaW8xID0gZGRh dGEtPmdwaW9bUEhZX01ETTY2MDBfTU9ERTFdOworCXN0cnVjdCBncGlvX2Rlc2MgKnJlc2V0X2dw aW8gPSBkZGF0YS0+Z3Bpb1tQSFlfTURNNjYwMF9SRVNFVF07CisJc3RydWN0IGdwaW9fZGVzYyAq cG93ZXJfZ3BpbyA9IGRkYXRhLT5ncGlvW1BIWV9NRE02NjAwX1BPV0VSXTsKKwlpbnQgZXJyb3Ig PSAwOworCisJLyoKKwkgKiBTaGFyZWQgR1BJT3MgbXVzdCBiZSBsb3cgZm9yIG5vcm1hbCBVU0Ig bW9kZS4gQWZ0ZXIgYm9vdGluZywKKwkgKiB3ZSBkb24ndCBuZWVkIHRoZW0uIFRoZXNlIGNhbiBi ZSBhbHNvIHVzZWQgdG8gY29uZmlndXJlIFVTQgorCSAqIGZsYXNoaW5nIG1vZGUgbGF0ZXIgb24g YmFzZWQgb24gYSBtb2R1bGUgcGFyYW1ldGVyLgorCSAqLworCWdwaW9kX3NldF92YWx1ZV9jYW5z bGVlcChtb2RlX2dwaW8wLCAwKTsKKwlncGlvZF9zZXRfdmFsdWVfY2Fuc2xlZXAobW9kZV9ncGlv MSwgMCk7CisKKwkvKiBSZXF1ZXN0IHN0YXJ0LXVwIG1vZGUgKi8KKwlwaHlfbWRtNjYwMF9jbWQo ZGRhdGEsIEFQX1NUQVRVU19OT19CWVBBU1MpOworCisJLyogUmVxdWVzdCBhIHJlc2V0IGZpcnN0 ICovCisJZ3Bpb2Rfc2V0X3ZhbHVlX2NhbnNsZWVwKHJlc2V0X2dwaW8sIDApOworCW1zbGVlcCgx MDApOworCisJLyogVG9nZ2xlIHBvd2VyIEdQSU8gdG8gcmVxdWVzdCBtZG02NjAwIHRvIHN0YXJ0 ICovCisJZ3Bpb2Rfc2V0X3ZhbHVlX2NhbnNsZWVwKHBvd2VyX2dwaW8sIDEpOworCW1zbGVlcCgx MDApOworCWdwaW9kX3NldF92YWx1ZV9jYW5zbGVlcChwb3dlcl9ncGlvLCAwKTsKKworCS8qCisJ ICogTG9va3MgbGlrZSB0aGUgVVNCIFBIWSBpcyBhdCBsZWFzdCAyLjIgc2Vjb25kcy4KKwkgKiBJ ZiB3ZSB0cnkgdG8gdXNlIGl0IGJlZm9yZSB0aGF0LCB3ZSB3aWxsIGdldCBMMyBlcnJvcnMKKwkg KiBmcm9tIG9tYXAtdXNiLWhvc3QgdHJ5aW5nIHRvIGFjY2VzcyB0aGUgUEhZLiBTZWUgYWxzbwor CSAqIHBoeV9tZG02NjAwX2luaXQoKSBmb3IgLUVQUk9CRV9ERUZFUi4KKwkgKi8KKwltc2xlZXAo UEhZX01ETTY2MDBfU1RBUlRVUF9ERUxBWV9NUyk7CisJZGRhdGEtPmVuYWJsZWQgPSB0cnVlOwor CisJLyogQm9vdGluZyB1cCB0aGUgcmVzdCBvZiBNRE02NjAwIHdpbGwgdGFrZSB0b3RhbCBhYm91 dCA4IHNlY29uZHMgKi8KKwlkZXZfaW5mbyhkZGF0YS0+ZGV2LCAiV2FpdGluZyBmb3IgcG93ZXIg dXAgcmVxdWVzdCB0byBjb21wbGV0ZS4uXG4iKTsKKwlpZiAod2FpdF9mb3JfY29tcGxldGlvbl90 aW1lb3V0KCZkZGF0YS0+YWNrLAorCQkJCQltc2Vjc190b19qaWZmaWVzKDgwMDApKSkgeworCQlk ZXZfaW5mbyhkZGF0YS0+ZGV2LCAiUG93ZXJlZCB1cCBPS1xuIik7CisJfSBlbHNlIHsKKwkJZGRh dGEtPmVuYWJsZWQgPSBmYWxzZTsKKwkJZXJyb3IgPSAtRVRJTUVET1VUOworCQlkZXZfZXJyKGRk YXRhLT5kZXYsICJUaW1lZCBvdXQgcG93ZXJpbmcgdXBcbiIpOworCX0KKworCS8qIEdpdmUgdXAg c2hhcmVkIEdQSU9zIG5vdywgdGhleSB3aWxsIGJlIHVzZWQgZm9yIE9PQiB3YWtlICovCisJZGV2 bV9ncGlvZF9wdXQoZGRhdGEtPmRldiwgbW9kZV9ncGlvMCk7CisJZGRhdGEtPmdwaW9bUEhZX01E TTY2MDBfTU9ERTBdID0gRVJSX1BUUigtRU5PREVWKTsKKwlkZXZtX2dwaW9kX3B1dChkZGF0YS0+ ZGV2LCBtb2RlX2dwaW8xKTsKKwlkZGF0YS0+Z3Bpb1tQSFlfTURNNjYwMF9NT0RFMF0gPSBFUlJf UFRSKC1FTk9ERVYpOworCisJcmV0dXJuIGVycm9yOworfQorCisvKioKKyAqIHBoeV9tZG02NjAw X2RldmljZV9wb3dlcl9vZmYoKSAtIHBvd2VyIG9mZiBtZG02NjAwIGRldmljZQorICogQGRkYXRh OiBkZXZpY2UgZHJpdmVyIGRhdGEKKyAqLworc3RhdGljIHZvaWQgcGh5X21kbTY2MDBfZGV2aWNl X3Bvd2VyX29mZihzdHJ1Y3QgcGh5X21kbTY2MDAgKmRkYXRhKQoreworCXN0cnVjdCBncGlvX2Rl c2MgKnJlc2V0X2dwaW8gPQorCQlkZGF0YS0+Z3Bpb1tQSFlfTURNNjYwMF9SRVNFVF07CisKKwlk ZGF0YS0+ZW5hYmxlZCA9IGZhbHNlOworCXBoeV9tZG02NjAwX2NtZChkZGF0YSwgQVBfU1RBVFVT X0JQX1NIVVRET1dOX1JFUSk7CisJbXNsZWVwKDEwMCk7CisKKwlpZiAocmVzZXRfZ3BpbyA+PSAw KQorCQlncGlvZF9zZXRfdmFsdWVfY2Fuc2xlZXAocmVzZXRfZ3BpbywgMSk7CisKKwlkZXZfaW5m byhkZGF0YS0+ZGV2LCAiV2FpdGluZyBmb3IgcG93ZXIgZG93biByZXF1ZXN0IHRvIGNvbXBsZXRl Li4gIik7CisJaWYgKHdhaXRfZm9yX2NvbXBsZXRpb25fdGltZW91dCgmZGRhdGEtPmFjaywKKwkJ CQkJbXNlY3NfdG9famlmZmllcyg1MDAwKSkpIHsKKwkJZGV2X2luZm8oZGRhdGEtPmRldiwgIlBv d2VyZWQgZG93biBPS1xuIik7CisJfSBlbHNlIHsKKwkJZGV2X2VycihkZGF0YS0+ZGV2LCAiVGlt ZWQgb3V0IHBvd2VyaW5nIGRvd25cbiIpOworCX0KK30KKworc3RhdGljIHZvaWQgcGh5X21kbTY2 MDBfZGVmZXJyZWRfcG93ZXJfb24oc3RydWN0IHdvcmtfc3RydWN0ICp3b3JrKQoreworCXN0cnVj dCBwaHlfbWRtNjYwMCAqZGRhdGE7CisJaW50IGVycm9yOworCisJZGRhdGEgPSBjb250YWluZXJf b2Yod29yaywgc3RydWN0IHBoeV9tZG02NjAwLCBib290dXBfd29yay53b3JrKTsKKworCWVycm9y ID0gcGh5X21kbTY2MDBfZGV2aWNlX3Bvd2VyX29uKGRkYXRhKTsKKwlpZiAoZXJyb3IpCisJCWRl dl9lcnIoZGRhdGEtPmRldiwgIkRldmljZSBub3QgZnVuY3Rpb25hbFxuIik7Cit9CisKKyNpZmRl ZiBDT05GSUdfT0YKK3N0YXRpYyBjb25zdCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkIHBoeV9tZG02NjAw X2lkX3RhYmxlW10gPSB7CisJeyAuY29tcGF0aWJsZSA9ICJtb3Rvcm9sYSxtYXBwaG9uZS1tZG02 NjAwIiwgfSwKKwl7fSwKK307CitNT0RVTEVfREVWSUNFX1RBQkxFKG9mLCBwaHlfbWRtNjYwMF9p ZF90YWJsZSk7CisjZW5kaWYKKworc3RhdGljIGludCBwaHlfbWRtNjYwMF9wcm9iZShzdHJ1Y3Qg cGxhdGZvcm1fZGV2aWNlICpwZGV2KQoreworCXN0cnVjdCBwaHlfbWRtNjYwMCAqZGRhdGE7CisJ c3RydWN0IHVzYl9vdGcgKm90ZzsKKwljb25zdCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkICpvZl9pZDsK KwlpbnQgZXJyb3I7CisKKwlvZl9pZCA9IG9mX21hdGNoX2RldmljZShvZl9tYXRjaF9wdHIocGh5 X21kbTY2MDBfaWRfdGFibGUpLAorCQkJCSZwZGV2LT5kZXYpOworCWlmICghb2ZfaWQpCisJCXJl dHVybiAtRUlOVkFMOworCisJZGRhdGEgPSBkZXZtX2t6YWxsb2MoJnBkZXYtPmRldiwgc2l6ZW9m KCpkZGF0YSksIEdGUF9LRVJORUwpOworCWlmICghZGRhdGEpCisJCXJldHVybiAtRU5PTUVNOwor CisJSU5JVF9ERUxBWUVEX1dPUksoJmRkYXRhLT5ib290dXBfd29yaywKKwkJCSAgcGh5X21kbTY2 MDBfZGVmZXJyZWRfcG93ZXJfb24pOworCUlOSVRfREVMQVlFRF9XT1JLKCZkZGF0YS0+c3RhdHVz X3dvcmssIHBoeV9tZG02NjAwX3N0YXR1cyk7CisJaW5pdF9jb21wbGV0aW9uKCZkZGF0YS0+YWNr KTsKKworCW90ZyA9IGRldm1fa3phbGxvYygmcGRldi0+ZGV2LCBzaXplb2YoKm90ZyksIEdGUF9L RVJORUwpOworCWlmICghb3RnKQorCQlyZXR1cm4gLUVOT01FTTsKKworCWRkYXRhLT5kZXYgPSAm cGRldi0+ZGV2OworCWRkYXRhLT5waHkuZGV2ID0gZGRhdGEtPmRldjsKKwlkZGF0YS0+cGh5Lmxh YmVsID0gInBoeV9tZG02NjAwIjsKKwlkZGF0YS0+cGh5Lm90ZyA9IG90ZzsKKwlkZGF0YS0+cGh5 LnR5cGUgPSBVU0JfUEhZX1RZUEVfVVNCMjsKKwlvdGctPnVzYl9waHkgPSAmZGRhdGEtPnBoeTsK KworCXBsYXRmb3JtX3NldF9kcnZkYXRhKHBkZXYsIGRkYXRhKTsKKworCWVycm9yID0gcGh5X21k bTY2MDBfaW5pdF9saW5lcyhkZGF0YSk7CisJaWYgKGVycm9yKQorCQlyZXR1cm4gZXJyb3I7CisK KwlwaHlfbWRtNjYwMF9pbml0X2lycShkZGF0YSk7CisKKwlkZGF0YS0+Z2VuZXJpY19waHkgPSBk ZXZtX3BoeV9jcmVhdGUoZGRhdGEtPmRldiwgTlVMTCwgJmdwaW9fdXNiX29wcyk7CisJaWYgKElT X0VSUihkZGF0YS0+Z2VuZXJpY19waHkpKSB7CisJCWVycm9yID0gUFRSX0VSUihkZGF0YS0+Z2Vu ZXJpY19waHkpOworCQlnb3RvIGNsZWFudXA7CisJfQorCisJcGh5X3NldF9kcnZkYXRhKGRkYXRh LT5nZW5lcmljX3BoeSwgZGRhdGEpOworCisJZGRhdGEtPnBoeV9wcm92aWRlciA9CisJCWRldm1f b2ZfcGh5X3Byb3ZpZGVyX3JlZ2lzdGVyKGRkYXRhLT5kZXYsCisJCQkJCSAgICAgIG9mX3BoeV9z aW1wbGVfeGxhdGUpOworCWlmIChJU19FUlIoZGRhdGEtPnBoeV9wcm92aWRlcikpIHsKKwkJZXJy b3IgPSBQVFJfRVJSKGRkYXRhLT5waHlfcHJvdmlkZXIpOworCQlnb3RvIGNsZWFudXA7CisJfQor CisJc2NoZWR1bGVfZGVsYXllZF93b3JrKCZkZGF0YS0+Ym9vdHVwX3dvcmssIDApOworCisJLyoK KwkgKiBTZWUgcGh5X21kbTY2MDBfZGV2aWNlX3Bvd2VyX29uKCkuIFdlIHNob3VsZCBiZSBhYmxl CisJICogdG8gcmVtb3ZlIHRoaXMgZXZlbnR1YWxseSB3aGVuIG9oY2ktcGxhdGZvcm0gY2FuIGRl YWwKKwkgKiB3aXRoIC1FUFJPQkVfREVGRVIuCisJICovCisJbXNsZWVwKFBIWV9NRE02NjAwX1NU QVJUVVBfREVMQVlfTVMgKyA1MDApOworCisJdXNiX2FkZF9waHlfZGV2KCZkZGF0YS0+cGh5KTsK KworCXJldHVybiAwOworCitjbGVhbnVwOgorCXBoeV9tZG02NjAwX2RldmljZV9wb3dlcl9vZmYo ZGRhdGEpOworCXJldHVybiBlcnJvcjsKK30KKworc3RhdGljIGludCBwaHlfbWRtNjYwMF9yZW1v dmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKK3sKKwlzdHJ1Y3QgcGh5X21kbTY2MDAg KmRkYXRhID0gcGxhdGZvcm1fZ2V0X2RydmRhdGEocGRldik7CisJc3RydWN0IGdwaW9fZGVzYyAq cmVzZXRfZ3BpbyA9IGRkYXRhLT5ncGlvW1BIWV9NRE02NjAwX1JFU0VUXTsKKworCWlmICghSVNf RVJSKHJlc2V0X2dwaW8pKQorCQlncGlvZF9zZXRfdmFsdWVfY2Fuc2xlZXAocmVzZXRfZ3Bpbywg MSk7CisKKwlwaHlfbWRtNjYwMF9kZXZpY2VfcG93ZXJfb2ZmKGRkYXRhKTsKKworCWNhbmNlbF9k ZWxheWVkX3dvcmtfc3luYygmZGRhdGEtPmJvb3R1cF93b3JrKTsKKwljYW5jZWxfZGVsYXllZF93 b3JrX3N5bmMoJmRkYXRhLT5zdGF0dXNfd29yayk7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGlj IHN0cnVjdCBwbGF0Zm9ybV9kcml2ZXIgcGh5X21kbTY2MDBfZHJpdmVyID0geworCS5wcm9iZSA9 IHBoeV9tZG02NjAwX3Byb2JlLAorCS5yZW1vdmUgPSBwaHlfbWRtNjYwMF9yZW1vdmUsCisJLmRy aXZlciA9IHsKKwkJLm5hbWUgPSAicGh5LW1hcHBob25lLW1kbTY2MDAiLAorCQkub2ZfbWF0Y2hf dGFibGUgPSBvZl9tYXRjaF9wdHIocGh5X21kbTY2MDBfaWRfdGFibGUpLAorCX0sCit9OworCitt b2R1bGVfcGxhdGZvcm1fZHJpdmVyKHBoeV9tZG02NjAwX2RyaXZlcik7CisKK01PRFVMRV9BTElB UygicGxhdGZvcm06Z3Bpb191c2IiKTsKK01PRFVMRV9BVVRIT1IoIlRvbnkgTGluZGdyZW4gPHRv bnlAYXRvbWlkZS5jb20+Iik7CitNT0RVTEVfREVTQ1JJUFRJT04oImdlbmVyaWMgZ3BpbyB1c2Ig cGh5IGRyaXZlciIpOworTU9EVUxFX0xJQ0VOU0UoIkdQTCB2MiIpOwo= From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Cyrus-Session-Id: sloti22d1t05-3676327-1518901667-2-10144073908787392567 X-Sieve: CMU Sieve 3.0 X-Spam-known-sender: no X-Spam-score: 0.0 X-Spam-hits: BAYES_00 -1.9, HEADER_FROM_DIFFERENT_DOMAINS 0.001, ME_NOAUTH 0.01, RCVD_IN_DNSWL_HI -5, T_RP_MATCHES_RCVD -0.01, LANGUAGES en, BAYES_USED global, SA_VERSION 3.4.0 X-Spam-source: IP='209.132.180.67', Host='vger.kernel.org', Country='US', FromHeader='com', MailFrom='org' X-Spam-charsets: X-Resolved-to: greg@kroah.com X-Delivered-to: greg@kroah.com X-Mail-from: linux-usb-owner@vger.kernel.org ARC-Seal: i=1; a=rsa-sha256; cv=none; d=messagingengine.com; s=arctest; t=1518901665; b=YUXT2lSJbYIph6pajsKPqUsAN5/4ItvwR8SfcziorqqBU/L pAAZ1TFytrRQaC+P0VcoRUYoC+ezjSe/UvU0NsM7hqR40LsZCpOIdClb5KInk/8G O7lIKJaV/+OSaT+PZmvg73f7jiKi+FI0TAdMgDJ2dusK45Q/7NefM248J8HTPnsY f/tqsw0UF0L2xkSZsIVfqqpRNqjf0ub/T0pSJp76hbZcIaLRMCWxVRY0TBQKnjC4 h0kJTJTi/P29IlpuHynwqsL2lDvnQBQIWDWYjctlKGLCuFhQEXgFbdxYwT64sR5v xtd2ht+rbSqZ99m8iifdvKdd997FJKwR9jnicJw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=from:to:cc:subject:date:message-id:sender :list-id; s=arctest; t=1518901665; bh=DM0PFoXZTTmZ8Qa26Pt0POGaNp tk6luuhp8mOJ6DhVY=; b=sRXMEZWPHHQP2M9LAeN2eNeunHdEWYTam4ChHXNYsw f+9kE2GsexXUxWsbhPEp9NjPgW0q8ml7yXLTX+PI4n14YrbEftskKAsryBhqFf1e wNrvGyzLL2P8457oFjXSnUGL2amsbFPiAmoLRCwNbXd/0+vxdwCtCOuJDNpG+oFD F/s01S2jkmSVXZbEOl9gIorTAd/FPf/wCxuekFGsfccUUS8ChXkPWerdiPuHw0Re mUI8K7X3gjuc7Zani8zNL9FC1tLndzkNLty+GgY/QtvPRnx434IyXcX+mbB5UhGC LXuD8gI4WVdvf4WpenR//j/KM++wjHmdG8eUcRbaUoSA== ARC-Authentication-Results: i=1; mx1.messagingengine.com; arc=none (no signatures found); dkim=none (no signatures found); dmarc=none (p=none,has-list-id=yes,d=none) header.from=atomide.com; iprev=pass policy.iprev=209.132.180.67 (vger.kernel.org); spf=none smtp.mailfrom=linux-usb-owner@vger.kernel.org smtp.helo=vger.kernel.org; x-aligned-from=fail; x-ptr=pass x-ptr-helo=vger.kernel.org x-ptr-lookup=vger.kernel.org; x-return-mx=pass smtp.domain=vger.kernel.org smtp.result=pass smtp_org.domain=kernel.org smtp_org.result=pass smtp_is_org_domain=no header.domain=atomide.com header.result=pass header_is_org_domain=yes Authentication-Results: mx1.messagingengine.com; arc=none (no signatures found); dkim=none (no signatures found); dmarc=none (p=none,has-list-id=yes,d=none) header.from=atomide.com; iprev=pass policy.iprev=209.132.180.67 (vger.kernel.org); spf=none smtp.mailfrom=linux-usb-owner@vger.kernel.org smtp.helo=vger.kernel.org; x-aligned-from=fail; x-ptr=pass x-ptr-helo=vger.kernel.org x-ptr-lookup=vger.kernel.org; x-return-mx=pass smtp.domain=vger.kernel.org smtp.result=pass smtp_org.domain=kernel.org smtp_org.result=pass smtp_is_org_domain=no header.domain=atomide.com header.result=pass header_is_org_domain=yes Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751453AbeBQVHd (ORCPT ); Sat, 17 Feb 2018 16:07:33 -0500 Received: from muru.com ([72.249.23.125]:57486 "EHLO muru.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751262AbeBQVHb (ORCPT ); Sat, 17 Feb 2018 16:07:31 -0500 From: Tony Lindgren To: Kishon Vijay Abraham I Cc: linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, linux-omap@vger.kernel.org, devicetree@vger.kernel.org, Mark Rutland , Marcel Partap , Michael Scott , Rob Herring , Sebastian Reichel Subject: [PATCH] phy: mapphone-mdm6600: Add USB PHY driver for MDM6600 on Droid 4 Date: Sat, 17 Feb 2018 13:07:23 -0800 Message-Id: <20180217210723.7013-1-tony@atomide.com> X-Mailer: git-send-email 2.16.1 Sender: linux-usb-owner@vger.kernel.org X-Mailing-List: linux-usb@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-Mailing-List: linux-kernel@vger.kernel.org List-ID: Let's add support for the GPIO controlled USB PHY on the MDM6600 modem. It is used on some Motorola Mapphone series of phones and tablets such as Droid 4. The MDM6600 is hardwired to the first OHCI port in the Droid 4 case, and is controlled by several GPIOs. The USB PHY is integrated into the MDM6600 device it seems. We know this as we get L3 errors from omap-usb-host if trying to use the PHY before MDM6600 is configured. The GPIOs controlling MDM6600 are used to power MDM660 on and off, to configure the USB start-up mode (normal mode versus USB flashing), and they also tell the state of the MDM6600 device. The two start-up mode GPIOs are dual-purposed and used for out of band (OOB) wake-up for USB and TS 27.010 serial mux. But we need to configure the USB start-up mode first to get MDM6600 booted in the right mode to be usable in the first place. For now, this driver just gives up the two start-up mode GPIOs after the modem has been configured to boot in normal mode. One of them we may want to configure for USB OOB wake in this driver later on, but that's a separate series of patches and needs more work. Note that the Motorola Mapphone Linux kernel tree has a "radio-ctrl" driver for modems. But it really does not control the radio at all, it just controls the modem power and start-up mode for USB. So I came to the conclusion that we're better off having this done in the USB PHY driver. For adding support for USB flashing mode, we can later on add a kernel module option for flash_mode=1 or something similar. Also note that currently there is no PM runtime support for the OHCI on omap variant SoCs. So for low(er) power idle states, currenty both ohci-platform and phy-mapphone-mdm6600 must be unloaded or unbound. For reference here is what I measured for total power consumption on an idle Droid 4 with and without USB related MDM6600 modules: idle lcd off phy-mapphone-mdm6600 ohci-platform 153mW 284mW 344mW So it seems that MDM6600 is currently not yet idling even with it's radio turned off, but that's something that is beyond the control of this USB PHY driver. Cc: devicetree@vger.kernel.org Cc: Mark Rutland Cc: Marcel Partap Cc: Michael Scott Cc: Rob Herring Cc: Sebastian Reichel Signed-off-by: Tony Lindgren --- .../bindings/phy/phy-mapphone-mdm6600.txt | 30 ++ drivers/phy/motorola/Kconfig | 9 + drivers/phy/motorola/Makefile | 1 + drivers/phy/motorola/phy-mapphone-mdm6600.c | 490 +++++++++++++++++++++ 4 files changed, 530 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt create mode 100644 drivers/phy/motorola/phy-mapphone-mdm6600.c diff --git a/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt b/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt new file mode 100644 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt @@ -0,0 +1,30 @@ +Device tree binding documentation for Motorola Mapphone MDM6600 USB PHY + +Required properties: +- compatible Must be "motorola,mapphone-mdm6600" +- enable-gpios GPIO to enable the USB PHY +- power-gpios GPIO to power on the device +- reset-gpios GPIO to reset the device +- mode-gpios Two GPIOs to configure MDM6600 USB start-up mode for + normal mode versus USB flashing mode +- status-gpios Three GPIOs to read the power state of the MDM6600 +- cmd-gpios Three GPIOs to control the power state of the MDM6600 + +Example: + +fsusb1_phy: fsusb1_phy { + compatible = "motorola,mapphone-mdm6600"; + enable-gpios = <&gpio3 31 GPIO_ACTIVE_LOW>; /* gpio_95 */ + power-gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>; /* gpio_54 */ + reset-gpios = <&gpio2 17 GPIO_ACTIVE_HIGH>; /* gpio_49 */ + mode-gpios = <&gpio5 20 GPIO_ACTIVE_HIGH>, /* gpio_148 */ + <&gpio5 21 GPIO_ACTIVE_HIGH>; /* gpio_149 */ + status-gpios = <&gpio2 23 GPIO_ACTIVE_HIGH>, /* gpio_55 */ + <&gpio2 21 GPIO_ACTIVE_HIGH>, /* gpio_53 */ + <&gpio2 20 GPIO_ACTIVE_HIGH>; /* gpio_52 */ + cmd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>, /* gpio_103 */ + <&gpio4 8 GPIO_ACTIVE_HIGH>, /* gpio_104 */ + <&gpio5 14 GPIO_ACTIVE_HIGH>; /* gpio_142 */ + #phy-cells = <0>; +}; + diff --git a/drivers/phy/motorola/Kconfig b/drivers/phy/motorola/Kconfig --- a/drivers/phy/motorola/Kconfig +++ b/drivers/phy/motorola/Kconfig @@ -10,3 +10,12 @@ config PHY_CPCAP_USB help Enable this for USB to work on Motorola phones and tablets such as Droid 4. + +config PHY_MAPPHONE_MDM6600 + tristate "Motorola Mapphone MDM6600 modem USB PHY driver" + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this for MDM6600 USB modem to work on Motorola phones + and tablets such as Droid 4. diff --git a/drivers/phy/motorola/Makefile b/drivers/phy/motorola/Makefile --- a/drivers/phy/motorola/Makefile +++ b/drivers/phy/motorola/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_PHY_CPCAP_USB) += phy-cpcap-usb.o +obj-$(CONFIG_PHY_MAPPHONE_MDM6600) += phy-mapphone-mdm6600.o diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c new file mode 100644 --- /dev/null +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Motorola Mapphone MDM6600 modem GPIO controlled USB PHY driver + * Copyright (C) 2018 Tony Lindgren + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PHY_MDM6600_STARTUP_DELAY_MS 3000 /* A least 2.2s usually */ + +/* + * MDM6600 status codes. These are copied from Motorola Mapphone Linux + * kernel tree. The BB naming here refers to "BaseBand" for modem. + */ +enum phy_mdm6600_status { + BP_STATUS_PANIC, /* Seems to be really off state */ + BP_STATUS_PANIC_BUSY_WAIT, + BP_STATUS_QC_DLOAD, + BP_STATUS_RAM_DOWNLOADER, /* MDM6600 USB flashing mode */ + BP_STATUS_PHONE_CODE_AWAKE, /* MDM6600 normal USB mode */ + BP_STATUS_PHONE_CODE_ASLEEP, + BP_STATUS_SHUTDOWN_ACK, + BP_STATUS_UNDEFINED, +}; + +static const char * const +phy_mdm6600_status_name[] = { + "off", "busy", "qc_dl", "ram_dl", "awake", + "asleep", "shutdown", "undefined", +}; + +/* + * MDM6600 command codes. These are copied from Motorola Mapphone Linux + * kernel tree. The AP naming here refers to "Application Processor". + */ +enum phy_mdm6600_cmd { + AP_STATUS_BP_PANIC_ACK, + AP_STATUS_DATA_ONLY_BYPASS, /* Reroute USB to CPCAP PHY */ + AP_STATUS_FULL_BYPASS, /* Reroute USB to CPCAP PHY */ + AP_STATUS_NO_BYPASS, /* Request normal start-up mode */ + AP_STATUS_BP_SHUTDOWN_REQ, /* Request device power off */ + AP_STATUS_BP_UNKNOWN_5, + AP_STATUS_BP_UNKNOWN_6, + AP_STATUS_UNDEFINED, +}; + +enum phy_mdm6600_lines { + PHY_MDM6600_ENABLE, /* USB PHY enable */ + PHY_MDM6600_POWER, /* Device power */ + PHY_MDM6600_RESET, /* Device reset */ + PHY_MDM6600_MODE0, /* USB boot mode flashing vs normal */ + PHY_MDM6600_MODE1, /* USB boot mode flashing vs normal */ + PHY_MDM6600_STATUS0, /* Device state */ + PHY_MDM6600_STATUS1, /* Device state */ + PHY_MDM6600_STATUS2, /* Device state */ + PHY_MDM6600_CMD0, /* Device command */ + PHY_MDM6600_CMD1, /* Device command */ + PHY_MDM6600_CMD2, /* Device command */ + PHY_MDM6600_NR_LINES, +}; + +struct phy_mdm6600 { + struct device *dev; + struct usb_phy phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct gpio_desc *gpio[PHY_MDM6600_NR_LINES]; + struct delayed_work bootup_work; + struct delayed_work status_work; + struct completion ack; + bool enabled; + int status; +}; + +static int phy_mdm6600_init(struct phy *x) +{ + struct phy_mdm6600 *ddata = phy_get_drvdata(x); + struct gpio_desc *enable_gpio = ddata->gpio[PHY_MDM6600_ENABLE]; + + if (!ddata->enabled) + return -EPROBE_DEFER; + + gpiod_set_value_cansleep(enable_gpio, 0); + + return 0; +} + +static int phy_mdm6600_power_on(struct phy *x) +{ + struct phy_mdm6600 *ddata = phy_get_drvdata(x); + struct gpio_desc *enable_gpio = ddata->gpio[PHY_MDM6600_ENABLE]; + + if (!ddata->enabled) + return -ENODEV; + + gpiod_set_value_cansleep(enable_gpio, 1); + + return 0; +} + +static int phy_mdm6600_power_off(struct phy *x) +{ + struct phy_mdm6600 *ddata = phy_get_drvdata(x); + struct gpio_desc *enable_gpio = ddata->gpio[PHY_MDM6600_ENABLE]; + + if (!ddata->enabled) + return -ENODEV; + + gpiod_set_value_cansleep(enable_gpio, 0); + + return 0; +} + +static const struct phy_ops gpio_usb_ops = { + .init = phy_mdm6600_init, + .power_on = phy_mdm6600_power_on, + .power_off = phy_mdm6600_power_off, + .owner = THIS_MODULE, +}; + +struct phy_mdm6600_map { + const char *name; + int nr_gpios; + int direction; +}; + +static const struct phy_mdm6600_map +phy_mdm6600_line_map[PHY_MDM6600_NR_LINES] = { + { "enable", 1, GPIOD_OUT_LOW, }, /* low = disabled */ + { "power", 1, GPIOD_OUT_LOW, }, /* low = off */ + { "reset", 1, GPIOD_OUT_HIGH, }, /* high = reset */ + { "mode", 2, GPIOD_OUT_LOW, }, + { "status", 3, GPIOD_IN, }, + { "cmd", 3, GPIOD_OUT_LOW, }, /* low = no command */ +}; + +/** + * phy_mdm6600_cmd() - send a command request to mdm6600 + * @ddata: device driver data + * + * Configures the three command request GPIOs to the specified value. + */ +static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) +{ + int i; + + val &= 0x7; + + for (i = PHY_MDM6600_CMD0; + i <= PHY_MDM6600_CMD2; i++) { + struct gpio_desc *gpio = ddata->gpio[i]; + int shift = (2 - (i - PHY_MDM6600_CMD0)); + + if (IS_ERR(gpio)) + return; + + gpiod_set_value_cansleep(gpio, (val & BIT(shift)) >> shift); + } +} + +/** + * phy_mdm6600_status() - read mdm6600 status lines + * @ddata: device driver data + */ +static void phy_mdm6600_status(struct work_struct *work) +{ + struct phy_mdm6600 *ddata; + struct device *dev; + int i, val; + + ddata = container_of(work, struct phy_mdm6600, status_work.work); + dev = ddata->dev; + + for (i = PHY_MDM6600_STATUS0; + i <= PHY_MDM6600_STATUS2; i++) { + struct gpio_desc *gpio = ddata->gpio[i]; + int shift = (2 - (i - PHY_MDM6600_STATUS0)); + + if (IS_ERR(ddata->gpio[i])) + continue; + val = gpiod_get_value_cansleep(gpio); + ddata->status &= ~(BIT(shift)); + ddata->status |= (val << shift); + } + dev_info(dev, "modem status: %i %s\n", + ddata->status, + phy_mdm6600_status_name[ddata->status & 7]); + complete(&ddata->ack); +} + +static irqreturn_t phy_mdm6600_irq_thread(int irq, void *data) +{ + struct phy_mdm6600 *ddata = data; + + schedule_delayed_work(&ddata->status_work, msecs_to_jiffies(10)); + + return IRQ_HANDLED; +} + +/** + * phy_mdm6600_init_irq() - initialize mdm6600 status IRQ lines + * @ddata: device driver data + */ +static void phy_mdm6600_init_irq(struct phy_mdm6600 *ddata) +{ + struct device *dev = ddata->dev; + int i, error, irq; + + for (i = PHY_MDM6600_STATUS0; + i <= PHY_MDM6600_STATUS2; i++) { + if (IS_ERR(ddata->gpio[i])) + continue; + + irq = gpiod_to_irq(ddata->gpio[i]); + if (irq <= 0) + continue; + + error = devm_request_threaded_irq(dev, irq, NULL, + phy_mdm6600_irq_thread, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "mdm6600", + ddata); + if (error) + dev_warn(dev, "no modem status irq%i: %i\n", + irq, error); + } +} + +/** + * phy_mdm6600_init_lines() - initialize mdm6600 GPIO lines + * @ddata: device driver data + */ +static int phy_mdm6600_init_lines(struct phy_mdm6600 *ddata) +{ + struct device *dev = ddata->dev; + int i, j, nr_gpio = 0; + + for (i = 0; i < ARRAY_SIZE(phy_mdm6600_line_map); i++) { + const struct phy_mdm6600_map *map = + &phy_mdm6600_line_map[i]; + + for (j = 0; j < map->nr_gpios; j++) { + struct gpio_desc **gpio = &ddata->gpio[nr_gpio]; + + *gpio = devm_gpiod_get_index(dev, + map->name, j, + map->direction); + if (IS_ERR(*gpio)) { + dev_info(dev, + "gpio %s error %li, already taken?\n", + map->name, PTR_ERR(*gpio)); + return PTR_ERR(*gpio); + } + nr_gpio++; + } + } + + return 0; +} + +/** + * phy_mdm6600_device_power_on() - power on mdm6600 device + * @ddata: device driver data + * + * To get the integrated USB phy in MDM6600 takes some hoops. We must ensure + * the shared USB bootmode GPIOs are configured, then request modem start-up, + * reset and power-up.. And then we need to give up the shared USB bootmode + * GPIOs as they are also used for Out of Band (OOB) wake for the USB and + * TS 27.010 serial mux. + */ +static int phy_mdm6600_device_power_on(struct phy_mdm6600 *ddata) +{ + struct gpio_desc *mode_gpio0 = ddata->gpio[PHY_MDM6600_MODE0]; + struct gpio_desc *mode_gpio1 = ddata->gpio[PHY_MDM6600_MODE1]; + struct gpio_desc *reset_gpio = ddata->gpio[PHY_MDM6600_RESET]; + struct gpio_desc *power_gpio = ddata->gpio[PHY_MDM6600_POWER]; + int error = 0; + + /* + * Shared GPIOs must be low for normal USB mode. After booting, + * we don't need them. These can be also used to configure USB + * flashing mode later on based on a module parameter. + */ + gpiod_set_value_cansleep(mode_gpio0, 0); + gpiod_set_value_cansleep(mode_gpio1, 0); + + /* Request start-up mode */ + phy_mdm6600_cmd(ddata, AP_STATUS_NO_BYPASS); + + /* Request a reset first */ + gpiod_set_value_cansleep(reset_gpio, 0); + msleep(100); + + /* Toggle power GPIO to request mdm6600 to start */ + gpiod_set_value_cansleep(power_gpio, 1); + msleep(100); + gpiod_set_value_cansleep(power_gpio, 0); + + /* + * Looks like the USB PHY is at least 2.2 seconds. + * If we try to use it before that, we will get L3 errors + * from omap-usb-host trying to access the PHY. See also + * phy_mdm6600_init() for -EPROBE_DEFER. + */ + msleep(PHY_MDM6600_STARTUP_DELAY_MS); + ddata->enabled = true; + + /* Booting up the rest of MDM6600 will take total about 8 seconds */ + dev_info(ddata->dev, "Waiting for power up request to complete..\n"); + if (wait_for_completion_timeout(&ddata->ack, + msecs_to_jiffies(8000))) { + dev_info(ddata->dev, "Powered up OK\n"); + } else { + ddata->enabled = false; + error = -ETIMEDOUT; + dev_err(ddata->dev, "Timed out powering up\n"); + } + + /* Give up shared GPIOs now, they will be used for OOB wake */ + devm_gpiod_put(ddata->dev, mode_gpio0); + ddata->gpio[PHY_MDM6600_MODE0] = ERR_PTR(-ENODEV); + devm_gpiod_put(ddata->dev, mode_gpio1); + ddata->gpio[PHY_MDM6600_MODE0] = ERR_PTR(-ENODEV); + + return error; +} + +/** + * phy_mdm6600_device_power_off() - power off mdm6600 device + * @ddata: device driver data + */ +static void phy_mdm6600_device_power_off(struct phy_mdm6600 *ddata) +{ + struct gpio_desc *reset_gpio = + ddata->gpio[PHY_MDM6600_RESET]; + + ddata->enabled = false; + phy_mdm6600_cmd(ddata, AP_STATUS_BP_SHUTDOWN_REQ); + msleep(100); + + if (reset_gpio >= 0) + gpiod_set_value_cansleep(reset_gpio, 1); + + dev_info(ddata->dev, "Waiting for power down request to complete.. "); + if (wait_for_completion_timeout(&ddata->ack, + msecs_to_jiffies(5000))) { + dev_info(ddata->dev, "Powered down OK\n"); + } else { + dev_err(ddata->dev, "Timed out powering down\n"); + } +} + +static void phy_mdm6600_deferred_power_on(struct work_struct *work) +{ + struct phy_mdm6600 *ddata; + int error; + + ddata = container_of(work, struct phy_mdm6600, bootup_work.work); + + error = phy_mdm6600_device_power_on(ddata); + if (error) + dev_err(ddata->dev, "Device not functional\n"); +} + +#ifdef CONFIG_OF +static const struct of_device_id phy_mdm6600_id_table[] = { + { .compatible = "motorola,mapphone-mdm6600", }, + {}, +}; +MODULE_DEVICE_TABLE(of, phy_mdm6600_id_table); +#endif + +static int phy_mdm6600_probe(struct platform_device *pdev) +{ + struct phy_mdm6600 *ddata; + struct usb_otg *otg; + const struct of_device_id *of_id; + int error; + + of_id = of_match_device(of_match_ptr(phy_mdm6600_id_table), + &pdev->dev); + if (!of_id) + return -EINVAL; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + INIT_DELAYED_WORK(&ddata->bootup_work, + phy_mdm6600_deferred_power_on); + INIT_DELAYED_WORK(&ddata->status_work, phy_mdm6600_status); + init_completion(&ddata->ack); + + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + ddata->dev = &pdev->dev; + ddata->phy.dev = ddata->dev; + ddata->phy.label = "phy_mdm6600"; + ddata->phy.otg = otg; + ddata->phy.type = USB_PHY_TYPE_USB2; + otg->usb_phy = &ddata->phy; + + platform_set_drvdata(pdev, ddata); + + error = phy_mdm6600_init_lines(ddata); + if (error) + return error; + + phy_mdm6600_init_irq(ddata); + + ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops); + if (IS_ERR(ddata->generic_phy)) { + error = PTR_ERR(ddata->generic_phy); + goto cleanup; + } + + phy_set_drvdata(ddata->generic_phy, ddata); + + ddata->phy_provider = + devm_of_phy_provider_register(ddata->dev, + of_phy_simple_xlate); + if (IS_ERR(ddata->phy_provider)) { + error = PTR_ERR(ddata->phy_provider); + goto cleanup; + } + + schedule_delayed_work(&ddata->bootup_work, 0); + + /* + * See phy_mdm6600_device_power_on(). We should be able + * to remove this eventually when ohci-platform can deal + * with -EPROBE_DEFER. + */ + msleep(PHY_MDM6600_STARTUP_DELAY_MS + 500); + + usb_add_phy_dev(&ddata->phy); + + return 0; + +cleanup: + phy_mdm6600_device_power_off(ddata); + return error; +} + +static int phy_mdm6600_remove(struct platform_device *pdev) +{ + struct phy_mdm6600 *ddata = platform_get_drvdata(pdev); + struct gpio_desc *reset_gpio = ddata->gpio[PHY_MDM6600_RESET]; + + if (!IS_ERR(reset_gpio)) + gpiod_set_value_cansleep(reset_gpio, 1); + + phy_mdm6600_device_power_off(ddata); + + cancel_delayed_work_sync(&ddata->bootup_work); + cancel_delayed_work_sync(&ddata->status_work); + + return 0; +} + +static struct platform_driver phy_mdm6600_driver = { + .probe = phy_mdm6600_probe, + .remove = phy_mdm6600_remove, + .driver = { + .name = "phy-mapphone-mdm6600", + .of_match_table = of_match_ptr(phy_mdm6600_id_table), + }, +}; + +module_platform_driver(phy_mdm6600_driver); + +MODULE_ALIAS("platform:gpio_usb"); +MODULE_AUTHOR("Tony Lindgren "); +MODULE_DESCRIPTION("generic gpio usb phy driver"); +MODULE_LICENSE("GPL v2"); -- 2.16.1