Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2b343f7 authored by Se Wang (Patrick) Oh's avatar Se Wang (Patrick) Oh Committed by Hanumant Singh
Browse files

esoc: mdm-4x: add support mdm9x55



Add support for mdm9x55, using mdm4x data as a starting point.

Change-Id: Idf1e6961399a7d315fd67071e14aaa14d609de44
Signed-off-by: default avatarSe Wang (Patrick) Oh <sewango@codeaurora.org>
Signed-off-by: default avatarHanumant Singh <hanumant@codeaurora.org>
parent 6cb8923f
Loading
Loading
Loading
Loading
+148 −0
Original line number Diff line number Diff line
Attached MDM Modem Devices

External modems are devices that are attached to the msm and controlled by gpios.
There is also a data channel between the msm and the external modem that sometimes needs
to be reset.

Required Properties:
- compatible:	The bus devices need to be compatible with
		"qcom,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom, ext-mdm9x45",
		"qcom,ext-mdm9x55".

Required named gpio properties:
- qcom,mdm2ap-errfatal-gpio: gpio for the external modem to indicate to the apps processor
		of an error fatal condition on the modem.
- qcom,ap2mdm-errfatal-gpio: gpio for the apps processor to indicate to the external modem
		of an error fatal condition on the apps processor.
- qcom,mdm2ap-status-gpio: gpio to indicate to the apps processor when there is a watchdog
		bite on the external modem.
- qcom,ap2mdm-status-gpio: gpio for the apps processor to indicate to the modem that an apps
		processor watchdog bite has occurred.
- qcom,ap2mdm-soft-reset-gpio: gpio for the apps processor to use to soft-reset the external
		modem. If the flags parameter has a value of 0x1 then the gpio is active LOW.

Required Interrupts:
- "err_fatal_irq": Interrupt generated on the apps processor when the error fatal gpio is pulled
		high by the external modem.
- "status_irq": Interrupt generated on the apps processor when the mdm2ap-status gpio falls low
		on the external modem. This usually indicates a watchdog bite on the modem.
- "plbrdy_irq": Interrupt generated on the aps processor when the mdm2ap-pblrdy gpio is pulled
		either high or low by the external modem. This is an indication that the modem
		has rebooted.
- "mdm2ap_vddmin_irq": Interrupt generated on the apps processor when the external modem goes
		into vddmin power state.

Optional named gpio properties:
- qcom,mdm2ap-pblrdy-gpio: gpio used by some external modems to indicate when the modem has
		booted into the PBL bootloader.
- qcom,ap2mdm-wakeup-gpio: gpio used by the apps processor to wake the external modem
		out of a low power state.
- qcom,ap2mdm-chnl-rdy-gpio: gpio used by the apps processor to inform the external modem
		that data link is ready.
- qcom,mdm2ap-wakeup-gpio: gpio from the external modem to the apps processor to wake it
		out of a low power state.
- qcom,ap2mdm-vddmin-gpio: gpio to indicate to the external modem when the apps processor
		is about to enter vddmin power state.
- qcom,mdm2ap-vddmin-gpio: gpio used by the external modem to inform the apps processor
		when it is about to enter vddmin power state.
- qcom,ap2mdm-kpdpwr-gpio: gpio used to simulate a power button press on the external
		modem. Some modems use this as part of their initial power-up sequence.
		If the "flags" parameter has a value of 0x1 then it is active LOW.
- qcom,ap2mdm-pmic-pwr-en-gpio: Some modems need this gpio for the apps processor to enable
		the pmic on the external modem.
- qcom,use-usb-port-gpio: some modems use this gpio to switch a port connection from uart to usb.
		This is used during firmware upgrade of some modems.
- qcom,mdm-link-detect-gpio: some modems may support two interfaces. This gpio
		indicates whether only one or both links can be used.

Optional driver parameters:
- qcom,ramdump-delay-ms: time in milliseconds to wait before starting to collect ramdumps.
		This interval is the time to wait after an error on the external modem is
		signaled to the apps processor before starting to collect ramdumps. Its
		value depends on the type of external modem (e.g. MDM vs QSC), and how
		error fatal handing is done on the modem.
		The default value is 2 seconds (2000 milliseconds) as specified by the
		mdm9x15 software developer. Consultation with the developer of the modem
		software is required to determine this value for that modem.
- qcom,ps-hold-delay-ms: minimum delay in milliseconds between consecutive PS_HOLD toggles.
		SGLTE targets that use a QSC1215 modem require a minimum delay between consecutive
		toggling of the PS_HOLD pmic input. For one target it is 500 milliseconds but it
		may vary depending on the target and how the external modem is connected. The value
		is specified by the hardware designers.
- qcom,early-power-on: boolean flag to indicate if to power on the modem when the device is probed.
- qcom,sfr-query: boolean flag to indicate if to query the modem for a reset reason.
- qcom,no-powerdown-after-ramdumps: boolean flag to indicate if to power down the modem after ramdumps.
- qcom,no-a2m-errfatal-on-ssr: boolean to tell driver not to raise ap2mdm errfatal during SSR.
- qcom,no-reset-on-first-powerup: boolean to tell driver not to reset the modem when first
		powering up the modem.
- qcom,ramdump-timeout-ms: ramdump timeout interval in milliseconds.
		This interval is the time to wait for collection of the external modem's ramdump
		to complete. It's value depends on the speed of the data connection between the
		external modem and the apps processor on the platform. If the connection is a
		UART port then this delay needs to be longer in order to avoid premature timeout
		of the ramdump collection.
		The default value is 2 minutes (120000 milliseconds) which is based on the
		measured time it takes over a UART connection. It is reduced when the data
		connection is an HSIC port. The value is usually tuned empirically for a
		particular target.
- qcom,image-upgrade-supported: boolean flag to indicate if software upgrade is supported.
- qcom,support-shutdown: boolean flag to indicate if graceful shutdown is supported.
- qcom,vddmin-drive-strength: drive strength in milliamps of the ap2mdm-vddmin gpio.
		The ap2mdm_vddmin gpio is controlled by the RPM processor. It is pulled low
		to indicate to the external modem that the apps processor has entered vddmin
		state, and high to indicate the reverse. Its parameters are passed to the RPM
		software from the HLOS because the RPM software has to way of saving this type
		of configuration when an external modem is attached.
		The value of the drive strength is specified by the hardware designers. A value
		of 8 milliamps is typical.
		This property is ignored if the property "qcom,ap2mdm-vddmin-gpio" is
		not set.
- qcom,vddmin-modes: a string indicating the "modes" requested for the ap2mdm-vddmin gpio.
		This value is passed to RPM and is used by the RPM module to determine the
		gpio mux function. The only currently supported modes string is "normal" and
		corresponds to the value 0x03 that is passed to RPM.
- qcom,restart-group: List of subsystems that will need to restart together.
- qcom,mdm-dual-link: Boolean indicates whether both links can used for
		communication.
- qcom,ssctl-instance-id: Instance id used by the subsystem to connect with the SSCTL service.
- qcom,sysmon-id: platform device id that sysmon is probed with for the subsystem.
- qcom,pil-force-shutdown: Boolean. If set, the SSR framework will not trigger graceful shutdown
			   on behalf of the subsystem driver.

Example:
	mdm0: qcom,mdm0 {
                compatible = "qcom,mdm2-modem";
		cell-index = <0>;
		#address-cells = <0>;
                interrupt-parent = <&mdm0>;
                interrupts = <0 1 2 3>;
                #interrupt-cells = <1>;
                interrupt-map-mask = <0xffffffff>;
                interrupt-map =
			<0 &msmgpio 82 0x3
			1 &msmgpio 46 0x3
			2 &msmgpio 80 0x3
			3 &msmgpio 27 0x3>;
                interrupt-names =
			"err_fatal_irq",
			"status_irq",
			"plbrdy_irq",
			"mdm2ap_vddmin_irq";

                qcom,mdm2ap-errfatal-gpio = <&msmgpio 82 0x00>;
		qcom,ap2mdm-errfatal-gpio = <&msmgpio 106 0x00>;
		qcom,mdm2ap-status-gpio   = <&msmgpio 46 0x00>;
		qcom,ap2mdm-status-gpio   = <&msmgpio 105 0x00>;
		qcom,ap2mdm-soft-reset-gpio = <&msmgpio 24 0x00>;
		qcom,mdm2ap-pblrdy-gpio = <&msmgpio 80 0x00>;
		qcom,ap2mdm-wakeup-gpio = <&msmgpio 104 0x00>;
		qcom,ap2mdm-vddmin-gpio = <&msmgpio 108 0x00>;
		qcom,mdm2ap-vddmin-gpio = <&msmgpio 27 0x00>;

                qcom,ramdump-delay-ms = <2000>;
                qcom,ramdump-timeout-ms = <120000>;
                qcom,vddmin-modes  = "normal";
                qcom,vddmin-drive-strength = <8>;
		qcom,ssctl-instance-id = <10>;
		qcom,sysmon-id = <20>;
        };
+2 −2
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ config ESOC_DEBUG
	  and individual esoc drivers.

config ESOC_MDM_4x
	bool "Add support for external mdm9x25/mdm9x35"
	bool "Add support for external mdm9x25/mdm9x35/mdm9x45/mdm9x55"
	help
	  In some qualcomm boards, an external modem such as mdm9x25 or mdm9x35
	  is connected to a primary msm. The primary soc can control/monitor
@@ -46,7 +46,7 @@ config ESOC_MDM_DRV
	tristate "Command engine for 4x series external modems"
	help
	  Provides a command engine to control the behavior of an external modem
	  such as mdm9x25/mdm9x35/QSC. Allows the primary soc to put the
	  such as mdm9x25/mdm9x35/mdm9x45/mdm9x55/QSC. Allows the primary soc to put the
	  external modem in a specific mode. Also listens for events on the
	  external modem.
endif
+82 −2
Original line number Diff line number Diff line
@@ -621,10 +621,11 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev)
	gpio_direction_input(MDM_GPIO(mdm, MDM2AP_ERRFATAL));

	/* ERR_FATAL irq. */
	irq = platform_get_irq_byname(pdev, "err_fatal_irq");
	irq = gpio_to_irq(MDM_GPIO(mdm, MDM2AP_ERRFATAL));
	if (irq < 0) {
		dev_err(dev, "bad MDM2AP_ERRFATAL IRQ resource\n");
		goto errfatal_err;

	}
	ret = request_irq(irq, mdm_errfatal,
			IRQF_TRIGGER_RISING , "mdm errfatal", mdm);
@@ -638,7 +639,7 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev)

errfatal_err:
	 /* status irq */
	irq = platform_get_irq_byname(pdev, "status_irq");
	irq = gpio_to_irq(MDM_GPIO(mdm, MDM2AP_STATUS));
	if (irq < 0) {
		dev_err(dev, "%s: bad MDM2AP_STATUS IRQ resource, err = %d\n",
				__func__, irq);
@@ -886,6 +887,77 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm,
	return 0;
}

static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
					const struct mdm_ops *ops,
					struct platform_device *pdev)
{
	int ret;
	struct device_node *node;
	struct esoc_clink *esoc;
	const struct esoc_clink_ops *const clink_ops = ops->clink_ops;
	const struct mdm_pon_ops *pon_ops = ops->pon_ops;

	mdm->dev = &pdev->dev;
	mdm->pon_ops = pon_ops;
	node = pdev->dev.of_node;
	esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
	if (IS_ERR(esoc)) {
		dev_err(mdm->dev, "cannot allocate esoc device\n");
		return PTR_ERR(esoc);
	}
	mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
	if (!mdm->mdm_queue) {
		dev_err(mdm->dev, "could not create mdm_queue\n");
		return -ENOMEM;
	}
	mdm->irq_mask = 0;
	mdm->ready = false;
	ret = mdm_dt_parse_gpios(mdm);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "parsing gpio done\n");
	ret = mdm_pon_dt_init(mdm);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "pon dt init done\n");
	ret = mdm_pinctrl_init(mdm);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "pinctrl init done\n");
	ret = mdm_pon_setup(mdm);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "pon setup done\n");
	ret = mdm_configure_ipc(mdm, pdev);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "ipc configure done\n");
	esoc->name = MDM9x55_LABEL;
	mdm->dual_interface = of_property_read_bool(node,
						"qcom,mdm-dual-link");
	esoc->link_name = MDM9x55_PCIE;
	esoc->clink_ops = clink_ops;
	esoc->parent = mdm->dev;
	esoc->owner = THIS_MODULE;
	esoc->np = pdev->dev.of_node;
	set_esoc_clink_data(esoc, mdm);
	ret = esoc_clink_register(esoc);
	if (ret) {
		dev_err(mdm->dev, "esoc registration failed\n");
		return ret;
	}
	dev_dbg(mdm->dev, "esoc registration done\n");
	init_completion(&mdm->debug_done);
	INIT_WORK(&mdm->mdm_status_work, mdm_status_fn);
	INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason);
	INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check);
	mdm->get_restart_reason = false;
	mdm->debug_fail = false;
	mdm->esoc = esoc;
	mdm->init = 0;
	return 0;
}

static struct esoc_clink_ops mdm_cops = {
	.cmd_exe = mdm_cmd_exe,
	.get_status = mdm_get_status,
@@ -904,11 +976,19 @@ static struct mdm_ops mdm9x35_ops = {
	.pon_ops = &mdm9x35_pon_ops,
};

static struct mdm_ops mdm9x55_ops = {
	.clink_ops = &mdm_cops,
	.config_hw = mdm9x55_setup_hw,
	.pon_ops = &mdm9x55_pon_ops,
};

static const struct of_device_id mdm_dt_match[] = {
	{ .compatible = "qcom,ext-mdm9x25",
		.data = &mdm9x25_ops, },
	{ .compatible = "qcom,ext-mdm9x35",
		.data = &mdm9x35_ops, },
	{ .compatible = "qcom,ext-mdm9x55",
		.data = &mdm9x55_ops, },
	{},
};
MODULE_DEVICE_TABLE(of, mdm_dt_match);
+4 −0
Original line number Diff line number Diff line
@@ -254,6 +254,10 @@ static struct esoc_compat compat_table[] = {
		.name = "MDM9x35",
		.data = NULL,
	},
	{
		.name = "MDM9x55",
		.data = NULL,
	},
};

static struct esoc_drv esoc_ssr_drv = {
+65 −1
Original line number Diff line number Diff line
@@ -36,6 +36,31 @@ static int mdm4x_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
	return 0;
}

/* This function can be called from atomic context. */
static int mdm9x55_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
{
	int soft_reset_direction_assert = 0,
	    soft_reset_direction_de_assert = 1;

	if (mdm->soft_reset_inverted) {
		soft_reset_direction_assert = 1;
		soft_reset_direction_de_assert = 0;
	}
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			soft_reset_direction_assert);
	/*
	 * Allow PS hold assert to be detected
	 */
	if (!atomic)
		usleep_range(203000, 300000);
	else
		mdelay(203);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			soft_reset_direction_de_assert);
	return 0;
}


static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm)
{
	int i;
@@ -43,7 +68,7 @@ static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm)
	struct device *dev = mdm->dev;

	dev_dbg(dev, "Powering on modem for the first time\n");
	mdm4x_toggle_soft_reset(mdm, false);
	mdm_toggle_soft_reset(mdm, false);
	/* Add a delay to allow PON sequence to complete*/
	msleep(50);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1);
@@ -87,6 +112,26 @@ static int mdm4x_power_down(struct mdm_ctrl *mdm)
	return 0;
}

static int mdm9x55_power_down(struct mdm_ctrl *mdm)
{
	struct device *dev = mdm->dev;
	int soft_reset_direction = mdm->soft_reset_inverted ? 1 : 0;
	/* Assert the soft reset line whether mdm2ap_status went low or not */
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
					soft_reset_direction);
	dev_dbg(dev, "Doing a hard reset\n");
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
						soft_reset_direction);
	/*
	* Currently, there is a debounce timer on the charm PMIC. It is
	* necessary to hold the PMIC RESET low for 406ms
	* for the reset to fully take place. Sleep here to ensure the
	* reset has occurred before the function exits.
	*/
	msleep(406);
	return 0;
}

static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
{
	dev_dbg(mdm->dev, "Triggering mdm cold reset");
@@ -97,6 +142,16 @@ static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
			!mdm->soft_reset_inverted);
}

static void mdm9x55_cold_reset(struct mdm_ctrl *mdm)
{
	dev_dbg(mdm->dev, "Triggering mdm cold reset");
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			!!mdm->soft_reset_inverted);
	mdelay(334);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			!mdm->soft_reset_inverted);
}

static int mdm4x_pon_dt_init(struct mdm_ctrl *mdm)
{
	int val;
@@ -154,3 +209,12 @@ struct mdm_pon_ops mdm9x45_pon_ops = {
	.dt_init = mdm4x_pon_dt_init,
	.setup = mdm4x_pon_setup,
};

struct mdm_pon_ops mdm9x55_pon_ops = {
	.pon = mdm4x_do_first_power_on,
	.soft_reset = mdm9x55_toggle_soft_reset,
	.poff_force = mdm9x55_power_down,
	.cold_reset = mdm9x55_cold_reset,
	.dt_init = mdm4x_pon_dt_init,
	.setup = mdm4x_pon_setup,
};
Loading