| .. | .. |
|---|
| 16 | 16 | #include <linux/gpio/consumer.h> |
|---|
| 17 | 17 | #include <linux/of_platform.h> |
|---|
| 18 | 18 | #include <linux/phy/phy.h> |
|---|
| 19 | +#include <linux/pinctrl/consumer.h> |
|---|
| 19 | 20 | |
|---|
| 20 | 21 | #define PHY_MDM6600_PHY_DELAY_MS 4000 /* PHY enable 2.2s to 3.5s */ |
|---|
| 21 | 22 | #define PHY_MDM6600_ENABLED_DELAY_MS 8000 /* 8s more total for MDM6600 */ |
|---|
| .. | .. |
|---|
| 121 | 122 | { |
|---|
| 122 | 123 | struct phy_mdm6600 *ddata = phy_get_drvdata(x); |
|---|
| 123 | 124 | struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; |
|---|
| 125 | + int error; |
|---|
| 124 | 126 | |
|---|
| 125 | 127 | if (!ddata->enabled) |
|---|
| 126 | 128 | return -ENODEV; |
|---|
| 127 | 129 | |
|---|
| 130 | + error = pinctrl_pm_select_default_state(ddata->dev); |
|---|
| 131 | + if (error) |
|---|
| 132 | + dev_warn(ddata->dev, "%s: error with default_state: %i\n", |
|---|
| 133 | + __func__, error); |
|---|
| 134 | + |
|---|
| 128 | 135 | gpiod_set_value_cansleep(enable_gpio, 1); |
|---|
| 136 | + |
|---|
| 137 | + /* Allow aggressive PM for USB, it's only needed for n_gsm port */ |
|---|
| 138 | + if (pm_runtime_enabled(&x->dev)) |
|---|
| 139 | + phy_pm_runtime_put(x); |
|---|
| 129 | 140 | |
|---|
| 130 | 141 | return 0; |
|---|
| 131 | 142 | } |
|---|
| .. | .. |
|---|
| 134 | 145 | { |
|---|
| 135 | 146 | struct phy_mdm6600 *ddata = phy_get_drvdata(x); |
|---|
| 136 | 147 | struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; |
|---|
| 148 | + int error; |
|---|
| 137 | 149 | |
|---|
| 138 | 150 | if (!ddata->enabled) |
|---|
| 139 | 151 | return -ENODEV; |
|---|
| 140 | 152 | |
|---|
| 153 | + /* Paired with phy_pm_runtime_put() in phy_mdm6600_power_on() */ |
|---|
| 154 | + if (pm_runtime_enabled(&x->dev)) { |
|---|
| 155 | + error = phy_pm_runtime_get(x); |
|---|
| 156 | + if (error < 0 && error != -EINPROGRESS) |
|---|
| 157 | + dev_warn(ddata->dev, "%s: phy_pm_runtime_get: %i\n", |
|---|
| 158 | + __func__, error); |
|---|
| 159 | + } |
|---|
| 160 | + |
|---|
| 141 | 161 | gpiod_set_value_cansleep(enable_gpio, 0); |
|---|
| 162 | + |
|---|
| 163 | + error = pinctrl_pm_select_sleep_state(ddata->dev); |
|---|
| 164 | + if (error) |
|---|
| 165 | + dev_warn(ddata->dev, "%s: error with sleep_state: %i\n", |
|---|
| 166 | + __func__, error); |
|---|
| 142 | 167 | |
|---|
| 143 | 168 | return 0; |
|---|
| 144 | 169 | } |
|---|
| .. | .. |
|---|
| 153 | 178 | /** |
|---|
| 154 | 179 | * phy_mdm6600_cmd() - send a command request to mdm6600 |
|---|
| 155 | 180 | * @ddata: device driver data |
|---|
| 181 | + * @val: value of cmd to be set |
|---|
| 156 | 182 | * |
|---|
| 157 | 183 | * Configures the three command request GPIOs to the specified value. |
|---|
| 158 | 184 | */ |
|---|
| 159 | 185 | static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) |
|---|
| 160 | 186 | { |
|---|
| 161 | | - int values[PHY_MDM6600_NR_CMD_LINES]; |
|---|
| 162 | | - int i; |
|---|
| 187 | + DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES); |
|---|
| 163 | 188 | |
|---|
| 164 | | - val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1; |
|---|
| 165 | | - for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++) |
|---|
| 166 | | - values[i] = (val & BIT(i)) >> i; |
|---|
| 189 | + values[0] = val; |
|---|
| 167 | 190 | |
|---|
| 168 | 191 | gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES, |
|---|
| 169 | | - ddata->cmd_gpios->desc, values); |
|---|
| 192 | + ddata->cmd_gpios->desc, |
|---|
| 193 | + ddata->cmd_gpios->info, values); |
|---|
| 170 | 194 | } |
|---|
| 171 | 195 | |
|---|
| 172 | 196 | /** |
|---|
| 173 | 197 | * phy_mdm6600_status() - read mdm6600 status lines |
|---|
| 174 | | - * @ddata: device driver data |
|---|
| 198 | + * @work: work structure |
|---|
| 175 | 199 | */ |
|---|
| 176 | 200 | static void phy_mdm6600_status(struct work_struct *work) |
|---|
| 177 | 201 | { |
|---|
| 178 | 202 | struct phy_mdm6600 *ddata; |
|---|
| 179 | 203 | struct device *dev; |
|---|
| 180 | | - int values[PHY_MDM6600_NR_STATUS_LINES]; |
|---|
| 181 | | - int error, i, val = 0; |
|---|
| 204 | + DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES); |
|---|
| 205 | + int error; |
|---|
| 182 | 206 | |
|---|
| 183 | 207 | ddata = container_of(work, struct phy_mdm6600, status_work.work); |
|---|
| 184 | 208 | dev = ddata->dev; |
|---|
| 185 | 209 | |
|---|
| 186 | 210 | error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES, |
|---|
| 187 | 211 | ddata->status_gpios->desc, |
|---|
| 212 | + ddata->status_gpios->info, |
|---|
| 188 | 213 | values); |
|---|
| 189 | 214 | if (error) |
|---|
| 190 | 215 | return; |
|---|
| 191 | 216 | |
|---|
| 192 | | - for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) { |
|---|
| 193 | | - val |= values[i] << i; |
|---|
| 194 | | - dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n", |
|---|
| 195 | | - __func__, i, values[i], val); |
|---|
| 196 | | - } |
|---|
| 197 | | - ddata->status = val; |
|---|
| 217 | + ddata->status = values[0] & ((1 << PHY_MDM6600_NR_STATUS_LINES) - 1); |
|---|
| 198 | 218 | |
|---|
| 199 | 219 | dev_info(dev, "modem status: %i %s\n", |
|---|
| 200 | 220 | ddata->status, |
|---|
| 201 | | - phy_mdm6600_status_name[ddata->status & 7]); |
|---|
| 221 | + phy_mdm6600_status_name[ddata->status]); |
|---|
| 202 | 222 | complete(&ddata->ack); |
|---|
| 203 | 223 | } |
|---|
| 204 | 224 | |
|---|
| .. | .. |
|---|
| 551 | 571 | ddata->dev = &pdev->dev; |
|---|
| 552 | 572 | platform_set_drvdata(pdev, ddata); |
|---|
| 553 | 573 | |
|---|
| 574 | + /* Active state selected in phy_mdm6600_power_on() */ |
|---|
| 575 | + error = pinctrl_pm_select_sleep_state(ddata->dev); |
|---|
| 576 | + if (error) |
|---|
| 577 | + dev_warn(ddata->dev, "%s: error with sleep_state: %i\n", |
|---|
| 578 | + __func__, error); |
|---|
| 579 | + |
|---|
| 554 | 580 | error = phy_mdm6600_init_lines(ddata); |
|---|
| 555 | 581 | if (error) |
|---|
| 556 | 582 | return error; |
|---|
| 557 | 583 | |
|---|
| 558 | 584 | phy_mdm6600_init_irq(ddata); |
|---|
| 559 | | - |
|---|
| 560 | | - ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops); |
|---|
| 561 | | - if (IS_ERR(ddata->generic_phy)) { |
|---|
| 562 | | - error = PTR_ERR(ddata->generic_phy); |
|---|
| 563 | | - goto cleanup; |
|---|
| 564 | | - } |
|---|
| 565 | | - |
|---|
| 566 | | - phy_set_drvdata(ddata->generic_phy, ddata); |
|---|
| 567 | | - |
|---|
| 568 | | - ddata->phy_provider = |
|---|
| 569 | | - devm_of_phy_provider_register(ddata->dev, |
|---|
| 570 | | - of_phy_simple_xlate); |
|---|
| 571 | | - if (IS_ERR(ddata->phy_provider)) { |
|---|
| 572 | | - error = PTR_ERR(ddata->phy_provider); |
|---|
| 573 | | - goto cleanup; |
|---|
| 574 | | - } |
|---|
| 575 | | - |
|---|
| 576 | 585 | schedule_delayed_work(&ddata->bootup_work, 0); |
|---|
| 577 | 586 | |
|---|
| 578 | 587 | /* |
|---|
| .. | .. |
|---|
| 596 | 605 | if (error < 0) { |
|---|
| 597 | 606 | dev_warn(ddata->dev, "failed to wake modem: %i\n", error); |
|---|
| 598 | 607 | pm_runtime_put_noidle(ddata->dev); |
|---|
| 608 | + goto cleanup; |
|---|
| 599 | 609 | } |
|---|
| 610 | + |
|---|
| 611 | + ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops); |
|---|
| 612 | + if (IS_ERR(ddata->generic_phy)) { |
|---|
| 613 | + error = PTR_ERR(ddata->generic_phy); |
|---|
| 614 | + goto idle; |
|---|
| 615 | + } |
|---|
| 616 | + |
|---|
| 617 | + phy_set_drvdata(ddata->generic_phy, ddata); |
|---|
| 618 | + |
|---|
| 619 | + ddata->phy_provider = |
|---|
| 620 | + devm_of_phy_provider_register(ddata->dev, |
|---|
| 621 | + of_phy_simple_xlate); |
|---|
| 622 | + if (IS_ERR(ddata->phy_provider)) |
|---|
| 623 | + error = PTR_ERR(ddata->phy_provider); |
|---|
| 624 | + |
|---|
| 625 | +idle: |
|---|
| 600 | 626 | pm_runtime_mark_last_busy(ddata->dev); |
|---|
| 601 | 627 | pm_runtime_put_autosuspend(ddata->dev); |
|---|
| 602 | 628 | |
|---|
| 603 | | - return 0; |
|---|
| 604 | | - |
|---|
| 605 | 629 | cleanup: |
|---|
| 606 | | - phy_mdm6600_device_power_off(ddata); |
|---|
| 630 | + if (error < 0) |
|---|
| 631 | + phy_mdm6600_device_power_off(ddata); |
|---|
| 632 | + pm_runtime_disable(ddata->dev); |
|---|
| 633 | + pm_runtime_dont_use_autosuspend(ddata->dev); |
|---|
| 607 | 634 | return error; |
|---|
| 608 | 635 | } |
|---|
| 609 | 636 | |
|---|