[RFC][PATCH 5/5] OMAP SSI API documentation

From: Carlos Chinea
Date: Fri Oct 03 2008 - 07:54:20 EST



Signed-off-by: Carlos Chinea <carlos.chinea@xxxxxxxxx>
---
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 <carlos.chinea@xxxxxxxxx>
+ *
+ * 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..990ae48
--- /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
+ - SSI_IOCTL_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 <carlos.chinea@xxxxxxxxx>
+Copyright (C) 2008 Nokia Corporation.
--
1.5.3.6

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/