greybus: arche-platform: Add support for SPI bus sharing for Mihi
authorVaibhav Hiremath <vaibhav.hiremath@linaro.org>
Thu, 28 Jul 2016 08:17:37 +0000 (13:47 +0530)
committerGreg Kroah-Hartman <gregkh@google.com>
Wed, 3 Aug 2016 13:45:08 +0000 (15:45 +0200)
In case of Mihi, SPI bus is shared between APB1 and APB2
SPI ROMs, so their FW flashing must be sequential and
arche-platform driver should make sure that they are mutual
exclusive in nature.

So this patch adds certain restrictions to the user of the
arche-platform driver,

 - User can no longer flash APB1 and APB2 SPI ROM in parallel
 - SPI bus becomes an resource, so user must claim it by moving
   respective APB device into FW_FLASHING mode and release it
   by exiting FW_FLASHING mode. User can exit FW_FLASHING mode by
   switching to any other modes (ACTIVE, OFF, STANDBY).
 - If APB1 is in FW_FLASHING mode, APB2 can no longer enter into
   FW_FLASHING mode. User will get -EBUSY.

Having said that, while APB1 is into FW_FLASHING mode,
APB2 can independently boot from its own SPI ROM.

Testing Done: Tested by simulating usecase on EVT2.
 - Made sure that APB1 and APB2 FW_FLASHING mode is mutual exclusive
   in nature. Confirmed that an attempt on second device return -EBUSY.
 - Added simulating code, where printed state of dummy gpio for
   spi-en and verified that it shows right pin status for both APBs

Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/arche-apb-ctrl.c
drivers/staging/greybus/arche-platform.c

index 049d496..59d9d42 100644 (file)
@@ -42,6 +42,10 @@ struct arche_apb_ctrl_drvdata {
 
        struct pinctrl *pinctrl;
        struct pinctrl_state *pin_default;
+
+       /* V2: SPI Bus control  */
+       int spi_en_gpio;
+       bool spi_en_polarity_high;
 };
 
 /*
@@ -73,6 +77,10 @@ static int coldboot_seq(struct platform_device *pdev)
        /* Hold APB in reset state */
        assert_reset(apb->resetn_gpio);
 
+       if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING &&
+                       gpio_is_valid(apb->spi_en_gpio))
+               devm_gpio_free(dev, apb->spi_en_gpio);
+
        /* Enable power to APB */
        if (!IS_ERR(apb->vcore)) {
                ret = regulator_enable(apb->vcore);
@@ -128,6 +136,23 @@ static int fw_flashing_seq(struct platform_device *pdev)
                return ret;
        }
 
+       if (gpio_is_valid(apb->spi_en_gpio)) {
+               unsigned long flags;
+
+               if (apb->spi_en_polarity_high)
+                       flags = GPIOF_OUT_INIT_HIGH;
+               else
+                       flags = GPIOF_OUT_INIT_LOW;
+
+               ret = devm_gpio_request_one(dev, apb->spi_en_gpio,
+                               flags, "apb_spi_en");
+               if (ret) {
+                       dev_err(dev, "Failed requesting SPI bus en gpio %d\n",
+                               apb->spi_en_gpio);
+                       return ret;
+               }
+       }
+
        /* for flashing device should be in reset state */
        assert_reset(apb->resetn_gpio);
        apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING;
@@ -137,6 +162,7 @@ static int fw_flashing_seq(struct platform_device *pdev)
 
 static int standby_boot_seq(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
 
        if (apb->init_disabled)
@@ -147,6 +173,10 @@ static int standby_boot_seq(struct platform_device *pdev)
                        apb->state == ARCHE_PLATFORM_STATE_OFF)
                return 0;
 
+       if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING &&
+                       gpio_is_valid(apb->spi_en_gpio))
+               devm_gpio_free(dev, apb->spi_en_gpio);
+
        /*
         * As per WDM spec, do nothing
         *
@@ -162,11 +192,16 @@ static int standby_boot_seq(struct platform_device *pdev)
 
 static void poweroff_seq(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
 
        if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF)
                return;
 
+       if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING &&
+                       gpio_is_valid(apb->spi_en_gpio))
+               devm_gpio_free(dev, apb->spi_en_gpio);
+
        /* disable the clock */
        if (gpio_is_valid(apb->clk_en_gpio))
                gpio_set_value(apb->clk_en_gpio, 0);
@@ -369,6 +404,14 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev,
                return PTR_ERR(apb->pin_default);
        }
 
+       /* Only applicable for platform >= V2 */
+       apb->spi_en_gpio = of_get_named_gpio(np, "spi-en-gpio", 0);
+       if (apb->spi_en_gpio >= 0) {
+               if (of_property_read_bool(pdev->dev.of_node,
+                                       "spi-en-active-high"))
+                       apb->spi_en_polarity_high = true;
+       }
+
        return 0;
 }
 
index adec1fd..9d9048e 100644 (file)
@@ -215,18 +215,6 @@ static int apb_cold_boot(struct device *dev, void *data)
        return 0;
 }
 
-static int apb_fw_flashing_state(struct device *dev, void *data)
-{
-       int ret;
-
-       ret = apb_ctrl_fw_flashing(dev);
-       if (ret)
-               dev_warn(dev, "failed to switch to fw flashing state\n");
-
-       /*Child nodes are independent, so do not exit coldboot operation */
-       return 0;
-}
-
 static int apb_poweroff(struct device *dev, void *data)
 {
        apb_ctrl_poweroff(dev);
@@ -485,17 +473,18 @@ retry:
                if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
                        goto exit;
 
-               /* First we want to make sure we power off everything
-                * and then enter FW flashing state */
-               device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
-
+               /*
+                * Here we only control SVC.
+                *
+                * In case of FW_FLASHING mode we do not want to control
+                * APBs, as in case of V2, SPI bus is shared between both
+                * the APBs. So let user chose which APB he wants to flash.
+                */
                arche_platform_poweroff_seq(arche_pdata);
 
                ret = arche_platform_fw_flashing_seq(arche_pdata);
                if (ret)
                        goto exit;
-
-               device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state);
        } else {
                dev_err(arche_pdata->dev, "unknown state\n");
                ret = -EINVAL;