.. | .. |
---|
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 | |
---|