Signed-off-by: Russ Gorby <[email protected]>
---
drivers/serial/ifx6x60.c | 183 ++++++++++++++++++----------------------------
drivers/serial/ifx6x60.h | 4 +
drivers/spi/pw_spi3.c | 130 +++++++++++++++++++-------------
3 files changed, 152 insertions(+), 165 deletions(-)
diff --git a/drivers/serial/ifx6x60.c b/drivers/serial/ifx6x60.c
index d68fafb..d7bf507 100644
--- a/drivers/serial/ifx6x60.c
+++ b/drivers/serial/ifx6x60.c
@@ -134,6 +134,7 @@ static void
ifx_spi_power_state_set(struct ifx_spi_device *ifx_dev, unsigned char val)
{
unsigned long flags;
+ int ret;
spin_lock_irqsave(&ifx_dev->power_lock, flags);
@@ -142,8 +143,9 @@ ifx_spi_power_state_set(struct ifx_spi_device *ifx_dev,
unsigned char val)
* tell power system
*/
if (!ifx_dev->power_status) {
- dev_dbg(&ifx_dev->spi_dev->dev, "pm_runtime_get called");
- pm_runtime_get(&ifx_dev->spi_dev->dev);
+ ret = pm_runtime_get(ifx_dev->spi_dev->dev.parent);
+ if (ret < 0)
+ pr_warn("%s: rt_get failed: %d", DRVNAME, ret);
}
ifx_dev->power_status |= val;
@@ -161,15 +163,16 @@ static void
ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val)
{
unsigned long flags;
+ int ret;
spin_lock_irqsave(&ifx_dev->power_lock, flags);
if (ifx_dev->power_status) {
ifx_dev->power_status &= ~val;
if (!ifx_dev->power_status) {
- dev_dbg(&ifx_dev->spi_dev->dev,
- "pm_runtime_put called");
- pm_runtime_put(&ifx_dev->spi_dev->dev);
+ ret = pm_runtime_put(ifx_dev->spi_dev->dev.parent);
+ if (ret < 0)
+ pr_warn("%s: rt_put failed: %d", DRVNAME, ret);
}
}
@@ -273,6 +276,7 @@ static void ifx_spi_timeout(unsigned long arg)
}
dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***");
+ ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_DATA_PENDING);
ifx_spi_ttyhangup(ifx_dev);
mrdy_set_low(ifx_dev);
clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
@@ -623,6 +627,14 @@ static int ifx_spi_write(struct tty_struct *tty, const
unsigned char *buf,
int srdy_value;
dev_dbg(&ifx_dev->spi_dev->dev, "%s called", __func__);
+ if (test_bit(IFX_SPI_STATE_SUSPEND, &ifx_dev->flags)) {
+ /*
+ * FIXME:
+ * some modems have been observed to die (requiring reset)
+ * if MRDY/SRDY handshake occurs w/o clocking data
+ */
+ return -EIO;
+ }
tx_count = kfifo_in_locked(&port_data->tx_fifo, tmp_buf, count,
&port_data->fifo_lock);
@@ -955,6 +967,19 @@ static void ifx_spi_insert_flip_string(struct
ifx_spi_serial *ifx_ser,
}
/**
+ * ifx_spi_retry_timer_fn - Timer function for ifx_spi_complete()
+ * @data: Device pointer passed by ifx_spi_complete().
+ *
+ * The delay timer has expired so reschedule the tasklet
+ */
+static void ifx_spi_retry_timer_fn(unsigned long data)
+{
+ struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)data;
+
+ tasklet_schedule(&ifx_dev->io_work_tasklet);
+}
+
+/**
* ifx_spi_complete - SPI transfer completed
* @ctx: our SPI device
*
@@ -1030,6 +1055,9 @@ complete_exit:
}
clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags));
+ clear_bit(IFX_SPI_STATE_IO_RETRY, &(ifx_dev->flags));
+ del_timer(&ifx_dev->spi_retry_timer);
+
queue_length = (port_data == NULL) ? 0 : kfifo_len(&port_data->tx_fifo);
srdy = gpio_get_value(ifx_dev->gpio.srdy);
@@ -1092,6 +1120,18 @@ complete_exit:
}
}
+static void
+ifx_spi_retry(struct ifx_spi_device *ifx_dev)
+{
+ unsigned long expires;
+
+ if (!test_and_set_bit(IFX_SPI_STATE_IO_RETRY, &ifx_dev->flags))
+ setup_timer(&ifx_dev->spi_retry_timer, ifx_spi_retry_timer_fn,
+ (unsigned long)ifx_dev);
+ expires = jiffies + IFX_RETRY_TIMEOUT;
+ mod_timer(&ifx_dev->spi_retry_timer, expires);
+}
+
/**
* ifx_spio_io - I/O tasklet
* @data: our SPI device
@@ -1162,15 +1202,28 @@ static void ifx_spi_io(unsigned long data)
retval = spi_async(ifx_dev->spi_dev, &ifx_dev->spi_msg);
if (retval) {
- dev_dbg(&ifx_dev->spi_dev->dev,
- "spi_async failed (%d)", retval);
- clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS,
- &ifx_dev->flags);
- tasklet_schedule(&ifx_dev->io_work_tasklet);
+ if (retval != -EBUSY)
+ ifx_spi_complete((void *)ifx_dev);
+ else
+ ifx_spi_retry(ifx_dev);
+ return;
+ }
+ } else if (test_bit(IFX_SPI_STATE_IO_RETRY, &ifx_dev->flags)) {
+ retval = spi_async(ifx_dev->spi_dev, &ifx_dev->spi_msg);
+ if (retval) {
+ if (retval != -EBUSY)
+ ifx_spi_complete((void *)ifx_dev);
+ else
+ ifx_spi_retry(ifx_dev);
return;
}
} else {
- /* print error if called in progress */
+ /*
+ * ifx_spi_complete processing can extend beyond
+ * the point where the modem drops SRDY and so we can
+ * get a second SRDY interrupt for new I/O before
+ * completion processing is finished
+ */
dev_dbg(&ifx_dev->spi_dev->dev,
"spi_io called while SPI in progress");
ifx_dev->write_pending = 1;
@@ -1470,6 +1523,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
spin_lock_init(&ifx_dev->power_lock);
ifx_dev->power_status = 0;
init_timer(&ifx_dev->spi_timer);
+ init_timer(&ifx_dev->spi_retry_timer);
ifx_dev->spi_timer.function = ifx_spi_timeout;
ifx_dev->spi_timer.data = (unsigned long)ifx_dev;
@@ -1618,9 +1672,6 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
goto error_ret2;
}
- /* set pm runtime power state and register with power system */
- pm_runtime_set_active(&spi->dev);
- pm_runtime_enable(&spi->dev);
/* handle case that modem is already signaling SRDY */
/* no outgoing tty open at this point, this just satisfies the
@@ -1690,7 +1741,10 @@ static void ifx_spi_spi_shutdown(struct spi_device *spi)
*/
static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg)
{
+ struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi);
+
dev_dbg(&spi->dev, "%s called", __func__);
+ set_bit(IFX_SPI_STATE_SUSPEND, &ifx_dev->flags);
return 0;
}
@@ -1703,107 +1757,13 @@ static int ifx_spi_spi_suspend(struct spi_device *spi,
pm_message_t msg)
*/
static int ifx_spi_spi_resume(struct spi_device *spi)
{
- dev_dbg(&spi->dev, "%s called", __func__);
- return 0;
-}
-
-/**
- * ifx_spi_pm_suspend - suspend modem on system suspend
- * @dev: device being suspended
- *
- * Suspend the modem. No action needed on Intel MID platforms, may
- * need extending for other systems.
- */
-static int ifx_spi_pm_suspend(struct device *dev)
-{
- int retval = 0;
- struct spi_device *spi = to_spi_device(dev);
-
- dev_dbg(&spi->dev, "pm suspend");
-
- return retval;
-}
-
-/**
- * ifx_spi_pm_resume - resume modem on system resume
- * @dev: device being suspended
- *
- * Allow the modem to resume. No action needed.
- *
- * FIXME: do we need to reset anything here ?
- */
-static int ifx_spi_pm_resume(struct device *dev)
-{
- int retval = 0;
- struct spi_device *spi = to_spi_device(dev);
-
- dev_dbg(&spi->dev, "pm resume");
-
- return retval;
-}
-
-/**
- * ifx_spi_pm_runtime_resume - suspend modem
- * @dev: device being suspended
- *
- * Allow the modem to resume. No action needed.
- */
-static int ifx_spi_pm_runtime_resume(struct device *dev)
-{
- int retval = 0;
- struct spi_device *spi = to_spi_device(dev);
-
- dev_dbg(&spi->dev, "pm runtime resume");
-
- return retval;
-}
-
-/**
- * ifx_spi_pm_runtime_suspend - suspend modem
- * @dev: device being suspended
- *
- * Allow the modem to suspend and thus suspend to continue up the
- * device tree.
- */
-static int ifx_spi_pm_runtime_suspend(struct device *dev)
-{
- int retval = 0;
- struct spi_device *spi = to_spi_device(dev);
-
- dev_dbg(&spi->dev, "pm runtime_suspend");
-
- return retval;
-}
-
-/**
- * ifx_spi_pm_runtime_idle - check if modem idle
- * @dev: our device
- *
- * Check conditions and queue runtime suspend if idle.
- * FIXME: What locking/races are there on the power_status variable ?
- */
-static int ifx_spi_pm_runtime_idle(struct device *dev)
-{
- int retval = 0;
- struct spi_device *spi = to_spi_device(dev);
struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi);
- dev_dbg(&spi->dev, "pm runtime_idle");
-
- if (!ifx_dev->power_status)
- pm_runtime_suspend(dev);
-
- return retval;
+ dev_dbg(&spi->dev, "%s called", __func__);
+ clear_bit(IFX_SPI_STATE_SUSPEND, &ifx_dev->flags);
+ return 0;
}
-static const struct dev_pm_ops ifx_spi_pm = {
- .resume = ifx_spi_pm_resume,
- .suspend = ifx_spi_pm_suspend,
- .runtime_resume = ifx_spi_pm_runtime_resume,
- .runtime_suspend = ifx_spi_pm_runtime_suspend,
- .runtime_idle = ifx_spi_pm_runtime_idle
-};
-
static const struct spi_device_id ifx_spi_device_ids[] = {
{ MODEMNAME_6160, IFX_6160 },
{ MODEMNAME_6260, IFX_6260 },
@@ -1816,7 +1776,6 @@ static const struct spi_driver ifx_spi_driver = {
.driver = {
.name = DRVNAME,
.bus = &spi_bus_type,
- .pm = &ifx_spi_pm,
.owner = THIS_MODULE},
.id_table = ifx_spi_device_ids,
.probe = ifx_spi_spi_probe,
diff --git a/drivers/serial/ifx6x60.h b/drivers/serial/ifx6x60.h
index 458e385..6a1e88f 100644
--- a/drivers/serial/ifx6x60.h
+++ b/drivers/serial/ifx6x60.h
@@ -38,14 +38,17 @@
#define IFX_SPI_FIFO_SIZE 4096
#define IFX_SPI_HEADER_OVERHEAD 4
+#define IFX_RETRY_TIMEOUT msecs_to_jiffies(1)
#define IFX_RESET_TIMEOUT msecs_to_jiffies(50)
/* device state bit offsets */
enum ifx_spi_state {
IFX_SPI_STATE_PRESENT,
IFX_SPI_STATE_IO_IN_PROGRESS,
+ IFX_SPI_STATE_IO_RETRY,
IFX_SPI_STATE_IO_READY,
IFX_SPI_STATE_TIMER_PENDING,
+ IFX_SPI_STATE_SUSPEND,
};
/* flow control bit offsets */
@@ -104,6 +107,7 @@ struct ifx_spi_device {
int ifx_transfer_size;
struct timer_list spi_timer;
+ struct timer_list spi_retry_timer;
struct spi_message spi_msg;
struct spi_transfer spi_xfer;
diff --git a/drivers/spi/pw_spi3.c b/drivers/spi/pw_spi3.c
index 61f4d27..06a2d69 100644
--- a/drivers/spi/pw_spi3.c
+++ b/drivers/spi/pw_spi3.c
@@ -107,6 +107,16 @@ struct callback_param {
int *donep;
};
+enum dd_pwrstate {
+ PWRSTATE_ON = 1,
+ PWRSTATE_IDLE,
+ PWRSTATE_OFF,
+};
+
+enum dd_pwrflags {
+ PWRFLAG_RTRESUMING,
+};
+
struct driver_data {
/* Driver model hookup */
struct pci_dev *pdev;
@@ -149,10 +159,8 @@ struct driver_data {
int dma_inited;
/* pwrstate mgmt */
- int pwrstate;
-#define PWRSTATE_ON 1
-#define PWRSTATE_IDLE 2
-#define PWRSTATE_OFF 3
+ int pwrstate; /* enum dd_pwrstate */
+ unsigned long pwrflags; /* enum dd_pwrflags */
/* used by DMA code */
struct pci_dev *dmac1;
@@ -752,15 +760,19 @@ static int transfer(struct spi_device *spi, struct
spi_message *msg)
u32 cr0;
u32 cr1;
- if (drv_data->pwrstate != PWRSTATE_ON) {
+ if (drv_data->pwrstate == PWRSTATE_OFF) {
dev_dbg(&drv_data->pdev->dev, "transfer: busy, pwrstate:%d",
drv_data->pwrstate);
+ return -EIO;
+ }
+ if (drv_data->pwrstate == PWRSTATE_IDLE) {
+ if (!test_and_set_bit(PWRFLAG_RTRESUMING, &drv_data->pwrflags))
+ pm_runtime_get(&drv_data->pdev->dev);
return -EBUSY;
}
msg->actual_length = 0;
msg->status = -EINPROGRESS;
drv_data->cur_msg = msg;
- /* Initial message state*/
/* We handle only one transfer message since the protocol module has to
control the out of band signaling. */
@@ -777,7 +789,8 @@ static int transfer(struct spi_device *spi, struct
spi_message *msg)
}
/* Setup the transfer state based on the type of transfer */
- pm_runtime_get(&drv_data->pdev->dev);
+ if (!test_and_clear_bit(PWRFLAG_RTRESUMING, &drv_data->pwrflags))
+ pm_runtime_get(&drv_data->pdev->dev);
chip = spi_get_ctldata(msg->spi);
flush(drv_data);
drv_data->n_bytes = chip->n_bytes;
@@ -897,10 +910,10 @@ static int setup(struct spi_device *spi)
u32 clk_div;
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
- if (drv_data->pwrstate != PWRSTATE_ON) {
+ if (drv_data->pwrstate == PWRSTATE_OFF) {
dev_dbg(&drv_data->pdev->dev, "setup: busy, pwrstate:%d",
drv_data->pwrstate);
- return -EBUSY;
+ return -EIO;
}
if (!spi->bits_per_word)
@@ -929,7 +942,6 @@ static int setup(struct spi_device *spi)
chip_info = spi->controller_data;
/* chip_info isn't always needed */
-
if (chip_info) {
if (chip_info->timeout)
chip->timeout = chip_info->timeout;
@@ -1114,13 +1126,16 @@ static int pw_spi_probe(struct pci_dev *pdev, const
struct pci_device_id *ent)
goto err_free_4;
}
- /* set pm runtime power state and register with power system */
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
+
pw_spi_dma_init(drv_data);
pci_set_drvdata(pdev, drv_data);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+ pm_runtime_allow(dev);
+
return status;
err_free_4:
@@ -1168,8 +1183,6 @@ static void __devexit pw_spi_remove(struct pci_dev *pdev)
return;
}
-#ifdef CONFIG_PM
-
/*
* for now IDLE and OFF states are treated the same
*/
@@ -1215,29 +1228,6 @@ static int _pm_suspend(struct pci_dev *pdev, int to)
return 0;
}
-static int pw_spi_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- int retval;
- struct driver_data *drv_data = pci_get_drvdata(pdev);
-
- dev_dbg(&pdev->dev, "suspend");
-
- if (drv_data->pwrstate != PWRSTATE_ON)
- dev_warn(&pdev->dev, "suspend: !on, pwrstate:%d",
- drv_data->pwrstate);
- retval = _pm_suspend(pdev, PWRSTATE_OFF);
- if (retval)
- return retval;
- retval = pci_prepare_to_sleep(pdev);
- if (retval) {
- dev_err(&pdev->dev, "suspend: prepare to sleep failed");
- return retval;
- }
- pci_disable_device(pdev);
-
- return 0;
-}
-
/*
* for now IDLE and OFF states are treated the same
*/
@@ -1272,13 +1262,36 @@ static void _pm_resume(struct pci_dev *pdev)
}
}
-static int pw_spi_resume(struct pci_dev *pdev)
+
+#ifdef CONFIG_PM
+
+static int pw_spi_suspend(struct pci_dev *pdev, pm_message_t state)
{
int retval;
struct driver_data *drv_data = pci_get_drvdata(pdev);
- dev_dbg(&pdev->dev, "resume");
+ dev_dbg(&pdev->dev, "%s called", __func__);
+ if (drv_data->pwrstate != PWRSTATE_ON)
+ dev_warn(&pdev->dev, "suspend: !on, pwrstate:%d",
+ drv_data->pwrstate);
+ retval = _pm_suspend(pdev, PWRSTATE_OFF);
+ if (retval)
+ return retval;
+ retval = pci_prepare_to_sleep(pdev);
+ if (retval) {
+ dev_err(&pdev->dev, "suspend: prepare to sleep failed");
+ return retval;
+ }
+ pci_disable_device(pdev);
+ return 0;
+}
+
+static int pw_spi_resume(struct pci_dev *pdev)
+{
+ int retval;
+ struct driver_data *drv_data = pci_get_drvdata(pdev);
+ dev_dbg(&pdev->dev, "%s called", __func__);
if (drv_data->pwrstate != PWRSTATE_OFF)
dev_warn(&pdev->dev, "resume: !off, pwrstate:%d",
drv_data->pwrstate);
@@ -1293,9 +1306,9 @@ static int pw_spi_resume(struct pci_dev *pdev)
return retval;
}
_pm_resume(pdev);
-
return 0;
}
+
#else
#define pw_spi_suspend NULL
#define pw_spi_resume NULL
@@ -1308,17 +1321,20 @@ static int pw_spi_pm_runtime_resume(struct device *dev)
struct pci_dev *pdev = to_pci_dev(dev);
struct driver_data *drv_data = pci_get_drvdata(pdev);
- dev_dbg(dev, "pm runtime resume");
-
+ dev_dbg(dev, "%s called", __func__);
+ if (drv_data->pwrstate == PWRSTATE_ON)
+ return 0;
if (drv_data->pwrstate != PWRSTATE_IDLE)
- dev_warn(&pdev->dev, "rt suspend: !idle, pwrstate:%d",
+ dev_warn(&pdev->dev, "rt resume: !idle, pwrstate:%d",
drv_data->pwrstate);
- retval = pci_back_from_sleep(pdev);
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ retval = pci_enable_device(pdev);
if (retval)
- dev_err(&pdev->dev, "rt resume: back from sleep failed");
+ return retval;
_pm_resume(pdev);
- return 0;
+ return retval;
}
static int pw_spi_pm_runtime_suspend(struct device *dev)
@@ -1327,24 +1343,32 @@ static int pw_spi_pm_runtime_suspend(struct device *dev)
struct pci_dev *pdev = to_pci_dev(dev);
struct driver_data *drv_data = pci_get_drvdata(pdev);
- dev_dbg(dev, "pm runtime suspend");
-
+ dev_dbg(dev, "%s called", __func__);
if (drv_data->pwrstate != PWRSTATE_ON)
dev_warn(&pdev->dev, "rt suspend: !on, pwrstate:%d",
drv_data->pwrstate);
retval = _pm_suspend(pdev, PWRSTATE_IDLE);
if (retval)
return retval;
- retval = pci_prepare_to_sleep(pdev);
- if (retval)
- dev_err(&pdev->dev, "rt suspend: prepare to sleep failed");
+ pci_save_state(pdev);
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
- return 0;
+ return retval;
+}
+
+static int pw_spi_pm_runtime_idle(struct device *dev)
+{
+ dev_dbg(dev, "%s called", __func__);
+ pm_schedule_suspend(dev, 100);
+ return -EBUSY;
}
static const struct dev_pm_ops pw_spi_pm = {
.runtime_resume = pw_spi_pm_runtime_resume,
.runtime_suspend = pw_spi_pm_runtime_suspend,
+ .runtime_idle = pw_spi_pm_runtime_idle,
};
static const struct pci_device_id pci_ids[] __devinitdata = {
--
1.6.0.6
_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel