Signed-off-by: Carlos Chinea <[EMAIL PROTECTED]>
---
 Documentation/arm/OMAP/ssi/board-ssi.c.example |  216 ++++++++++++++++++++++
 Documentation/arm/OMAP/ssi/ssi                 |  232 ++++++++++++++++++++++++
 2 files changed, 448 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/arm/OMAP/ssi/board-ssi.c.example
 create mode 100644 Documentation/arm/OMAP/ssi/ssi

diff --git a/Documentation/arm/OMAP/ssi/board-ssi.c.example 
b/Documentation/arm/OMAP/ssi/board-ssi.c.example
new file mode 100644
index 0000000..a346628
--- /dev/null
+++ b/Documentation/arm/OMAP/ssi/board-ssi.c.example
@@ -0,0 +1,216 @@
+/*
+ * board-ssi.c.example
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <[EMAIL PROTECTED]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifdef CONFIG_OMAP_SSI
+
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/ssi_driver_if.h>
+#include <mach/ssi/ssi_sys_reg.h>
+#include <mach/ssi/ssi_ssr_reg.h>
+#include <mach/ssi/ssi_sst_reg.h>
+
+#include "clock.h"
+
+struct ssi_internal_clk {
+       struct clk clk;
+       struct clk **childs;
+       int n_childs;
+       struct platform_device *pdev;
+};
+
+static struct ssi_internal_clk ssi_clock;
+
+static void ssi_pdev_release(struct device *dev)
+{
+}
+
+static struct ssi_port_pd ssi_ports[] = {
+       [0] = {
+               .tx_mode = SSI_MODE_FRAME,
+               .tx_frame_size = SSI_FRAMESIZE_DEFAULT,
+               .divisor = SSI_DIVISOR_DEFAULT,
+               .tx_ch = SSI_CHANNELS_DEFAULT,
+               .arb_mode = SSI_ARBMODE_ROUNDROBIN,
+               .rx_mode = SSI_MODE_FRAME,
+               .rx_frame_size = SSI_FRAMESIZE_DEFAULT,
+               .rx_ch = SSI_CHANNELS_DEFAULT,
+               .timeout = SSI_TIMEOUT_DEFAULT,
+               .n_irq = 0,
+               },
+};
+
+static struct ssi_platform_data ssi_p_d = {
+       .clk_name = "ssi_clk",
+       .num_ports = ARRAY_SIZE(ssi_ports),
+       .ports = ssi_ports,
+};
+
+static struct resource ssi_resources[] = {
+       [0] = {
+               .start = SSI_IOMEM_BASE_ADDR,
+               .end = SSI_IOMEM_BASE_ADDR + SSI_IOMEM_SIZE,
+               .name = SSI_IOMEM_NAME,
+               .flags = IORESOURCE_MEM,
+               },
+       [1] =   {
+               .start = SSI_P1_MPU_IRQ0,
+               .end = SSI_P1_MPU_IRQ0,
+               .name = SSI_P1_MPU_IRQ0_NAME,
+               .flags = IORESOURCE_IRQ,
+               },
+       [2] =   {
+               .start = SSI_P1_MPU_IRQ1,
+               .end = SSI_P1_MPU_IRQ1,
+               .name = SSI_P1_MPU_IRQ1_NAME,
+               .flags = IORESOURCE_IRQ,
+               },
+       [3] =   {
+               .start = SSI_P2_MPU_IRQ0,
+               .end = SSI_P2_MPU_IRQ0,
+               .name = SSI_P2_MPU_IRQ0_NAME,
+               .flags = IORESOURCE_IRQ,
+               },
+       [4] =   {
+               .start = SSI_P2_MPU_IRQ1,
+               .end = SSI_P2_MPU_IRQ1,
+               .name = SSI_P2_MPU_IRQ1_NAME,
+               .flags = IORESOURCE_IRQ,
+               },
+       [5] =   {
+               .start = SSI_GDD_MPU_IRQ,
+               .end = SSI_GDD_MPU_IRQ,
+               .name = SSI_GDD_MPU_IRQ_NAME,
+               .flags = IORESOURCE_IRQ,
+               },
+};
+
+static struct platform_device ssi_pdev = {
+       .name = "omap_ssi",
+       .id = -1,
+       .num_resources = ARRAY_SIZE(ssi_resources),
+       .resource = ssi_resources,
+       .dev =  {
+               .release = ssi_pdev_release,
+               .platform_data = &ssi_p_d,
+               },
+};
+
+static void set_ssi_mode(struct platform_device *pdev, u32 mode)
+{
+       void __iomem *base = (void __iomem *)pdev->resource[0].start;
+       int port;
+       int num_ports = ((struct ssi_platform_data *)
+                                       (pdev->dev.platform_data))->num_ports;
+
+       for (port = 0; port < num_ports; port++) {
+               outl(mode, OMAP2_IO_ADDRESS(base + SSI_SST_MODE_REG(port)));
+               outl(mode, OMAP2_IO_ADDRESS(base + SSI_SSR_MODE_REG(port)));
+       }
+}
+
+static int ssi_clk_init(struct ssi_internal_clk *ssi_clk)
+{
+       const char *clk_names[] = { "ssi_ick", "ssi_ssr_fck" };
+       int i;
+       int j;
+
+       ssi_clk->n_childs = ARRAY_SIZE(clk_names);
+       ssi_clk->childs = kzalloc(ssi_clk->n_childs * sizeof(*ssi_clk->childs),
+                                                               GFP_KERNEL);
+       if (!ssi_clk->childs)
+               return -ENOMEM;
+
+       for (i = 0; i < ssi_clk->n_childs; i++) {
+               ssi_clk->childs[i] = clk_get(NULL, clk_names[i]);
+               if (IS_ERR(ssi_clk->childs[i])) {
+                       pr_err("Unable to get SSI clock: %s", clk_names[i]);
+                       for (j = i - 1; j >= 0; j--)
+                               clk_put(ssi_clk->childs[j]);
+                       return -ENODEV;
+               }
+       }
+
+       return 0;
+}
+
+static int ssi_clk_enable(struct clk *clk)
+{
+       struct ssi_internal_clk *ssi_clk =
+                               container_of(clk, struct ssi_internal_clk, clk);
+       int err = 0;
+       int i;
+       int j;
+
+       for (i = 0; ((i < ssi_clk->n_childs) && (err >= 0)); i++)
+               err = omap2_clk_enable(ssi_clk->childs[i]);
+
+       if (unlikely(err < 0)) {
+               pr_err("Error on SSI clk %d\n", i);
+               for (j = i - 1; j >= 0; j--)
+                       omap2_clk_disable(ssi_clk->childs[j]);
+       } else {
+               if (ssi_clk->clk.usecount == 1)
+                       set_ssi_mode(ssi_clk->pdev, SSI_MODE_FRAME);
+       }
+
+       return err;
+}
+
+static void ssi_clk_disable(struct clk *clk)
+{
+       struct ssi_internal_clk *ssi_clk =
+                               container_of(clk, struct ssi_internal_clk, clk);
+
+       int i;
+
+       if (ssi_clk->clk.usecount == 0)
+               set_ssi_mode(ssi_clk->pdev, SSI_MODE_SLEEP);
+
+       for (i = 0; i < ssi_clk->n_childs; i++)
+               omap2_clk_disable(ssi_clk->childs[i]);
+}
+
+static struct ssi_internal_clk ssi_clock = {
+       .clk = {
+               .name = "ssi_clk",
+               .id = -1,
+               .enable = ssi_clk_enable,
+               .disable = ssi_clk_disable,
+       },
+       .pdev = &ssi_pdev,
+};
+
+void __init ssi_init(void)
+{
+       int err;
+
+       ssi_clk_init(&ssi_clock);
+       clk_register(&ssi_clock.clk);
+
+       err = platform_device_register(&ssi_pdev);
+       if (err < 0)
+               pr_err("Unable to register SSI platform device: %d\n", err);
+}
+#endif
diff --git a/Documentation/arm/OMAP/ssi/ssi b/Documentation/arm/OMAP/ssi/ssi
new file mode 100644
index 0000000..27702bf
--- /dev/null
+++ b/Documentation/arm/OMAP/ssi/ssi
@@ -0,0 +1,232 @@
+OMAP SSI API's How To
+=====================
+
+The Synchronous Serial Interface (SSI) is a high speed communication interface
+that is used for connecting OMAP to a cellular modem engine.
+
+The SSI interface supports full duplex communication over multiple channels and
+is capable of reaching speeds up to 110 Mbit/s
+
+I OMAP SSI driver API overview
+-----------------------------
+
+A) SSI Bus, SSI channels and protocol drivers overview. 
+
+The OMAP SSI driver is intended to be used inside the kernel by protocol 
drivers.
+
+The OMAP SSI abstracts the concept of SSI channels by creating an SSI bus an
+attaching SSI channel devices to it.(see Figure 1)
+
+Protocol drivers will then claim one or more SSI channels, after registering 
with the OMAP SSI driver.
+
+       +---------------------+         +----------------+
+       +  SSI channel device +         +  SSI protocol  +
+       +  (omap_ssi.pX-cY)   + <-------+  driver        +
+       +---------------------+         +----------------+
+               |                               |
+(/sys/bus/ssi/devices/omap_ssi.pX-cy)  (/sys/bus/ssi/drivers/ssi_protocol)
+               |                               |
++---------------------------------------------------------------+
++                      SSI bus                                 +       
++---------------------------------------------------------------+      
+       
+                       Figure 1.
+
+(NOTE: omap_ssi.pX-cY represents the SSI channel Y on port X from the omap_ssi
+device)
+
+B) Data transfers
+
+The OMAP SSI driver exports an asynchronous interface for sending and receiving
+data over the SSI channels. Protocol drivers will register a set of read and 
write
+completion callbacks for each SSI channel they use.
+
+Protocol drivers call ssi_write/ssi_read functions to signal the OMAP SSI 
driver
+that is willing to write/read data to/from a channel. Transfers are completed 
only
+when the OMAP SSI driver calls the completion callback.
+
+An SSI channel can simultaneously have both a read and a write request
+pending, however, requests cannot be queued.
+
+It is safe to call ssi_write/ssi_read functions inside the callbacks functions.
+In fact, a protocol driver should normally re-issue the read request from 
within
+the read callback, in order to not miss any incoming messages.
+
+C) Error handling
+
+SSI is a multi channel interface but the channels share the same physical 
wires.
+Therefore, any transmission error potentially affects all the protocol drivers
+that sit on top of the SSI driver. Whenever an error occurs, it is broadcasted 
to
+all protocol drivers.
+
+Errors are signaled to the protocol drivers through the port_event callback.
+Protocol drivers can avoid receiving those notifications by not setting the
+SSI_EVENT_ERROR in the event_mask field.(see struct ssi_device_driver)
+
+Completion callbacks functions are only called when a transfer has succeed.
+
+II OMAP SSI API's
+-----------------
+
+A) Include
+
+#include<linux/ssi_driver_if.h>
+
+B) int register_ssi_driver(struct ssi_device_driver *driver);
+
+Description: Register an SSI protocol driver
+
+Parameter: A protocol driver declaration (see struct ssi_device_driver)
+
+B) void unregister_ssi_driver(struct ssi_device_driver *driver);
+
+Description: Unregister an SSI protocol driver
+
+Parameter: A protocol driver declaration (see struct ssi_device_driver)
+
+C) int ssi_open(struct ssi_device *dev);
+
+Description: Open an SSI device channel
+
+Parameter: The SSI channel
+
+D) int ssi_write(struct ssi_device *dev, u32 *data, unsigned int count);
+
+Description: Send data through an SSI channel. The transfer is only completed
+when the write_complete callback is called
+
+Parameters:
+       - dev: SSI channel
+       - data: pointer to the data to send
+       - count: number of 32-bit words to be sent
+
+E) void ssi_write_cancel(struct ssi_device *dev);
+
+Description: Cancel current pending write operation
+
+Parameters: SSI channel
+       
+F) int ssi_read(struct ssi_device *dev, u32 *data, unsigned int w_count);
+
+Description: Receive data through an SSI channel. The transfer is only 
completed
+when the read_complete callback is called
+
+Parameters:
+       - dev: SSI channel
+       - data: pointer where to store the data
+       - count: number of 32-bit words to be read
+
+
+G) void ssi_read_cancel(struct ssi_device *dev);
+
+Description: Cancel current pending read operation
+
+Parameters: SSI channel
+
+H) int ssi_ioctl(struct ssi_device *dev, unsigned int command, void *arg);
+
+Description: Apply some control command to the port associated to the given
+SSI channel
+
+Parameters:
+       - dev: SSI channel
+       - command: command to execute
+       - arg: parameter for the control command
+
+Commands:
+       - SSI_IOCTL_WAKE_UP: 
+               Description: Set SSI wakeup line for the channel
+               Parameters: None
+       - SSI_IOCTL_WAKE_DOWN:
+               Description: Unset SSI wakeup line for the channel
+               Parameters: None
+       - SSI_IOCTL_SEND_BREAK:
+               Description: Send a HW BREAK frame in FRAME mode
+               Parameters: None
+       - GET_WAKE:
+               Description: Get wakeup line status
+               Parameters: Pointer to a u32 variable to return result
+               (Result: 0 means wakeline DOWN, other result means wakeline UP)
+
+I)void ssi_close(struct ssi_device *dev);
+
+Description: Close an SSI channel
+
+Parameters: The SSI channel to close
+
+J) void ssi_dev_set_cb(        struct ssi_device *dev,
+                       void (*r_cb)(struct ssi_device *dev),
+                       void (*w_cb)(struct ssi_device *dev));
+
+Description: Set the read and write callbacks for the SSI channel. This
+function is usually called in the probe function of the SSI protocol driver to
+set completion callbacks for the asynchronous read and write transfer
+
+Parameters:
+       - dev: SSI channel
+       - r_cb: Pointer to a callback function to signal that a read transfer is
+               completed
+       - w_cb: Pointer to a callback function to signal that a write transfer
+               is completed
+
+H) struct ssi_device_driver
+
+Description: Protocol drivers pass this struct to the register_ssi_driver 
function
+in order to register with the OMAP SSI driver. Among other things it tells the
+OMAP SSI driver which channels the protocol driver wants to allocate for its 
use
+
+Declaration:
+struct ssi_device_driver {
+       unsigned long           ctrl_mask;
+       unsigned long           ch_mask[SSI_MAX_PORTS];
+       unsigned long           event_mask;
+       void                    (*port_event) (int c_id, unsigned int port,
+                                               unsigned int event, void *arg);
+       int                     (*probe)(struct ssi_device *dev);
+       int                     (*remove)(struct ssi_device *dev);
+       int                     (*suspend)(struct ssi_device *dev,
+                                               pm_message_t mesg);
+       int                     (*resume)(struct ssi_device *dev);
+       struct device_driver    driver;
+};
+
+Fields description:
+       ctrl_mask: SSI block ids to use
+       ch_mask[SSI_MAX_PORTS]: SSI channels to use
+       event_mask: SSI events to be notified
+       port_event: Function callback for notifying SSI events
+                  (i.e.: error transfer)
+               Parameters:
+                       c_id: SSI Block id which generate the event
+                       port: Port number which generate the event
+                       event: Event code
+       probe: Probe function
+               Parameters: SSI channel
+       remove: Remove function
+               Parameters: SSI channel
+
+Example:
+
+static struct ssi_device_driver ssi_protocol_driver = {
+       .ctrl_mask = ANY_SSI_CONTROLLER,
+       .ch_mask[0] = CHANNEL(0) | CHANNEL(1),
+       .event_mask = SSI_EVENT_ERROR_MASK,
+       .port_event = port_event_callback,
+       .probe = ssi_proto_probe,
+       .remove = __devexit_p(ssi_proto_remove),
+       .driver = {
+                       .name = "ssi_protocol",
+       },
+};
+
+
+III OMAP SSI platform_device
+----------------------------
+
+You can find a example of how to define an SSI platform device in:
+
+Documentation/arm/OMAP/ssi/board-ssi.c.example
+
+=================================================
+Contact: Carlos Chinea <[EMAIL PROTECTED]>
+Copyright (C) 2008 Nokia Corporation.
-- 
1.5.3.6

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to