aboutsummaryrefslogtreecommitdiff
path: root/drivers/tty/serial/ifx6x60.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/ifx6x60.c')
-rw-r--r--drivers/tty/serial/ifx6x60.c274
1 files changed, 157 insertions, 117 deletions
diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c
index 7e925e20cba..59039097099 100644
--- a/drivers/tty/serial/ifx6x60.c
+++ b/drivers/tty/serial/ifx6x60.c
@@ -60,20 +60,27 @@
#include <linux/pm_runtime.h>
#include <linux/spi/ifx_modem.h>
#include <linux/delay.h>
+#include <linux/reboot.h>
#include "ifx6x60.h"
#define IFX_SPI_MORE_MASK 0x10
-#define IFX_SPI_MORE_BIT 12 /* bit position in u16 */
-#define IFX_SPI_CTS_BIT 13 /* bit position in u16 */
+#define IFX_SPI_MORE_BIT 4 /* bit position in u8 */
+#define IFX_SPI_CTS_BIT 6 /* bit position in u8 */
#define IFX_SPI_MODE SPI_MODE_1
#define IFX_SPI_TTY_ID 0
#define IFX_SPI_TIMEOUT_SEC 2
#define IFX_SPI_HEADER_0 (-1)
#define IFX_SPI_HEADER_F (-2)
+#define PO_POST_DELAY 200
+#define IFX_MDM_RST_PMU 4
+
/* forward reference */
static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev);
+static int ifx_modem_reboot_callback(struct notifier_block *nfb,
+ unsigned long event, void *data);
+static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev);
/* local variables */
static int spi_bpw = 16; /* 8, 16 or 32 bit word length */
@@ -81,6 +88,29 @@ static struct tty_driver *tty_drv;
static struct ifx_spi_device *saved_ifx_dev;
static struct lock_class_key ifx_spi_key;
+static struct notifier_block ifx_modem_reboot_notifier_block = {
+ .notifier_call = ifx_modem_reboot_callback,
+};
+
+static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev)
+{
+ gpio_set_value(IFX_MDM_RST_PMU, 1);
+ msleep(PO_POST_DELAY);
+
+ return 0;
+}
+
+static int ifx_modem_reboot_callback(struct notifier_block *nfb,
+ unsigned long event, void *data)
+{
+ if (saved_ifx_dev)
+ ifx_modem_power_off(saved_ifx_dev);
+ else
+ pr_warn("no ifx modem active;\n");
+
+ return NOTIFY_OK;
+}
+
/* GPIO/GPE settings */
/**
@@ -152,26 +182,67 @@ ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val)
}
/**
- * swap_buf
+ * swap_buf_8
* @buf: our buffer
* @len : number of bytes (not words) in the buffer
* @end: end of buffer
*
* Swap the contents of a buffer into big endian format
*/
-static inline void swap_buf(u16 *buf, int len, void *end)
+static inline void swap_buf_8(unsigned char *buf, int len, void *end)
+{
+ /* don't swap buffer if SPI word width is 8 bits */
+ return;
+}
+
+/**
+ * swap_buf_16
+ * @buf: our buffer
+ * @len : number of bytes (not words) in the buffer
+ * @end: end of buffer
+ *
+ * Swap the contents of a buffer into big endian format
+ */
+static inline void swap_buf_16(unsigned char *buf, int len, void *end)
{
int n;
+ u16 *buf_16 = (u16 *)buf;
len = ((len + 1) >> 1);
- if ((void *)&buf[len] > end) {
- pr_err("swap_buf: swap exceeds boundary (%p > %p)!",
- &buf[len], end);
+ if ((void *)&buf_16[len] > end) {
+ pr_err("swap_buf_16: swap exceeds boundary (%p > %p)!",
+ &buf_16[len], end);
return;
}
for (n = 0; n < len; n++) {
- *buf = cpu_to_be16(*buf);
- buf++;
+ *buf_16 = cpu_to_be16(*buf_16);
+ buf_16++;
+ }
+}
+
+/**
+ * swap_buf_32
+ * @buf: our buffer
+ * @len : number of bytes (not words) in the buffer
+ * @end: end of buffer
+ *
+ * Swap the contents of a buffer into big endian format
+ */
+static inline void swap_buf_32(unsigned char *buf, int len, void *end)
+{
+ int n;
+
+ u32 *buf_32 = (u32 *)buf;
+ len = (len + 3) >> 2;
+
+ if ((void *)&buf_32[len] > end) {
+ pr_err("swap_buf_32: swap exceeds boundary (%p > %p)!\n",
+ &buf_32[len], end);
+ return;
+ }
+ for (n = 0; n < len; n++) {
+ *buf_32 = cpu_to_be32(*buf_32);
+ buf_32++;
}
}
@@ -190,9 +261,7 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev)
if (!val) {
if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING,
&ifx_dev->flags)) {
- ifx_dev->spi_timer.expires =
- jiffies + IFX_SPI_TIMEOUT_SEC*HZ;
- add_timer(&ifx_dev->spi_timer);
+ mod_timer(&ifx_dev->spi_timer,jiffies + IFX_SPI_TIMEOUT_SEC*HZ);
}
}
@@ -201,23 +270,6 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev)
}
/**
- * ifx_spi_hangup - hang up an IFX device
- * @ifx_dev: our SPI device
- *
- * Hang up the tty attached to the IFX device if one is currently
- * open. If not take no action
- */
-static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev)
-{
- struct tty_port *pport = &ifx_dev->tty_port;
- struct tty_struct *tty = tty_port_tty_get(pport);
- if (tty) {
- tty_hangup(tty);
- tty_kref_put(tty);
- }
-}
-
-/**
* ifx_spi_timeout - SPI timeout
* @arg: our SPI device
*
@@ -229,7 +281,7 @@ static void ifx_spi_timeout(unsigned long arg)
struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg;
dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***");
- ifx_spi_ttyhangup(ifx_dev);
+ tty_port_tty_hangup(&ifx_dev->tty_port, false);
mrdy_set_low(ifx_dev);
clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
}
@@ -374,25 +426,6 @@ static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count,
}
/**
- * ifx_spi_wakeup_serial - SPI space made
- * @port_data: our SPI device
- *
- * We have emptied the FIFO enough that we want to get more data
- * queued into it. Poke the line discipline via tty_wakeup so that
- * it will feed us more bits
- */
-static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev)
-{
- struct tty_struct *tty;
-
- tty = tty_port_tty_get(&ifx_dev->tty_port);
- if (!tty)
- return;
- tty_wakeup(tty);
- tty_kref_put(tty);
-}
-
-/**
* ifx_spi_prepare_tx_buffer - prepare transmit frame
* @ifx_dev: our SPI device
*
@@ -412,7 +445,6 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev)
unsigned char *tx_buffer;
tx_buffer = ifx_dev->tx_buffer;
- memset(tx_buffer, 0, IFX_SPI_TRANSFER_SIZE);
/* make room for required SPI header */
tx_buffer += IFX_SPI_HEADER_OVERHEAD;
@@ -438,7 +470,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev)
tx_count += temp_count;
if (temp_count == queue_length)
/* poke port to get more data */
- ifx_spi_wakeup_serial(ifx_dev);
+ tty_port_tty_wakeup(&ifx_dev->tty_port);
else /* more data in port, use next SPI message */
ifx_dev->spi_more = 1;
}
@@ -449,7 +481,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev)
tx_count-IFX_SPI_HEADER_OVERHEAD,
ifx_dev->spi_more);
/* swap actual data in the buffer */
- swap_buf((u16 *)(ifx_dev->tx_buffer), tx_count,
+ ifx_dev->swap_buf((ifx_dev->tx_buffer), tx_count,
&ifx_dev->tx_buffer[IFX_SPI_TRANSFER_SIZE]);
return tx_count;
}
@@ -469,9 +501,17 @@ static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf,
{
struct ifx_spi_device *ifx_dev = tty->driver_data;
unsigned char *tmp_buf = (unsigned char *)buf;
- int tx_count = kfifo_in_locked(&ifx_dev->tx_fifo, tmp_buf, count,
- &ifx_dev->fifo_lock);
- mrdy_assert(ifx_dev);
+ unsigned long flags;
+ bool is_fifo_empty;
+ int tx_count;
+
+ spin_lock_irqsave(&ifx_dev->fifo_lock, flags);
+ is_fifo_empty = kfifo_is_empty(&ifx_dev->tx_fifo);
+ tx_count = kfifo_in(&ifx_dev->tx_fifo, tmp_buf, count);
+ spin_unlock_irqrestore(&ifx_dev->fifo_lock, flags);
+ if (is_fifo_empty)
+ mrdy_assert(ifx_dev);
+
return tx_count;
}
@@ -530,11 +570,18 @@ static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty)
/* clear any old data; can't do this in 'close' */
kfifo_reset(&ifx_dev->tx_fifo);
+ /* clear any flag which may be set in port shutdown procedure */
+ clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags);
+ clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags);
+
/* put port data into this tty */
tty->driver_data = ifx_dev;
/* allows flip string push from int context */
- tty->low_latency = 1;
+ port->low_latency = 1;
+
+ /* set flag to allows data transfer */
+ set_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags);
return 0;
}
@@ -551,7 +598,9 @@ static void ifx_port_shutdown(struct tty_port *port)
struct ifx_spi_device *ifx_dev =
container_of(port, struct ifx_spi_device, tty_port);
+ clear_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags);
mrdy_set_low(ifx_dev);
+ del_timer(&ifx_dev->spi_timer);
clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
tasklet_kill(&ifx_dev->io_work_tasklet);
}
@@ -584,12 +633,8 @@ static const struct tty_operations ifx_spi_serial_ops = {
static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev,
unsigned char *chars, size_t size)
{
- struct tty_struct *tty = tty_port_tty_get(&ifx_dev->tty_port);
- if (!tty)
- return;
- tty_insert_flip_string(tty, chars, size);
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+ tty_insert_flip_string(&ifx_dev->tty_port, chars, size);
+ tty_flip_buffer_push(&ifx_dev->tty_port);
}
/**
@@ -602,8 +647,6 @@ static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev,
static void ifx_spi_complete(void *ctx)
{
struct ifx_spi_device *ifx_dev = ctx;
- struct tty_struct *tty;
- struct tty_ldisc *ldisc = NULL;
int length;
int actual_length;
unsigned char more;
@@ -617,7 +660,7 @@ static void ifx_spi_complete(void *ctx)
if (!ifx_dev->spi_msg.status) {
/* check header validity, get comm flags */
- swap_buf((u16 *)ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD,
+ ifx_dev->swap_buf(ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD,
&ifx_dev->rx_buffer[IFX_SPI_HEADER_OVERHEAD]);
decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer,
&length, &more, &cts);
@@ -636,7 +679,8 @@ static void ifx_spi_complete(void *ctx)
actual_length = min((unsigned int)length,
ifx_dev->spi_msg.actual_length);
- swap_buf((u16 *)(ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD),
+ ifx_dev->swap_buf(
+ (ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD),
actual_length,
&ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]);
ifx_spi_insert_flip_string(
@@ -680,15 +724,7 @@ complete_exit:
*/
ifx_spi_power_state_clear(ifx_dev,
IFX_SPI_POWER_DATA_PENDING);
- tty = tty_port_tty_get(&ifx_dev->tty_port);
- if (tty) {
- ldisc = tty_ldisc_ref(tty);
- if (ldisc) {
- ldisc->ops->write_wakeup(tty);
- tty_ldisc_deref(ldisc);
- }
- tty_kref_put(tty);
- }
+ tty_port_tty_wakeup(&ifx_dev->tty_port);
}
}
}
@@ -705,7 +741,8 @@ static void ifx_spi_io(unsigned long data)
int retval;
struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *) data;
- if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) {
+ if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags) &&
+ test_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags)) {
if (ifx_dev->gpio.unack_srdy_int_nb > 0)
ifx_dev->gpio.unack_srdy_int_nb--;
@@ -723,7 +760,8 @@ static void ifx_spi_io(unsigned long data)
ifx_dev->spi_xfer.cs_change = 0;
ifx_dev->spi_xfer.speed_hz = ifx_dev->spi_dev->max_speed_hz;
/* ifx_dev->spi_xfer.speed_hz = 390625; */
- ifx_dev->spi_xfer.bits_per_word = spi_bpw;
+ ifx_dev->spi_xfer.bits_per_word =
+ ifx_dev->spi_dev->bits_per_word;
ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer;
ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer;
@@ -773,6 +811,7 @@ static void ifx_spi_free_port(struct ifx_spi_device *ifx_dev)
{
if (ifx_dev->tty_dev)
tty_unregister_device(tty_drv, ifx_dev->minor);
+ tty_port_destroy(&ifx_dev->tty_port);
kfifo_free(&ifx_dev->tx_fifo);
}
@@ -800,16 +839,18 @@ static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev)
tty_port_init(pport);
pport->ops = &ifx_tty_port_ops;
ifx_dev->minor = IFX_SPI_TTY_ID;
- ifx_dev->tty_dev = tty_register_device(tty_drv, ifx_dev->minor,
- &ifx_dev->spi_dev->dev);
+ ifx_dev->tty_dev = tty_port_register_device(pport, tty_drv,
+ ifx_dev->minor, &ifx_dev->spi_dev->dev);
if (IS_ERR(ifx_dev->tty_dev)) {
dev_dbg(&ifx_dev->spi_dev->dev,
"%s: registering tty device failed", __func__);
ret = PTR_ERR(ifx_dev->tty_dev);
- goto error_ret;
+ goto error_port;
}
return 0;
+error_port:
+ tty_port_destroy(pport);
error_ret:
ifx_spi_free_port(ifx_dev);
return ret;
@@ -826,7 +867,7 @@ error_ret:
static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev)
{
if (test_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) {
- del_timer_sync(&ifx_dev->spi_timer);
+ del_timer(&ifx_dev->spi_timer);
clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
}
@@ -875,7 +916,7 @@ static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev)
set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state);
if (!solreset) {
/* unsolicited reset */
- ifx_spi_ttyhangup(ifx_dev);
+ tty_port_tty_hangup(&ifx_dev->tty_port, false);
}
} else {
/* exited reset */
@@ -967,7 +1008,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
return -ENODEV;
}
- pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data;
+ pl_data = dev_get_platdata(&spi->dev);
if (!pl_data) {
dev_err(&spi->dev, "missing platform data!");
return -ENODEV;
@@ -1001,6 +1042,14 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
return -ENODEV;
}
+ /* init swap_buf function according to word width configuration */
+ if (spi->bits_per_word == 32)
+ ifx_dev->swap_buf = swap_buf_32;
+ else if (spi->bits_per_word == 16)
+ ifx_dev->swap_buf = swap_buf_16;
+ else
+ ifx_dev->swap_buf = swap_buf_8;
+
/* ensure SPI protocol flags are initialized to enable transfer */
ifx_dev->spi_more = 0;
ifx_dev->spi_slave_cts = 0;
@@ -1219,6 +1268,9 @@ static int ifx_spi_spi_remove(struct spi_device *spi)
static void ifx_spi_spi_shutdown(struct spi_device *spi)
{
+ struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi);
+
+ ifx_modem_power_off(ifx_dev);
}
/*
@@ -1227,30 +1279,6 @@ static void ifx_spi_spi_shutdown(struct spi_device *spi)
*/
/**
- * ifx_spi_spi_suspend - suspend SPI on system suspend
- * @dev: device being suspended
- *
- * Suspend the SPI side. No action needed on Intel MID platforms, may
- * need extending for other systems.
- */
-static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return 0;
-}
-
-/**
- * ifx_spi_spi_resume - resume SPI side on system resume
- * @dev: device being suspended
- *
- * Suspend the SPI side. No action needed on Intel MID platforms, may
- * need extending for other systems.
- */
-static int ifx_spi_spi_resume(struct spi_device *spi)
-{
- return 0;
-}
-
-/**
* ifx_spi_pm_suspend - suspend modem on system suspend
* @dev: device being suspended
*
@@ -1331,16 +1359,14 @@ static const struct spi_device_id ifx_id_table[] = {
MODULE_DEVICE_TABLE(spi, ifx_id_table);
/* spi operations */
-static const struct spi_driver ifx_spi_driver = {
+static struct spi_driver ifx_spi_driver = {
.driver = {
.name = DRVNAME,
.pm = &ifx_spi_pm,
.owner = THIS_MODULE},
.probe = ifx_spi_spi_probe,
.shutdown = ifx_spi_spi_shutdown,
- .remove = __devexit_p(ifx_spi_spi_remove),
- .suspend = ifx_spi_spi_suspend,
- .resume = ifx_spi_spi_resume,
+ .remove = ifx_spi_spi_remove,
.id_table = ifx_id_table
};
@@ -1354,7 +1380,9 @@ static void __exit ifx_spi_exit(void)
{
/* unregister */
tty_unregister_driver(tty_drv);
+ put_tty_driver(tty_drv);
spi_unregister_driver((void *)&ifx_spi_driver);
+ unregister_reboot_notifier(&ifx_modem_reboot_notifier_block);
}
/**
@@ -1375,12 +1403,9 @@ static int __init ifx_spi_init(void)
return -ENOMEM;
}
- tty_drv->magic = TTY_DRIVER_MAGIC;
- tty_drv->owner = THIS_MODULE;
tty_drv->driver_name = DRVNAME;
tty_drv->name = TTYNAME;
tty_drv->minor_start = IFX_SPI_TTY_ID;
- tty_drv->num = 1;
tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
tty_drv->subtype = SERIAL_TYPE_NORMAL;
tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
@@ -1392,16 +1417,31 @@ static int __init ifx_spi_init(void)
if (result) {
pr_err("%s: tty_register_driver failed(%d)",
DRVNAME, result);
- put_tty_driver(tty_drv);
- return result;
+ goto err_free_tty;
}
result = spi_register_driver((void *)&ifx_spi_driver);
if (result) {
pr_err("%s: spi_register_driver failed(%d)",
DRVNAME, result);
- tty_unregister_driver(tty_drv);
+ goto err_unreg_tty;
+ }
+
+ result = register_reboot_notifier(&ifx_modem_reboot_notifier_block);
+ if (result) {
+ pr_err("%s: register ifx modem reboot notifier failed(%d)",
+ DRVNAME, result);
+ goto err_unreg_spi;
}
+
+ return 0;
+err_unreg_spi:
+ spi_unregister_driver((void *)&ifx_spi_driver);
+err_unreg_tty:
+ tty_unregister_driver(tty_drv);
+err_free_tty:
+ put_tty_driver(tty_drv);
+
return result;
}