Commit 0b8af6a9 authored by Vaibhav Hiremath's avatar Vaibhav Hiremath Committed by Greg Kroah-Hartman

greybus: db3(arche)-platform: Remove all APB control code

Current db3(arche)-platform driver was only handling APB control signals,
which is where it all started. But now with completion of DB3/EVT1 bringup
we know that platform driver is more than APB control.

We have to bring SVC up before APB, as SVC supposed to enable
clock to APB's. Note that, in EVT1, AP will have direct control over
APB's clock.
Then we have dependency between APB and USB HUB, where, APB should be
brought up before USB HUB (note that this needs to rootcaused).

This patch cleanup the db3(arche)-platform driver to remove all APB control
code. The idea here is create another driver for APB control
(arche-apb-ctrl.c), which will deal with APB.
And this driver will have generic/common platform specific support,
currently manages SVC resources.

This patch also takes in all the changes from factory branch, discovered
during bringup.
Signed-off-by: default avatarVaibhav Hiremath <vaibhav.hiremath@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 3563ff88
/*
* 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 <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
......@@ -23,304 +24,141 @@
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
enum apb_state {
APB_STATE_OFF,
APB_STATE_ACTIVE,
APB_STATE_STANDBY,
};
/*
* 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;
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;
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)
{
gpio_set_value(wake_detect, 1);
}
static inline void deassert_wake(unsigned int wake_detect)
static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
{
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;
gpio_set_value(gpio, onoff);
}
static void apb_ctrl_cleanup(struct apb_ctrl_drvdata *apb_data)
static void db3_platform_cleanup(struct db3_platform_drvdata *db3_pdata)
{
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)
static int db3_platform_probe(struct platform_device *pdev)
{
struct db3_platform_drvdata *db3_pdata;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
int ret;
pinctrl_select_state(apb_data->pinctrl, apb_data->pin_default);
db3_pdata = devm_kzalloc(&pdev->dev, sizeof(*db3_pdata), GFP_KERNEL);
if (!db3_pdata)
return -ENOMEM;
/* 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);
/* 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;
}
/* Enable power to APB */
if (apb_data->vcore) {
ret = regulator_enable(apb_data->vcore);
ret = devm_gpio_request(dev, db3_pdata->svc_reset_gpio, "svc-reset");
if (ret) {
dev_err(dev, "failed to enable core regulator\n");
dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
return ret;
}
}
if (apb_data->vio) {
ret = regulator_enable(apb_data->vio);
ret = gpio_direction_output(db3_pdata->svc_reset_gpio, db3_pdata->is_reset_act_hi);
if (ret) {
dev_err(dev, "failed to enable IO regulator\n");
dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
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);
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, apb_data->ctrl.wake_detect, "wake detect");
if (ret)
dev_err(dev, "Failed requesting wake_detect gpio %d\n",
apb_data->ctrl.wake_detect);
ret = devm_gpio_request(dev, db3_pdata->svc_sysboot_gpio, "sysboot0");
if (ret) {
dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
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;
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;
}
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);
/* 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;
}
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);
ret = devm_gpio_request(dev, db3_pdata->svc_refclk_req, "svc-clk-req");
if (ret) {
dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
return ret;
}
return 0;
}
static int apb_ctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct apb_ctrl_drvdata *apb_data;
int ret;
apb_data = devm_kzalloc(&pdev->dev, sizeof(*apb_data), GFP_KERNEL);
if (!apb_data)
return -ENOMEM;
ret = apb_ctrl_get_devtree_data(dev, apb_data);
ret = gpio_direction_input(db3_pdata->svc_refclk_req);
if (ret) {
dev_err(dev, "failed to get devicetree data %d\n", ret);
dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
return ret;
}
ret = apb_ctrl_init_seq(dev, apb_data);
/* 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 set init state of control signal %d\n",
ret);
goto exit;
dev_err(dev, "failed to enable svc_ref_clk: %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);
platform_set_drvdata(pdev, db3_pdata);
/*
* 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);
db3_pdata->num_apbs = of_get_child_count(np);
dev_dbg(dev, "Number of APB's available - %d\n", db3_pdata->num_apbs);
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);
if (ret) {
dev_err(&pdev->dev, "failed to request wake detect IRQ\n");
goto exit;
}
/* bring SVC out of reset */
svc_reset_onoff(db3_pdata->svc_reset_gpio, !db3_pdata->is_reset_act_hi);
/*
* 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;
/* probe all childs here */
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret)
dev_err(dev, "no child node found\n");
exit:
apb_ctrl_cleanup(apb_data);
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 <vaibhav.hiremath@linaro.org>");
MODULE_DESCRIPTION("AP Bridge Control Driver for DB3 platform");
MODULE_DESCRIPTION("DB3 Platform Driver");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment