diff options
Diffstat (limited to 'drivers/bluetooth/btmtkuart.c')
-rw-r--r-- | drivers/bluetooth/btmtkuart.c | 51 |
1 files changed, 41 insertions, 10 deletions
diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index f5dbeec8e274..e11169ad8247 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -115,10 +115,12 @@ struct btmtk_hci_wmt_params { struct btmtkuart_dev { struct hci_dev *hdev; struct serdev_device *serdev; - struct clk *clk; + struct clk *clk; + struct clk *osc; struct regulator *vcc; struct gpio_desc *reset; + struct gpio_desc *boot; struct pinctrl *pinctrl; struct pinctrl_state *pins_runtime; struct pinctrl_state *pins_boot; @@ -911,6 +913,19 @@ static int btmtkuart_parse_dt(struct serdev_device *serdev) return err; } + bdev->osc = devm_clk_get_optional(&serdev->dev, "osc"); + if (IS_ERR(bdev->osc)) { + err = PTR_ERR(bdev->osc); + return err; + } + + bdev->boot = devm_gpiod_get_optional(&serdev->dev, "boot", + GPIOD_OUT_LOW); + if (IS_ERR(bdev->boot)) { + err = PTR_ERR(bdev->boot); + return err; + } + bdev->pinctrl = devm_pinctrl_get(&serdev->dev); if (IS_ERR(bdev->pinctrl)) { err = PTR_ERR(bdev->pinctrl); @@ -919,8 +934,10 @@ static int btmtkuart_parse_dt(struct serdev_device *serdev) bdev->pins_boot = pinctrl_lookup_state(bdev->pinctrl, "default"); - if (IS_ERR(bdev->pins_boot)) { + if (IS_ERR(bdev->pins_boot) && !bdev->boot) { err = PTR_ERR(bdev->pins_boot); + dev_err(&serdev->dev, + "Should assign RXD to LOW at boot stage\n"); return err; } @@ -996,13 +1013,25 @@ static int btmtkuart_probe(struct serdev_device *serdev) set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); if (btmtkuart_is_standalone(bdev)) { - /* Switch to the specific pin state for the booting requires */ - pinctrl_select_state(bdev->pinctrl, bdev->pins_boot); + err = clk_prepare_enable(bdev->osc); + if (err < 0) + return err; + + if (bdev->boot) { + gpiod_set_value_cansleep(bdev->boot, 1); + } else { + /* Switch to the specific pin state for the booting + * requires. + */ + pinctrl_select_state(bdev->pinctrl, bdev->pins_boot); + } /* Power on */ err = regulator_enable(bdev->vcc); - if (err < 0) + if (err < 0) { + clk_disable_unprepare(bdev->osc); return err; + } /* Reset if the reset-gpios is available otherwise the board * -level design should be guaranteed. @@ -1017,6 +1046,10 @@ static int btmtkuart_probe(struct serdev_device *serdev) * mode the device requires for UART transfers. */ msleep(50); + + if (bdev->boot) + devm_gpiod_put(&serdev->dev, bdev->boot); + pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime); /* A standalone device doesn't depends on power domain on SoC, @@ -1037,10 +1070,8 @@ static int btmtkuart_probe(struct serdev_device *serdev) return 0; err_regulator_disable: - if (btmtkuart_is_standalone(bdev)) { - pinctrl_select_state(bdev->pinctrl, bdev->pins_boot); + if (btmtkuart_is_standalone(bdev)) regulator_disable(bdev->vcc); - } return err; } @@ -1050,9 +1081,9 @@ static void btmtkuart_remove(struct serdev_device *serdev) struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev); struct hci_dev *hdev = bdev->hdev; - if (btmtkuart_is_standalone(bdev)) { - pinctrl_select_state(bdev->pinctrl, bdev->pins_boot); + if (btmtkuart_is_standalone(bdev)) { regulator_disable(bdev->vcc); + clk_disable_unprepare(bdev->osc); } hci_unregister_dev(hdev); |