Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 423b40e1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'hsi_fixes_for_3.4' of git://gitorious.org/kernel-hsi/kernel-hsi

Pull HSI fixes and ABI documentation from Carlos Chinea

* tag 'hsi_fixes_for_3.4' of git://gitorious.org/kernel-hsi/kernel-hsi:
  HSI: Add HSI ABI documentation
  HSI: hsi_char: Remove max_data_size from sysfs
  HSI: hsi: Rework hsi_event interface
  HSI: hsi: Remove controllers and ports from the bus
  HSI: hsi: Fix error path cleanup on client registration
  HSI: hsi: Rework hsi_controller release
parents 95f71472 24b7099a
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
What:		/sys/bus/hsi
Date:		April 2012
KernelVersion:	3.4
Contact:	Carlos Chinea <carlos.chinea@nokia.com>
Description:
		High Speed Synchronous Serial Interface (HSI) is a
		serial interface mainly used for connecting application
		engines (APE) with cellular modem engines (CMT) in cellular
		handsets.
		The bus will be populated with devices (hsi_clients) representing
		the protocols available in the system. Bus drivers implement
		those protocols.

What:		/sys/bus/hsi/devices/.../modalias
Date:		April 2012
KernelVersion:	3.4
Contact:	Carlos Chinea <carlos.chinea@nokia.com>
Description:	Stores the same MODALIAS value emitted by uevent
		Format: hsi:<hsi_client device name>
+1 −1
Original line number Diff line number Diff line
@@ -123,7 +123,7 @@ struct hsc_client_data {
static unsigned int hsc_major;
/* Maximum buffer size that hsi_char will accept from userspace */
static unsigned int max_data_size = 0x1000;
module_param(max_data_size, uint, S_IRUSR | S_IWUSR);
module_param(max_data_size, uint, 0);
MODULE_PARM_DESC(max_data_size, "max read/write data size [4,8..65536] (^2)");

static void hsc_add_tail(struct hsc_channel *channel, struct hsi_msg *msg,
+118 −105
Original line number Diff line number Diff line
@@ -21,26 +21,13 @@
 */
#include <linux/hsi/hsi.h>
#include <linux/compiler.h>
#include <linux/rwsem.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/notifier.h>
#include "hsi_core.h"

static struct device_type hsi_ctrl = {
	.name	= "hsi_controller",
};

static struct device_type hsi_cl = {
	.name	= "hsi_client",
};

static struct device_type hsi_port = {
	.name	= "hsi_port",
};

static ssize_t modalias_show(struct device *dev,
			struct device_attribute *a __maybe_unused, char *buf)
{
@@ -54,7 +41,6 @@ static struct device_attribute hsi_bus_dev_attrs[] = {

static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
	if (dev->type == &hsi_cl)
	add_uevent_var(env, "MODALIAS=hsi:%s", dev_name(dev));

	return 0;
@@ -80,12 +66,10 @@ static void hsi_client_release(struct device *dev)
static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info)
{
	struct hsi_client *cl;
	unsigned long flags;

	cl = kzalloc(sizeof(*cl), GFP_KERNEL);
	if (!cl)
		return;
	cl->device.type = &hsi_cl;
	cl->tx_cfg = info->tx_cfg;
	cl->rx_cfg = info->rx_cfg;
	cl->device.bus = &hsi_bus_type;
@@ -93,14 +77,11 @@ static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info)
	cl->device.release = hsi_client_release;
	dev_set_name(&cl->device, info->name);
	cl->device.platform_data = info->platform_data;
	spin_lock_irqsave(&port->clock, flags);
	list_add_tail(&cl->link, &port->clients);
	spin_unlock_irqrestore(&port->clock, flags);
	if (info->archdata)
		cl->device.archdata = *info->archdata;
	if (device_register(&cl->device) < 0) {
		pr_err("hsi: failed to register client: %s\n", info->name);
		kfree(cl);
		put_device(&cl->device);
	}
}

@@ -120,13 +101,6 @@ static void hsi_scan_board_info(struct hsi_controller *hsi)

static int hsi_remove_client(struct device *dev, void *data __maybe_unused)
{
	struct hsi_client *cl = to_hsi_client(dev);
	struct hsi_port *port = to_hsi_port(dev->parent);
	unsigned long flags;

	spin_lock_irqsave(&port->clock, flags);
	list_del(&cl->link);
	spin_unlock_irqrestore(&port->clock, flags);
	device_unregister(dev);

	return 0;
@@ -140,12 +114,17 @@ static int hsi_remove_port(struct device *dev, void *data __maybe_unused)
	return 0;
}

static void hsi_controller_release(struct device *dev __maybe_unused)
static void hsi_controller_release(struct device *dev)
{
	struct hsi_controller *hsi = to_hsi_controller(dev);

	kfree(hsi->port);
	kfree(hsi);
}

static void hsi_port_release(struct device *dev __maybe_unused)
static void hsi_port_release(struct device *dev)
{
	kfree(to_hsi_port(dev));
}

/**
@@ -170,20 +149,12 @@ int hsi_register_controller(struct hsi_controller *hsi)
	unsigned int i;
	int err;

	hsi->device.type = &hsi_ctrl;
	hsi->device.bus = &hsi_bus_type;
	hsi->device.release = hsi_controller_release;
	err = device_register(&hsi->device);
	err = device_add(&hsi->device);
	if (err < 0)
		return err;
	for (i = 0; i < hsi->num_ports; i++) {
		hsi->port[i].device.parent = &hsi->device;
		hsi->port[i].device.bus = &hsi_bus_type;
		hsi->port[i].device.release = hsi_port_release;
		hsi->port[i].device.type = &hsi_port;
		INIT_LIST_HEAD(&hsi->port[i].clients);
		spin_lock_init(&hsi->port[i].clock);
		err = device_register(&hsi->port[i].device);
		hsi->port[i]->device.parent = &hsi->device;
		err = device_add(&hsi->port[i]->device);
		if (err < 0)
			goto out;
	}
@@ -192,7 +163,9 @@ int hsi_register_controller(struct hsi_controller *hsi)

	return 0;
out:
	hsi_unregister_controller(hsi);
	while (i-- > 0)
		device_del(&hsi->port[i]->device);
	device_del(&hsi->device);

	return err;
}
@@ -222,6 +195,29 @@ static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused)
	return 0;
}

/**
 * hsi_put_controller - Free an HSI controller
 *
 * @hsi: Pointer to the HSI controller to freed
 *
 * HSI controller drivers should only use this function if they need
 * to free their allocated hsi_controller structures before a successful
 * call to hsi_register_controller. Other use is not allowed.
 */
void hsi_put_controller(struct hsi_controller *hsi)
{
	unsigned int i;

	if (!hsi)
		return;

	for (i = 0; i < hsi->num_ports; i++)
		if (hsi->port && hsi->port[i])
			put_device(&hsi->port[i]->device);
	put_device(&hsi->device);
}
EXPORT_SYMBOL_GPL(hsi_put_controller);

/**
 * hsi_alloc_controller - Allocate an HSI controller and its ports
 * @n_ports: Number of ports on the HSI controller
@@ -232,54 +228,51 @@ static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused)
struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags)
{
	struct hsi_controller	*hsi;
	struct hsi_port		*port;
	struct hsi_port		**port;
	unsigned int		i;

	if (!n_ports)
		return NULL;

	port = kzalloc(sizeof(*port)*n_ports, flags);
	if (!port)
		return NULL;
	hsi = kzalloc(sizeof(*hsi), flags);
	if (!hsi)
		goto out;
	for (i = 0; i < n_ports; i++) {
		dev_set_name(&port[i].device, "port%d", i);
		port[i].num = i;
		port[i].async = hsi_dummy_msg;
		port[i].setup = hsi_dummy_cl;
		port[i].flush = hsi_dummy_cl;
		port[i].start_tx = hsi_dummy_cl;
		port[i].stop_tx = hsi_dummy_cl;
		port[i].release = hsi_dummy_cl;
		mutex_init(&port[i].lock);
		return NULL;
	port = kzalloc(sizeof(*port)*n_ports, flags);
	if (!port) {
		kfree(hsi);
		return NULL;
	}
	hsi->num_ports = n_ports;
	hsi->port = port;
	hsi->device.release = hsi_controller_release;
	device_initialize(&hsi->device);

	for (i = 0; i < n_ports; i++) {
		port[i] = kzalloc(sizeof(**port), flags);
		if (port[i] == NULL)
			goto out;
		port[i]->num = i;
		port[i]->async = hsi_dummy_msg;
		port[i]->setup = hsi_dummy_cl;
		port[i]->flush = hsi_dummy_cl;
		port[i]->start_tx = hsi_dummy_cl;
		port[i]->stop_tx = hsi_dummy_cl;
		port[i]->release = hsi_dummy_cl;
		mutex_init(&port[i]->lock);
		ATOMIC_INIT_NOTIFIER_HEAD(&port[i]->n_head);
		dev_set_name(&port[i]->device, "port%d", i);
		hsi->port[i]->device.release = hsi_port_release;
		device_initialize(&hsi->port[i]->device);
	}

	return hsi;
out:
	kfree(port);
	hsi_put_controller(hsi);

	return NULL;
}
EXPORT_SYMBOL_GPL(hsi_alloc_controller);

/**
 * hsi_free_controller - Free an HSI controller
 * @hsi: Pointer to HSI controller
 */
void hsi_free_controller(struct hsi_controller *hsi)
{
	if (!hsi)
		return;

	kfree(hsi->port);
	kfree(hsi);
}
EXPORT_SYMBOL_GPL(hsi_free_controller);

/**
 * hsi_free_msg - Free an HSI message
 * @msg: Pointer to the HSI message
@@ -414,37 +407,67 @@ void hsi_release_port(struct hsi_client *cl)
}
EXPORT_SYMBOL_GPL(hsi_release_port);

static int hsi_start_rx(struct hsi_client *cl, void *data __maybe_unused)
static int hsi_event_notifier_call(struct notifier_block *nb,
				unsigned long event, void *data __maybe_unused)
{
	if (cl->hsi_start_rx)
		(*cl->hsi_start_rx)(cl);
	struct hsi_client *cl = container_of(nb, struct hsi_client, nb);

	(*cl->ehandler)(cl, event);

	return 0;
}

static int hsi_stop_rx(struct hsi_client *cl, void *data __maybe_unused)
/**
 * hsi_register_port_event - Register a client to receive port events
 * @cl: HSI client that wants to receive port events
 * @cb: Event handler callback
 *
 * Clients should register a callback to be able to receive
 * events from the ports. Registration should happen after
 * claiming the port.
 * The handler can be called in interrupt context.
 *
 * Returns -errno on error, or 0 on success.
 */
int hsi_register_port_event(struct hsi_client *cl,
			void (*handler)(struct hsi_client *, unsigned long))
{
	if (cl->hsi_stop_rx)
		(*cl->hsi_stop_rx)(cl);
	struct hsi_port *port = hsi_get_port(cl);

	return 0;
	if (!handler || cl->ehandler)
		return -EINVAL;
	if (!hsi_port_claimed(cl))
		return -EACCES;
	cl->ehandler = handler;
	cl->nb.notifier_call = hsi_event_notifier_call;

	return atomic_notifier_chain_register(&port->n_head, &cl->nb);
}
EXPORT_SYMBOL_GPL(hsi_register_port_event);

static int hsi_port_for_each_client(struct hsi_port *port, void *data,
				int (*fn)(struct hsi_client *cl, void *data))
/**
 * hsi_unregister_port_event - Stop receiving port events for a client
 * @cl: HSI client that wants to stop receiving port events
 *
 * Clients should call this function before releasing their associated
 * port.
 *
 * Returns -errno on error, or 0 on success.
 */
int hsi_unregister_port_event(struct hsi_client *cl)
{
	struct hsi_client *cl;
	struct hsi_port *port = hsi_get_port(cl);
	int err;

	spin_lock(&port->clock);
	list_for_each_entry(cl, &port->clients, link) {
		spin_unlock(&port->clock);
		(*fn)(cl, data);
		spin_lock(&port->clock);
	}
	spin_unlock(&port->clock);
	WARN_ON(!hsi_port_claimed(cl));

	return 0;
	err = atomic_notifier_chain_unregister(&port->n_head, &cl->nb);
	if (!err)
		cl->ehandler = NULL;

	return err;
}
EXPORT_SYMBOL_GPL(hsi_unregister_port_event);

/**
 * hsi_event -Notifies clients about port events
@@ -458,22 +481,12 @@ static int hsi_port_for_each_client(struct hsi_port *port, void *data,
 * Events:
 * HSI_EVENT_START_RX - Incoming wake line high
 * HSI_EVENT_STOP_RX - Incoming wake line down
 *
 * Returns -errno on error, or 0 on success.
 */
void hsi_event(struct hsi_port *port, unsigned int event)
int hsi_event(struct hsi_port *port, unsigned long event)
{
	int (*fn)(struct hsi_client *cl, void *data);

	switch (event) {
	case HSI_EVENT_START_RX:
		fn = hsi_start_rx;
		break;
	case HSI_EVENT_STOP_RX:
		fn = hsi_stop_rx;
		break;
	default:
		return;
	}
	hsi_port_for_each_client(port, NULL, fn);
	return atomic_notifier_call_chain(&port->n_head, event, NULL);
}
EXPORT_SYMBOL_GPL(hsi_event);

+17 −14
Original line number Diff line number Diff line
@@ -26,9 +26,9 @@
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/notifier.h>

/* HSI message ttype */
#define HSI_MSG_READ	0
@@ -121,18 +121,18 @@ static inline int hsi_register_board_info(struct hsi_board_info const *info,
 * @device: Driver model representation of the device
 * @tx_cfg: HSI TX configuration
 * @rx_cfg: HSI RX configuration
 * @hsi_start_rx: Called after incoming wake line goes high
 * @hsi_stop_rx: Called after incoming wake line goes low
 * @e_handler: Callback for handling port events (RX Wake High/Low)
 * @pclaimed: Keeps tracks if the clients claimed its associated HSI port
 * @nb: Notifier block for port events
 */
struct hsi_client {
	struct device		device;
	struct hsi_config	tx_cfg;
	struct hsi_config	rx_cfg;
	void			(*hsi_start_rx)(struct hsi_client *cl);
	void			(*hsi_stop_rx)(struct hsi_client *cl);
	/* private: */
	void			(*ehandler)(struct hsi_client *, unsigned long);
	unsigned int		pclaimed:1;
	struct list_head	link;
	struct notifier_block	nb;
};

#define to_hsi_client(dev) container_of(dev, struct hsi_client, device)
@@ -147,6 +147,10 @@ static inline void *hsi_client_drvdata(struct hsi_client *cl)
	return dev_get_drvdata(&cl->device);
}

int hsi_register_port_event(struct hsi_client *cl,
			void (*handler)(struct hsi_client *, unsigned long));
int hsi_unregister_port_event(struct hsi_client *cl);

/**
 * struct hsi_client_driver - Driver associated to an HSI client
 * @driver: Driver model representation of the driver
@@ -214,8 +218,7 @@ void hsi_free_msg(struct hsi_msg *msg);
 * @start_tx: Callback to inform that a client wants to TX data
 * @stop_tx: Callback to inform that a client no longer wishes to TX data
 * @release: Callback to inform that a client no longer uses the port
 * @clients: List of hsi_clients using the port.
 * @clock: Lock to serialize access to the clients list.
 * @n_head: Notifier chain for signaling port events to the clients.
 */
struct hsi_port {
	struct device			device;
@@ -231,14 +234,14 @@ struct hsi_port {
	int				(*start_tx)(struct hsi_client *cl);
	int				(*stop_tx)(struct hsi_client *cl);
	int				(*release)(struct hsi_client *cl);
	struct list_head		clients;
	spinlock_t			clock;
	/* private */
	struct atomic_notifier_head	n_head;
};

#define to_hsi_port(dev) container_of(dev, struct hsi_port, device)
#define hsi_get_port(cl) to_hsi_port((cl)->device.parent)

void hsi_event(struct hsi_port *port, unsigned int event);
int hsi_event(struct hsi_port *port, unsigned long event);
int hsi_claim_port(struct hsi_client *cl, unsigned int share);
void hsi_release_port(struct hsi_client *cl);

@@ -270,13 +273,13 @@ struct hsi_controller {
	struct module		*owner;
	unsigned int		id;
	unsigned int		num_ports;
	struct hsi_port		*port;
	struct hsi_port		**port;
};

#define to_hsi_controller(dev) container_of(dev, struct hsi_controller, device)

struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags);
void hsi_free_controller(struct hsi_controller *hsi);
void hsi_put_controller(struct hsi_controller *hsi);
int hsi_register_controller(struct hsi_controller *hsi);
void hsi_unregister_controller(struct hsi_controller *hsi);

@@ -294,7 +297,7 @@ static inline void *hsi_controller_drvdata(struct hsi_controller *hsi)
static inline struct hsi_port *hsi_find_port_num(struct hsi_controller *hsi,
							unsigned int num)
{
	return (num < hsi->num_ports) ? &hsi->port[num] : NULL;
	return (num < hsi->num_ports) ? hsi->port[num] : NULL;
}

/*