diff --git a/drivers/staging/greybus/db3-platform.c b/drivers/staging/greybus/db3-platform.c index 31f27f0628c4..eede56bac1c4 100644 --- a/drivers/staging/greybus/db3-platform.c +++ b/drivers/staging/greybus/db3-platform.c @@ -1,5 +1,5 @@ /* - * DB3 Platform driver for AP bridge control interface. + * DB3 Platform driver to enable Unipro link. * * Copyright 2014-2015 Google Inc. * Copyright 2014-2015 Linaro Ltd. @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -23,304 +24,141 @@ #include #include -enum apb_state { - APB_STATE_OFF, - APB_STATE_ACTIVE, - APB_STATE_STANDBY, -}; +struct db3_platform_drvdata { + /* Control GPIO signals to and from AP <=> SVC */ + int svc_reset_gpio; + bool is_reset_act_hi; + int svc_sysboot_gpio; -/* - * Control GPIO signals to and from AP <=> AP Bridges - */ -struct apb_ctrl_gpios { - int wake_detect; /* bi-dir, maps to WAKE_MOD and WAKE_FRAME signals */ - int reset; - int boot_ret; - int pwroff; - int wake_in; - int wake_out; - int pwrdn; -}; - -struct apb_ctrl_drvdata { - struct apb_ctrl_gpios ctrl; - - unsigned int wake_detect_irq; - enum apb_state state; - - struct regulator *vcore; - struct regulator *vio; + unsigned int svc_refclk_req; + struct clk *svc_ref_clk; struct pinctrl *pinctrl; struct pinctrl_state *pin_default; - /* To protect concurrent access of GPIO registers, need protection */ - spinlock_t lock; + int num_apbs; }; -static inline void assert_wake(unsigned int wake_detect) +static inline void svc_reset_onoff(unsigned int gpio, bool onoff) { - gpio_set_value(wake_detect, 1); + gpio_set_value(gpio, onoff); } -static inline void deassert_wake(unsigned int wake_detect) +static void db3_platform_cleanup(struct db3_platform_drvdata *db3_pdata) { - gpio_set_value(wake_detect, 0); -} - -static irqreturn_t apb_ctrl_wake_detect_irq(int irq, void *devid) -{ - struct apb_ctrl_drvdata *apb_data = devid; - unsigned long flags; - - /* - * TODO: - * Since currently SoC GPIOs are being used we are safe here - * But ideally we should create a workqueue and process the control - * signals, especially when we start using GPIOs over slow - * buses like I2C. - */ - if (!gpio_is_valid(apb_data->ctrl.wake_detect) && - !gpio_is_valid(apb_data->ctrl.reset)) - return IRQ_HANDLED; /* Should it be IRQ_NONE ?? */ - - spin_lock_irqsave(&apb_data->lock, flags); - - if (apb_data->state != APB_STATE_ACTIVE) { - /* Bring bridge out of reset on this event */ - gpio_set_value(apb_data->ctrl.reset, 0); - apb_data->state = APB_STATE_ACTIVE; - } else { - /* - * Assert Wake_OUT signal to APB - * It would resemble WakeDetect module's signal pass-through - */ - /* - * We have to generate the pulse, so we may need to schedule - * workqueue here. - * - * Also, since we are using both rising and falling edge for - * interrupt trigger, we may not need workqueue. Just pass - * through the value to bridge. - * Just read GPIO value and pass it to the bridge - */ - } - - spin_unlock_irqrestore(&apb_data->lock, flags); - - return IRQ_HANDLED; -} - -static void apb_ctrl_cleanup(struct apb_ctrl_drvdata *apb_data) -{ - if (apb_data->vcore && regulator_is_enabled(apb_data->vcore) > 0) - regulator_disable(apb_data->vcore); - - if (apb_data->vio && regulator_is_enabled(apb_data->vio) > 0) - regulator_disable(apb_data->vio); - /* As part of exit, put APB back in reset state */ - if (gpio_is_valid(apb_data->ctrl.reset)) - gpio_set_value(apb_data->ctrl.reset, 1); - - /* TODO: May have to send an event to SVC about this exit */ + if (gpio_is_valid(db3_pdata->svc_reset_gpio)) + svc_reset_onoff(db3_pdata->svc_reset_gpio, + db3_pdata->is_reset_act_hi); } -/* - * Note: Please do not modify the below sequence, as it is as per the spec - */ -static int apb_ctrl_init_seq(struct device *dev, - struct apb_ctrl_drvdata *apb_data) -{ - int ret; - - pinctrl_select_state(apb_data->pinctrl, apb_data->pin_default); - - /* Hold APB in reset state */ - ret = devm_gpio_request_one(dev, apb_data->ctrl.reset, - GPIOF_OUT_INIT_LOW, "reset"); - if (ret) { - dev_err(dev, "Failed requesting reset gpio %d\n", - apb_data->ctrl.reset); - return ret; - } - - /* Enable power to APB */ - if (apb_data->vcore) { - ret = regulator_enable(apb_data->vcore); - if (ret) { - dev_err(dev, "failed to enable core regulator\n"); - return ret; - } - } - - if (apb_data->vio) { - ret = regulator_enable(apb_data->vio); - if (ret) { - dev_err(dev, "failed to enable IO regulator\n"); - return ret; - } - } - - /* - * We should be safe here to deassert boot retention signal, as - * we are only supporting cold boot as of now. - */ - ret = devm_gpio_request_one(dev, apb_data->ctrl.boot_ret, - GPIOF_OUT_INIT_LOW, "boot retention"); - if (ret) { - dev_err(dev, "Failed requesting reset gpio %d\n", - apb_data->ctrl.boot_ret); - return ret; - } - - ret = devm_gpio_request(dev, apb_data->ctrl.wake_detect, "wake detect"); - if (ret) - dev_err(dev, "Failed requesting wake_detect gpio %d\n", - apb_data->ctrl.wake_detect); - - return ret; -} - -static int apb_ctrl_get_devtree_data(struct device *dev, - struct apb_ctrl_drvdata *apb_data) -{ - struct device_node *np = dev->of_node; - - /* fetch control signals */ - apb_data->ctrl.wake_detect = of_get_named_gpio(np, "wake-detect-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.wake_detect)) { - dev_err(dev, "failed to get wake detect gpio\n"); - return apb_data->ctrl.wake_detect; - } - - apb_data->ctrl.reset = of_get_named_gpio(np, "reset-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.reset)) { - dev_err(dev, "failed to get reset gpio\n"); - return apb_data->ctrl.reset; - } - - apb_data->ctrl.boot_ret = of_get_named_gpio(np, "boot-ret-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.boot_ret)) { - dev_err(dev, "failed to get boot retention gpio\n"); - return apb_data->ctrl.boot_ret; - } - - /* It's not mandatory to support power management interface */ - apb_data->ctrl.pwroff = of_get_named_gpio(np, "pwr-off-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.pwroff)) - dev_info(dev, "failed to get power off gpio\n"); - - apb_data->ctrl.pwrdn = of_get_named_gpio(np, "pwr-down-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.pwrdn)) - dev_info(dev, "failed to get power down gpio\n"); - - /* Regulators are optional, as we may have fixed supply coming in */ - apb_data->vcore = devm_regulator_get(dev, "vcore"); - if (IS_ERR_OR_NULL(apb_data->vcore)) { - dev_info(dev, "no core regulator found\n"); - apb_data->vcore = NULL; - } - - apb_data->vio = devm_regulator_get(dev, "vio"); - if (IS_ERR_OR_NULL(apb_data->vio)) { - dev_info(dev, "no IO regulator found\n"); - apb_data->vio = NULL; - } - - apb_data->pinctrl = devm_pinctrl_get(dev); - if (IS_ERR(apb_data->pinctrl)) { - dev_err(dev, "could not get pinctrl handle\n"); - return PTR_ERR(apb_data->pinctrl); - } - apb_data->pin_default = pinctrl_lookup_state(apb_data->pinctrl, "default"); - if (IS_ERR(apb_data->pin_default)) { - dev_err(dev, "could not get default pin state\n"); - return PTR_ERR(apb_data->pin_default); - } - - return 0; -} - -static int apb_ctrl_probe(struct platform_device *pdev) +static int db3_platform_probe(struct platform_device *pdev) { + struct db3_platform_drvdata *db3_pdata; struct device *dev = &pdev->dev; - struct apb_ctrl_drvdata *apb_data; + struct device_node *np = dev->of_node; int ret; - apb_data = devm_kzalloc(&pdev->dev, sizeof(*apb_data), GFP_KERNEL); - if (!apb_data) + db3_pdata = devm_kzalloc(&pdev->dev, sizeof(*db3_pdata), GFP_KERNEL); + if (!db3_pdata) return -ENOMEM; - ret = apb_ctrl_get_devtree_data(dev, apb_data); + /* setup svc reset gpio */ + db3_pdata->is_reset_act_hi = of_property_read_bool(np, "svc,reset-active-high"); + db3_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); + if (db3_pdata->svc_reset_gpio < 0) { + dev_err(dev, "failed to get reset-gpio\n"); + ret = -ENODEV; + return ret; + } + ret = devm_gpio_request(dev, db3_pdata->svc_reset_gpio, "svc-reset"); if (ret) { - dev_err(dev, "failed to get devicetree data %d\n", ret); + dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); + return ret; + } + ret = gpio_direction_output(db3_pdata->svc_reset_gpio, db3_pdata->is_reset_act_hi); + if (ret) { + dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); return ret; } - ret = apb_ctrl_init_seq(dev, apb_data); + db3_pdata->svc_sysboot_gpio = of_get_named_gpio(np, "svc,sysboot-gpio", 0); + if (db3_pdata->svc_sysboot_gpio < 0) { + dev_err(dev, "failed to get sysboot gpio\n"); + ret = -ENODEV; + return ret; + } + ret = devm_gpio_request(dev, db3_pdata->svc_sysboot_gpio, "sysboot0"); if (ret) { - dev_err(dev, "failed to set init state of control signal %d\n", - ret); - goto exit; + dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); + return ret; + } + ret = gpio_direction_output(db3_pdata->svc_sysboot_gpio, 0); + if (ret) { + dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); + return ret; } - platform_set_drvdata(pdev, apb_data); - - apb_data->state = APB_STATE_OFF; - /* - * Assert AP module detect signal by pulling wake_detect low - */ - deassert_wake(apb_data->ctrl.wake_detect); - - /* - * In order to receive an interrupt, the GPIO must be set to input mode - * - * As per WDM spec, for the cold boot, the wake pulse must be - * >= 5000 usec, but at this stage it is power up sequence, - * so we always treat it as cold boot. - */ - gpio_direction_input(apb_data->ctrl.wake_detect); - - ret = devm_request_irq(dev, gpio_to_irq(apb_data->ctrl.wake_detect), - apb_ctrl_wake_detect_irq, - IRQF_TRIGGER_RISING, - "wake detect", apb_data); + /* setup the clock request gpio first */ + db3_pdata->svc_refclk_req = of_get_named_gpio(np, "svc,refclk-req-gpio", 0); + if (db3_pdata->svc_refclk_req < 0) { + dev_err(dev, "failed to get svc clock-req gpio\n"); + return -ENODEV; + } + ret = devm_gpio_request(dev, db3_pdata->svc_refclk_req, "svc-clk-req"); if (ret) { - dev_err(&pdev->dev, "failed to request wake detect IRQ\n"); - goto exit; + dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); + return ret; + } + ret = gpio_direction_input(db3_pdata->svc_refclk_req); + if (ret) { + dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); + return ret; } - /* - * Interrupt handling for WAKE_IN (from bridge) signal is required - * - * Assumption here is, AP already would have woken up and in the - * WAKE_IN/WAKE_FRAME event from bridge, as AP would pass-through event - * to SVC. - * - * Not sure anything else needs to take care here. - */ - dev_info(dev, "Device registered successfully \n"); - return 0; + /* setup refclk2 to follow the pin */ + db3_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); + if (IS_ERR(db3_pdata->svc_ref_clk)) { + ret = PTR_ERR(db3_pdata->svc_ref_clk); + dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(db3_pdata->svc_ref_clk); + if (ret) { + dev_err(dev, "failed to enable svc_ref_clk: %d\n", ret); + return ret; + } -exit: - apb_ctrl_cleanup(apb_data); + platform_set_drvdata(pdev, db3_pdata); + + db3_pdata->num_apbs = of_get_child_count(np); + dev_dbg(dev, "Number of APB's available - %d\n", db3_pdata->num_apbs); + + /* bring SVC out of reset */ + svc_reset_onoff(db3_pdata->svc_reset_gpio, !db3_pdata->is_reset_act_hi); + + /* probe all childs here */ + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) + dev_err(dev, "no child node found\n"); + + dev_info(dev, "Device registered successfully\n"); return ret; } -static int apb_ctrl_remove(struct platform_device *pdev) +static int db3_platform_remove(struct platform_device *pdev) { - struct apb_ctrl_drvdata *apb_data = platform_get_drvdata(pdev); + struct db3_platform_drvdata *db3_pdata = platform_get_drvdata(pdev); - if (apb_data) - apb_ctrl_cleanup(apb_data); + if (db3_pdata) + db3_platform_cleanup(db3_pdata); platform_set_drvdata(pdev, NULL); return 0; } -static int apb_ctrl_suspend(struct device *dev) +static int db3_platform_suspend(struct device *dev) { /* * If timing profile premits, we may shutdown bridge @@ -334,7 +172,7 @@ static int apb_ctrl_suspend(struct device *dev) return 0; } -static int apb_ctrl_resume(struct device *dev) +static int db3_platform_resume(struct device *dev) { /* * Atleast for ES2 we have to meet the delay requirement between @@ -348,26 +186,26 @@ static int apb_ctrl_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(apb_ctrl_pm_ops, apb_ctrl_suspend, apb_ctrl_resume); +static SIMPLE_DEV_PM_OPS(db3_platform_pm_ops, db3_platform_suspend, db3_platform_resume); -static struct of_device_id apb_ctrl_of_match[] = { - { .compatible = "usbffff,2", }, +static struct of_device_id db3_platform_of_match[] = { + { .compatible = "google,db3-platform", }, /* Use PID/VID of SVC device */ { }, }; -MODULE_DEVICE_TABLE(of, apb_ctrl_of_match); +MODULE_DEVICE_TABLE(of, db3_platform_of_match); -static struct platform_driver apb_ctrl_device_driver = { - .probe = apb_ctrl_probe, - .remove = apb_ctrl_remove, +static struct platform_driver db3_platform_device_driver = { + .probe = db3_platform_probe, + .remove = db3_platform_remove, .driver = { - .name = "unipro-APbridge", - .pm = &apb_ctrl_pm_ops, - .of_match_table = of_match_ptr(apb_ctrl_of_match), + .name = "db3-platform-ctrl", + .pm = &db3_platform_pm_ops, + .of_match_table = of_match_ptr(db3_platform_of_match), } }; -module_platform_driver(apb_ctrl_device_driver); +module_platform_driver(db3_platform_device_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vaibhav Hiremath "); -MODULE_DESCRIPTION("AP Bridge Control Driver for DB3 platform"); +MODULE_DESCRIPTION("DB3 Platform Driver");