#define PHY_MDM6600_PHY_DELAY_MS 4000 /* PHY enable 2.2s to 3.5s */ #define PHY_MDM6600_ENABLED_DELAY_MS 8000 /* 8s more total for MDM6600 */ #define PHY_MDM6600_WAKE_KICK_MS 600 /* time on after GPIO toggle */ #define MDM6600_MODEM_IDLE_DELAY_MS 1000 /* modem after USB suspend */ #define MDM6600_MODEM_WAKE_DELAY_MS 200 /* modem response after idle */
enum phy_mdm6600_bootmode_lines {
PHY_MDM6600_MODE0, /* out USB mode0 and OOB wake */
PHY_MDM6600_MODE1, /* out USB mode1, in OOB wake */
PHY_MDM6600_NR_MODE_LINES,
};
/* * MDM6600 command codes. These are based on Motorola Mapphone Linux * kernel tree.
*/ enum phy_mdm6600_cmd {
PHY_MDM6600_CMD_BP_PANIC_ACK,
PHY_MDM6600_CMD_DATA_ONLY_BYPASS, /* Reroute USB to CPCAP PHY */
PHY_MDM6600_CMD_FULL_BYPASS, /* Reroute USB to CPCAP PHY */
PHY_MDM6600_CMD_NO_BYPASS, /* Request normal USB mode */
PHY_MDM6600_CMD_BP_SHUTDOWN_REQ, /* Request device power off */
PHY_MDM6600_CMD_BP_UNKNOWN_5,
PHY_MDM6600_CMD_BP_UNKNOWN_6,
PHY_MDM6600_CMD_UNDEFINED,
};
/* * MDM6600 status codes. These are based on Motorola Mapphone Linux * kernel tree.
*/ enum phy_mdm6600_status {
PHY_MDM6600_STATUS_PANIC, /* Seems to be really off */
PHY_MDM6600_STATUS_PANIC_BUSY_WAIT,
PHY_MDM6600_STATUS_QC_DLOAD,
PHY_MDM6600_STATUS_RAM_DOWNLOADER, /* MDM6600 USB flashing mode */
PHY_MDM6600_STATUS_PHONE_CODE_AWAKE, /* MDM6600 normal USB mode */
PHY_MDM6600_STATUS_PHONE_CODE_ASLEEP,
PHY_MDM6600_STATUS_SHUTDOWN_ACK,
PHY_MDM6600_STATUS_UNDEFINED,
};
/** * phy_mdm6600_cmd() - send a command request to mdm6600 * @ddata: device driver data * @val: value of cmd to be set * * Configures the three command request GPIOs to the specified value.
*/ staticvoid phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
{
DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES);
/** * phy_mdm6600_wakeirq_thread - handle mode1 line OOB wake after booting * @irq: interrupt * @data: interrupt handler data * * GPIO mode1 is used initially as output to configure the USB boot * mode for mdm6600. After booting it is used as input for OOB wake * signal from mdm6600 to the SoC. Just use it for debug info only * for now.
*/ static irqreturn_t phy_mdm6600_wakeirq_thread(int irq, void *data)
{ struct phy_mdm6600 *ddata = data; struct gpio_desc *mode_gpio1; int error, wakeup;
mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
wakeup = gpiod_get_value(mode_gpio1); if (!wakeup) return IRQ_NONE;
dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n", wakeup);
error = pm_runtime_get_sync(ddata->dev); if (error < 0) {
pm_runtime_put_noidle(ddata->dev);
return IRQ_NONE;
}
/* Just wake-up and kick the autosuspend timer */
pm_runtime_mark_last_busy(ddata->dev);
pm_runtime_put_autosuspend(ddata->dev);
return IRQ_HANDLED;
}
/** * phy_mdm6600_init_irq() - initialize mdm6600 status IRQ lines * @ddata: device driver data
*/ staticvoid 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++) { struct gpio_desc *gpio = ddata->status_gpios->desc[i];
irq = gpiod_to_irq(gpio); 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);
}
}
struct phy_mdm6600_map { constchar *name; int direction;
};
if (ddata->cmd_gpios->ndescs != PHY_MDM6600_NR_CMD_LINES) return -EINVAL;
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 recycle 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.
*/ staticint phy_mdm6600_device_power_on(struct phy_mdm6600 *ddata)
{ struct gpio_desc *mode_gpio0, *mode_gpio1, *reset_gpio, *power_gpio; int error = 0, wakeirq;
/* * Shared GPIOs must be low for normal USB mode. After booting * they are used for OOB wake signaling. 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 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 needs between 2.2 to 4 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_PHY_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(PHY_MDM6600_ENABLED_DELAY_MS))) { if (ddata->status > PHY_MDM6600_STATUS_PANIC &&
ddata->status < PHY_MDM6600_STATUS_SHUTDOWN_ACK)
dev_info(ddata->dev, "Powered up OK\n");
} else {
ddata->enabled = false;
error = -ETIMEDOUT;
dev_err(ddata->dev, "Timed out powering up\n");
}
/* Reconfigure mode1 GPIO as input for OOB wake */
gpiod_direction_input(mode_gpio1);
wakeirq = gpiod_to_irq(mode_gpio1); if (wakeirq <= 0) return wakeirq;
dev_info(ddata->dev, "Waiting for power down request to complete.. "); if (wait_for_completion_timeout(&ddata->ack,
msecs_to_jiffies(5000))) { if (ddata->status == PHY_MDM6600_STATUS_PANIC)
dev_info(ddata->dev, "Powered down OK\n");
} else {
dev_err(ddata->dev, "Timed out powering down\n");
}
/* * Keep reset gpio high with padconf internal pull-up resistor to * prevent modem from waking up during deeper SoC idle states. The * gpio bank lines can have glitches if not in the always-on wkup * domain.
*/
error = pinctrl_pm_select_sleep_state(ddata->dev); if (error)
dev_warn(ddata->dev, "%s: error with sleep_state: %i\n",
__func__, error);
}
staticvoid phy_mdm6600_deferred_power_on(struct work_struct *work)
{ struct phy_mdm6600 *ddata; int error;
error = phy_mdm6600_device_power_on(ddata); if (error)
dev_err(ddata->dev, "Device not functional\n");
}
/* * USB suspend puts mdm6600 into low power mode. For any n_gsm using apps, * we need to keep the modem awake by kicking it's mode0 GPIO. This will * keep the modem awake for about 1.2 seconds. When no n_gsm apps are using * the modem, runtime PM auto mode can be enabled so modem can enter low * power mode.
*/ staticvoid phy_mdm6600_wake_modem(struct phy_mdm6600 *ddata)
{ struct gpio_desc *mode_gpio0;
/* * The modem does not always stay awake 1.2 seconds after toggling * the wake GPIO, and sometimes it idles after about some 600 ms * making writes time out.
*/
schedule_delayed_work(&ddata->modem_wake_work,
msecs_to_jiffies(PHY_MDM6600_WAKE_KICK_MS));
}
/* * 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_PHY_DELAY_MS + 500);
/* * Enable PM runtime only after PHY has been powered up properly. * It is currently only needed after USB suspends mdm6600 and n_gsm * needs to access the device. We don't want to do this earlier as * gpio mode0 pin doubles as mdm6600 wake-up gpio.
*/
pm_runtime_use_autosuspend(ddata->dev);
pm_runtime_set_autosuspend_delay(ddata->dev,
MDM6600_MODEM_IDLE_DELAY_MS);
pm_runtime_enable(ddata->dev);
error = pm_runtime_get_sync(ddata->dev); if (error < 0) {
dev_warn(ddata->dev, "failed to wake modem: %i\n", error);
pm_runtime_put_noidle(ddata->dev); goto cleanup;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.