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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Oct 03, 2008 at 02:52:30PM +0300, Carlos Chinea wrote:

there are issues in the documentation as well.

> 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

don't ifdef here. This file should only be built if SSI is selected.

> +
> +#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>

you see how many includes you need to make it work ?

It should be #include <linux/ssi.h> and that's all. This driver can be
generic enough to build and work on non-omap platforms, can't it ?

> +
> +#include "clock.h"
> +
> +struct ssi_internal_clk {
> +	struct clk clk;
> +	struct clk **childs;
> +	int n_childs;
> +	struct platform_device *pdev;

why do you need to let the internal clock know so much about the user ?
I think struct device * should be enough here.

> +};
> +
> +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,
> +		},

tabify these.

> +};
> +
> +static struct ssi_platform_data ssi_p_d = {
> +	.clk_name = "ssi_clk",
> +	.num_ports = ARRAY_SIZE(ssi_ports),
> +	.ports = ssi_ports,

tabify

> +};
> +
> +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,
> +		},

tabify and the closing bracket should be aligned with [0]

> +	[1] = 	{
	     ^  trailing whitespace

> +		.start = SSI_P1_MPU_IRQ0,
> +		.end = SSI_P1_MPU_IRQ0,
> +		.name = SSI_P1_MPU_IRQ0_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify
tabify and the closing bracket should be aligned with [1]

> +	[2] = 	{
	     ^  trailing whitespace

> +		.start = SSI_P1_MPU_IRQ1,
> +		.end = SSI_P1_MPU_IRQ1,
> +		.name = SSI_P1_MPU_IRQ1_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify
tabify and the closing bracket should be aligned with [2]

> +	[3] = 	{
	     ^  trailing whitespace

> +		.start = SSI_P2_MPU_IRQ0,
> +		.end = SSI_P2_MPU_IRQ0,
> +		.name = SSI_P2_MPU_IRQ0_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify
tabify and the closing bracket should be aligned with [3]

> +	[4] = 	{
	     ^  trailing whitespace

> +		.start = SSI_P2_MPU_IRQ1,
> +		.end = SSI_P2_MPU_IRQ1,
> +		.name = SSI_P2_MPU_IRQ1_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify
tabify and the closing bracket should be aligned with [4]

> +	[5] = 	{
	     ^  trailing whitespace

> +		.start = SSI_GDD_MPU_IRQ,
> +		.end = SSI_GDD_MPU_IRQ,
> +		.name = SSI_GDD_MPU_IRQ_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify and the closing bracket should be aligned with [5]

> +};
> +
> +static struct platform_device ssi_pdev = {
> +	.name = "omap_ssi",
> +	.id = -1,
> +	.num_resources = ARRAY_SIZE(ssi_resources),
> +	.resource = ssi_resources,
> +	.dev = 	{
	      ^  trailing whitespace

> +		.release = ssi_pdev_release,
> +		.platform_data = &ssi_p_d,
> +		},

this bracket should be aligned with .dev

> +};
> +
> +static void set_ssi_mode(struct platform_device *pdev, u32 mode)
> +{
> +	void __iomem *base = (void __iomem *)pdev->resource[0].start;

this is wrong, looks like mode sould be passed down to the driver and
the driver would set_ssi_mode().

> +	int port;
> +	int num_ports = ((struct ssi_platform_data *)

the cast is unnecessary and you're abusing platform_data, I'd say.

> +					(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;
> +		}
> +	}

this looks like it should be done by the driver and not board-specific.

> +
> +	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]);

if you get and error, return already, will avoid the else below.

> +	} 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,
> +};

it doesn't look like you do anything useful with this ssi_internal_clk
structure. I'd say you can move the clk definition to <mach/clock.h> if
Tony, Paul and Kevin are ok with it.

> +
> +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);

if the clk definition can be moved to <mach/clock.h> then you can
simply:

return platform_device_register(&ssi_pdev);

> +}
> +#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

let's use an extension to this file: ssi.txt

> @@ -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. 
							  ^ trailing whitespace

> +
> +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

s/an/and (at the end)

> +attaching SSI channel devices to it.(see Figure 1)
				       ^ add a space

> +
> +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)

you could go simpler and call it:

/sys/bus/ssi/devices/X-Y: (X == port, Y == channel);

> +
> +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-omap" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux