#include <linux/notifier.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/usb/musb-ux500.h>
unsigned vbus_draw;
struct work_struct phy_dis_work;
enum ab8500_usb_mode mode;
+ struct clk *sysclk;
struct regulator *v_ape;
struct regulator *v_musb;
struct regulator *v_ulpi;
if (IS_ERR(ab->pinctrl))
dev_err(ab->dev, "could not get/set default pinstate\n");
+ if (clk_prepare_enable(ab->sysclk))
+ dev_err(ab->dev, "can't prepare/enable clock\n");
+
ab8500_usb_regulator_enable(ab);
abx500_mask_and_set_register_interruptible(ab->dev,
/* Needed to disable the phy.*/
ab8500_usb_wd_workaround(ab);
+ clk_disable_unprepare(ab->sysclk);
+
ab8500_usb_regulator_disable(ab);
if (!IS_ERR(ab->pinctrl)) {
else if (pinctrl_select_state(ab->pinctrl, ab->pins_sleep))
dev_err(ab->dev, "could not set pins to sleep state\n");
- /* as USB pins are shared with idddet, release them to allow
+ /*
+ * as USB pins are shared with iddet, release them to allow
* iddet to request them
*/
pinctrl_put(ab->pinctrl);
static irqreturn_t ab8500_usb_link_status_irq(int irq, void *data)
{
- struct ab8500_usb *ab = (struct ab8500_usb *) data;
+ struct ab8500_usb *ab = (struct ab8500_usb *)data;
abx500_usb_link_status_update(ab);
* is fixed.
*/
- if ((ab->mode != USB_IDLE) && (!gadget)) {
+ if ((ab->mode != USB_IDLE) && !gadget) {
ab->mode = USB_IDLE;
schedule_work(&ab->phy_dis_work);
}
* is fixed.
*/
- if ((ab->mode != USB_IDLE) && (!host)) {
+ if ((ab->mode != USB_IDLE) && !host) {
ab->mode = USB_IDLE;
schedule_work(&ab->phy_dis_work);
}
return 0;
}
+static void ab8500_usb_restart_phy(struct ab8500_usb *ab)
+{
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+ AB8500_BIT_PHY_CTRL_DEVICE_EN,
+ AB8500_BIT_PHY_CTRL_DEVICE_EN);
+
+ udelay(100);
+
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+ AB8500_BIT_PHY_CTRL_DEVICE_EN,
+ 0);
+
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+ AB8500_BIT_PHY_CTRL_HOST_EN,
+ AB8500_BIT_PHY_CTRL_HOST_EN);
+
+ udelay(100);
+
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+ AB8500_BIT_PHY_CTRL_HOST_EN,
+ 0);
+}
+
static int ab8500_usb_regulator_get(struct ab8500_usb *ab)
{
int err;
if (err)
return err;
+ ab->sysclk = devm_clk_get(ab->dev, "sysclk");
+ if (IS_ERR(ab->sysclk)) {
+ dev_err(ab->dev, "Could not get sysclk.\n");
+ return PTR_ERR(ab->sysclk);
+ }
+
err = ab8500_usb_irq_setup(pdev, ab);
if (err < 0)
return err;
return err;
}
- /* Phy tuning values for AB8500 */
- if (!is_ab8500_2p0_or_earlier(ab->ab8500)) {
+ /* Phy tuning values for AB8500 > v2.0 */
+ if (is_ab8500(ab->ab8500) && !is_ab8500_2p0_or_earlier(ab->ab8500)) {
/* Enable the PBT/Bank 0x12 access */
err = abx500_set_register_interruptible(ab->dev,
AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x01);
/* Needed to enable ID detection. */
ab8500_usb_wd_workaround(ab);
+ /*
+ * This is required for usb-link-status to work properly when a
+ * cable is connected at boot time.
+ */
+ ab8500_usb_restart_phy(ab);
+
abx500_usb_link_status_update(ab);
dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev);
else if (ab->mode == USB_PERIPHERAL)
ab8500_usb_peri_phy_dis(ab);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
+static struct platform_device_id ab8500_usb_devtype[] = {
+ { .name = "ab8500-usb", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype);
+
static struct platform_driver ab8500_usb_driver = {
.probe = ab8500_usb_probe,
.remove = ab8500_usb_remove,
+ .id_table = ab8500_usb_devtype,
.driver = {
- .name = "ab8500-usb",
+ .name = "abx5x0-usb",
.owner = THIS_MODULE,
},
};
}
module_exit(ab8500_usb_exit);
-MODULE_ALIAS("platform:ab8500_usb");
MODULE_AUTHOR("ST-Ericsson AB");
MODULE_DESCRIPTION("AB8500 usb transceiver driver");
MODULE_LICENSE("GPL");