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

Commit ec60d0ad authored by Pavan Savoy's avatar Pavan Savoy Committed by Greg Kroah-Hartman
Browse files

drivers:misc: ti-st: move from rfkill to sysfs



The communication between ST KIM and UIM was interfaced
over the /dev/rfkill device node.
Move the interface to a simpler less abusive sysfs entry
mechanism and document it in Documentation/ABI/testing/
under sysfs-platform-kim.

Shared transport driver would now read the UART details
originally received by bootloader or firmware as platform
data.
The data read will be shared over sysfs entries for the user-space
UIM or other n/w manager/plugins to be read, and assist the driver
by opening up the UART, setting the baud-rate and installing the
line discipline.

Signed-off-by: default avatarPavan Savoy <pavan_savoy@ti.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 5c88b021
Loading
Loading
Loading
Loading
+48 −0
Original line number Original line Diff line number Diff line
What:		/sys/devices/platform/kim/dev_name
Date:		January 2010
KernelVersion:	2.6.38
Contact:	"Pavan Savoy" <pavan_savoy@ti.com>
Description:
		Name of the UART device at which the WL128x chip
		is connected. example: "/dev/ttyS0".
		The device name flows down to architecture specific board
		initialization file from the SFI/ATAGS bootloader
		firmware. The name exposed is read from the user-space
		dameon and opens the device when install is requested.

What:		/sys/devices/platform/kim/baud_rate
Date:		January 2010
KernelVersion:	2.6.38
Contact:	"Pavan Savoy" <pavan_savoy@ti.com>
Description:
		The maximum reliable baud-rate the host can support.
		Different platforms tend to have different high-speed
		UART configurations, so the baud-rate needs to be set
		locally and also sent across to the WL128x via a HCI-VS
		command. The entry is read and made use by the user-space
		daemon when the ldisc install is requested.

What:		/sys/devices/platform/kim/flow_cntrl
Date:		January 2010
KernelVersion:	2.6.38
Contact:	"Pavan Savoy" <pavan_savoy@ti.com>
Description:
		The WL128x makes use of flow control mechanism, and this
		entry most often should be 1, the host's UART is required
		to have the capability of flow-control, or else this
		entry can be made use of for exceptions.

What:		/sys/devices/platform/kim/install
Date:		January 2010
KernelVersion:	2.6.38
Contact:	"Pavan Savoy" <pavan_savoy@ti.com>
Description:
		When one of the protocols Bluetooth, FM or GPS wants to make
		use of the shared UART transport, it registers to the shared
		transport driver, which will signal the user-space for opening,
		configuring baud and install line discipline via this sysfs
		entry. This entry would be polled upon by the user-space
		daemon managing the UART, and is notified about the change
		by the sysfs_notify. The value would be '1' when UART needs
		to be opened/ldisc installed, and would be '0' when UART
		is no more required and needs to be closed.
+121 −123
Original line number Original line Diff line number Diff line
@@ -30,46 +30,12 @@
#include <linux/debugfs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/seq_file.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/rfkill.h>
#include <linux/tty.h>


#include <linux/skbuff.h>
#include <linux/skbuff.h>
#include <linux/ti_wilink_st.h>
#include <linux/ti_wilink_st.h>




static int kim_probe(struct platform_device *pdev);
static int kim_remove(struct platform_device *pdev);

/* KIM platform device driver structure */
static struct platform_driver kim_platform_driver = {
	.probe = kim_probe,
	.remove = kim_remove,
	/* TODO: ST driver power management during suspend/resume ?
	 */
#if 0
	.suspend = kim_suspend,
	.resume = kim_resume,
#endif
	.driver = {
		   .name = "kim",
		   .owner = THIS_MODULE,
		   },
};

static int kim_toggle_radio(void*, bool);
static const struct rfkill_ops kim_rfkill_ops = {
	.set_block = kim_toggle_radio,
};

/* strings to be used for rfkill entries and by
 * ST Core to be used for sysfs debug entry
 */
#define PROTO_ENTRY(type, name)	name
const unsigned char *protocol_names[] = {
	PROTO_ENTRY(ST_BT, "Bluetooth"),
	PROTO_ENTRY(ST_FM, "FM"),
	PROTO_ENTRY(ST_GPS, "GPS"),
};

#define MAX_ST_DEVICES	3	/* Imagine 1 on each UART for now */
#define MAX_ST_DEVICES	3	/* Imagine 1 on each UART for now */
static struct platform_device *st_kim_devices[MAX_ST_DEVICES];
static struct platform_device *st_kim_devices[MAX_ST_DEVICES];


@@ -371,8 +337,7 @@ void st_kim_chip_toggle(enum proto_type type, enum kim_gpio_state state)
	kim_gdata = dev_get_drvdata(&kim_pdev->dev);
	kim_gdata = dev_get_drvdata(&kim_pdev->dev);


	if (kim_gdata->gpios[type] == -1) {
	if (kim_gdata->gpios[type] == -1) {
		pr_info(" gpio not requested for protocol %s",
		pr_info("gpio not requested for protocol %d", type);
			   protocol_names[type]);
		return;
		return;
	}
	}
	switch (type) {
	switch (type) {
@@ -450,11 +415,6 @@ long st_kim_start(void *kim_data)
	pr_info(" %s", __func__);
	pr_info(" %s", __func__);


	do {
	do {
		/* TODO: this is only because rfkill sub-system
		 * doesn't send events to user-space if the state
		 * isn't changed
		 */
		rfkill_set_hw_state(kim_gdata->rfkill[ST_BT], 1);
		/* Configure BT nShutdown to HIGH state */
		/* Configure BT nShutdown to HIGH state */
		gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_LOW);
		gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_LOW);
		mdelay(5);	/* FIXME: a proper toggle */
		mdelay(5);	/* FIXME: a proper toggle */
@@ -462,22 +422,20 @@ long st_kim_start(void *kim_data)
		mdelay(100);
		mdelay(100);
		/* re-initialize the completion */
		/* re-initialize the completion */
		INIT_COMPLETION(kim_gdata->ldisc_installed);
		INIT_COMPLETION(kim_gdata->ldisc_installed);
#if 0 /* older way of signalling user-space UIM */
		/* send notification to UIM */
		/* send signal to UIM */
		kim_gdata->ldisc_install = 1;
		err = kill_pid(find_get_pid(kim_gdata->uim_pid), SIGUSR2, 0);
		pr_info("ldisc_install = 1");
		if (err != 0) {
		sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
			pr_info(" sending SIGUSR2 to uim failed %ld", err);
				NULL, "install");
			err = -1;
			continue;
		}
#endif
		/* unblock and send event to UIM via /dev/rfkill */
		rfkill_set_hw_state(kim_gdata->rfkill[ST_BT], 0);
		/* wait for ldisc to be installed */
		/* wait for ldisc to be installed */
		err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
		err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
				msecs_to_jiffies(LDISC_TIME));
				msecs_to_jiffies(LDISC_TIME));
		if (!err) {	/* timeout */
		if (!err) {	/* timeout */
			pr_err("line disc installation timed out ");
			pr_err("line disc installation timed out ");
			kim_gdata->ldisc_install = 0;
			pr_info("ldisc_install = 0");
			sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
					NULL, "install");
			err = -1;
			err = -1;
			continue;
			continue;
		} else {
		} else {
@@ -486,6 +444,10 @@ long st_kim_start(void *kim_data)
			err = download_firmware(kim_gdata);
			err = download_firmware(kim_gdata);
			if (err != 0) {
			if (err != 0) {
				pr_err("download firmware failed");
				pr_err("download firmware failed");
				kim_gdata->ldisc_install = 0;
				pr_info("ldisc_install = 0");
				sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
						NULL, "install");
				continue;
				continue;
			} else {	/* on success don't retry */
			} else {	/* on success don't retry */
				break;
				break;
@@ -505,16 +467,15 @@ long st_kim_stop(void *kim_data)
	struct kim_data_s	*kim_gdata = (struct kim_data_s *)kim_data;
	struct kim_data_s	*kim_gdata = (struct kim_data_s *)kim_data;


	INIT_COMPLETION(kim_gdata->ldisc_installed);
	INIT_COMPLETION(kim_gdata->ldisc_installed);
#if 0 /* older way of signalling user-space UIM */

	/* send signal to UIM */
	/* Flush any pending characters in the driver and discipline. */
	err = kill_pid(find_get_pid(kim_gdata->uim_pid), SIGUSR2, 1);
	tty_ldisc_flush(kim_gdata->core_data->tty);
	if (err != 0) {
	tty_driver_flush_buffer(kim_gdata->core_data->tty);
		pr_err("sending SIGUSR2 to uim failed %ld", err);

		return -1;
	/* send uninstall notification to UIM */
	}
	pr_info("ldisc_install = 0");
#endif
	kim_gdata->ldisc_install = 0;
	/* set BT rfkill to be blocked */
	sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, NULL, "install");
	err = rfkill_set_hw_state(kim_gdata->rfkill[ST_BT], 1);


	/* wait for ldisc to be un-installed */
	/* wait for ldisc to be un-installed */
	err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
	err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
@@ -553,33 +514,59 @@ static int show_list(struct seq_file *s, void *unused)
	return 0;
	return 0;
}
}


/* function called from rfkill subsystem, when someone from
static ssize_t show_install(struct device *dev,
 * user space would write 0/1 on the sysfs entry
		struct device_attribute *attr, char *buf)
 * /sys/class/rfkill/rfkill0,1,3/state
 */
static int kim_toggle_radio(void *data, bool blocked)
{
{
	enum proto_type type = *((enum proto_type *)data);
	struct kim_data_s *kim_data = dev_get_drvdata(dev);
	pr_debug(" %s: %d ", __func__, type);
	return sprintf(buf, "%d\n", kim_data->ldisc_install);
}


	switch (type) {
static ssize_t show_dev_name(struct device *dev,
	case ST_BT:
		struct device_attribute *attr, char *buf)
		/* do nothing */
{
	break;
	struct kim_data_s *kim_data = dev_get_drvdata(dev);
	case ST_FM:
	return sprintf(buf, "%s\n", kim_data->dev_name);
	case ST_GPS:
		if (blocked)
			st_kim_chip_toggle(type, KIM_GPIO_INACTIVE);
		else
			st_kim_chip_toggle(type, KIM_GPIO_ACTIVE);
	break;
	case ST_MAX_CHANNELS:
		pr_err(" wrong proto type ");
	break;
}
}
	return 0;

static ssize_t show_baud_rate(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct kim_data_s *kim_data = dev_get_drvdata(dev);
	return sprintf(buf, "%ld\n", kim_data->baud_rate);
}

static ssize_t show_flow_cntrl(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct kim_data_s *kim_data = dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", kim_data->flow_cntrl);
}
}


/* structures specific for sysfs entries */
static struct kobj_attribute ldisc_install =
__ATTR(install, 0444, (void *)show_install, NULL);

static struct kobj_attribute uart_dev_name =
__ATTR(dev_name, 0444, (void *)show_dev_name, NULL);

static struct kobj_attribute uart_baud_rate =
__ATTR(baud_rate, 0444, (void *)show_baud_rate, NULL);

static struct kobj_attribute uart_flow_cntrl =
__ATTR(flow_cntrl, 0444, (void *)show_flow_cntrl, NULL);

static struct attribute *uim_attrs[] = {
	&ldisc_install.attr,
	&uart_dev_name.attr,
	&uart_baud_rate.attr,
	&uart_flow_cntrl.attr,
	NULL,
};

static struct attribute_group uim_attr_grp = {
	.attrs = uim_attrs,
};

/**
/**
 * st_kim_ref - reference the core's data
 * st_kim_ref - reference the core's data
 *	This references the per-ST platform device in the arch/xx/
 *	This references the per-ST platform device in the arch/xx/
@@ -633,8 +620,9 @@ static int kim_probe(struct platform_device *pdev)
{
{
	long status;
	long status;
	long proto;
	long proto;
	long *gpios = pdev->dev.platform_data;
	struct kim_data_s	*kim_gdata;
	struct kim_data_s	*kim_gdata;
	struct ti_st_plat_data	*pdata = pdev->dev.platform_data;
	long *gpios = pdata->gpios;


	if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) {
	if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) {
		/* multiple devices could exist */
		/* multiple devices could exist */
@@ -700,30 +688,18 @@ static int kim_probe(struct platform_device *pdev)
	init_completion(&kim_gdata->kim_rcvd);
	init_completion(&kim_gdata->kim_rcvd);
	init_completion(&kim_gdata->ldisc_installed);
	init_completion(&kim_gdata->ldisc_installed);


	for (proto = 0; (proto < ST_MAX_CHANNELS)
	status = sysfs_create_group(&pdev->dev.kobj, &uim_attr_grp);
			&& (gpios[proto] != -1); proto++) {
	if (status) {
		/* TODO: should all types be rfkill_type_bt ? */
		pr_err("failed to create sysfs entries");
		kim_gdata->rf_protos[proto] = proto;
		return status;
		kim_gdata->rfkill[proto] = rfkill_alloc(protocol_names[proto],
			&pdev->dev, RFKILL_TYPE_BLUETOOTH,
			&kim_rfkill_ops, &kim_gdata->rf_protos[proto]);
		if (kim_gdata->rfkill[proto] == NULL) {
			pr_err("cannot create rfkill entry for gpio %ld",
				   gpios[proto]);
			continue;
		}
		/* block upon creation */
		rfkill_init_sw_state(kim_gdata->rfkill[proto], 1);
		status = rfkill_register(kim_gdata->rfkill[proto]);
		if (unlikely(status)) {
			pr_err("rfkill registration failed for gpio %ld",
				   gpios[proto]);
			rfkill_unregister(kim_gdata->rfkill[proto]);
			continue;
		}
		pr_info("rfkill entry created for %ld", gpios[proto]);
	}
	}


	/* copying platform data */
	strncpy(kim_gdata->dev_name, pdata->dev_name, UART_DEV_NAME_LEN);
	kim_gdata->flow_cntrl = pdata->flow_cntrl;
	kim_gdata->baud_rate = pdata->baud_rate;
	pr_info("sysfs entries created\n");

	kim_debugfs_dir = debugfs_create_dir("ti-st", NULL);
	kim_debugfs_dir = debugfs_create_dir("ti-st", NULL);
	if (IS_ERR(kim_debugfs_dir)) {
	if (IS_ERR(kim_debugfs_dir)) {
		pr_err(" debugfs entries creation failed ");
		pr_err(" debugfs entries creation failed ");
@@ -741,9 +717,9 @@ static int kim_probe(struct platform_device *pdev)


static int kim_remove(struct platform_device *pdev)
static int kim_remove(struct platform_device *pdev)
{
{
	/* free the GPIOs requested
	/* free the GPIOs requested */
	 */
	struct ti_st_plat_data	*pdata = pdev->dev.platform_data;
	long *gpios = pdev->dev.platform_data;
	long *gpios = pdata->gpios;
	long proto;
	long proto;
	struct kim_data_s	*kim_gdata;
	struct kim_data_s	*kim_gdata;


@@ -755,12 +731,11 @@ static int kim_remove(struct platform_device *pdev)
		 * nShutdown gpio from the system
		 * nShutdown gpio from the system
		 */
		 */
		gpio_free(gpios[proto]);
		gpio_free(gpios[proto]);
		rfkill_unregister(kim_gdata->rfkill[proto]);
		rfkill_destroy(kim_gdata->rfkill[proto]);
		kim_gdata->rfkill[proto] = NULL;
	}
	}
	pr_info("kim: GPIO Freed");
	pr_info("kim: GPIO Freed");
	debugfs_remove_recursive(kim_debugfs_dir);
	debugfs_remove_recursive(kim_debugfs_dir);

	sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp);
	kim_gdata->kim_pdev = NULL;
	kim_gdata->kim_pdev = NULL;
	st_core_exit(kim_gdata->core_data);
	st_core_exit(kim_gdata->core_data);


@@ -769,23 +744,46 @@ static int kim_remove(struct platform_device *pdev)
	return 0;
	return 0;
}
}


int kim_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct ti_st_plat_data	*pdata = pdev->dev.platform_data;

	if (pdata->suspend)
		return pdata->suspend(pdev, state);

	return -EOPNOTSUPP;
}

int kim_resume(struct platform_device *pdev)
{
	struct ti_st_plat_data	*pdata = pdev->dev.platform_data;

	if (pdata->resume)
		return pdata->resume(pdev);

	return -EOPNOTSUPP;
}

/**********************************************************************/
/**********************************************************************/
/* entry point for ST KIM module, called in from ST Core */
/* entry point for ST KIM module, called in from ST Core */
static struct platform_driver kim_platform_driver = {
	.probe = kim_probe,
	.remove = kim_remove,
	.suspend = kim_suspend,
	.resume = kim_resume,
	.driver = {
		.name = "kim",
		.owner = THIS_MODULE,
	},
};


static int __init st_kim_init(void)
static int __init st_kim_init(void)
{
{
	long ret = 0;
	return platform_driver_register(&kim_platform_driver);
	ret = platform_driver_register(&kim_platform_driver);
	if (ret != 0) {
		pr_err("platform drv registration failed");
		return -1;
	}
	return 0;
}
}


static void __exit st_kim_deinit(void)
static void __exit st_kim_deinit(void)
{
{
	/* the following returns void */
	platform_driver_unregister(&kim_platform_driver);
	platform_driver_unregister(&kim_platform_driver);
}
}


+17 −2
Original line number Original line Diff line number Diff line
@@ -206,8 +206,8 @@ void gps_chrdrv_stub_init(void);
/* time in msec to wait for
/* time in msec to wait for
 * line discipline to be installed
 * line discipline to be installed
 */
 */
#define LDISC_TIME	500
#define LDISC_TIME	1000
#define CMD_RESP_TIME	500
#define CMD_RESP_TIME	800
#define MAKEWORD(a, b)  ((unsigned short)(((unsigned char)(a)) \
#define MAKEWORD(a, b)  ((unsigned short)(((unsigned char)(a)) \
	| ((unsigned short)((unsigned char)(b))) << 8))
	| ((unsigned short)((unsigned char)(b))) << 8))


@@ -230,6 +230,7 @@ struct chip_version {
	unsigned short maj_ver;
	unsigned short maj_ver;
};
};


#define UART_DEV_NAME_LEN 32
/**
/**
 * struct kim_data_s - the KIM internal data, embedded as the
 * struct kim_data_s - the KIM internal data, embedded as the
 *	platform's drv data. One for each ST device in the system.
 *	platform's drv data. One for each ST device in the system.
@@ -271,6 +272,10 @@ struct kim_data_s {
	enum proto_type rf_protos[ST_MAX_CHANNELS];
	enum proto_type rf_protos[ST_MAX_CHANNELS];
	struct st_data_s *core_data;
	struct st_data_s *core_data;
	struct chip_version version;
	struct chip_version version;
	unsigned char ldisc_install;
	unsigned char dev_name[UART_DEV_NAME_LEN];
	unsigned char flow_cntrl;
	unsigned long baud_rate;
};
};


/**
/**
@@ -413,4 +418,14 @@ struct gps_event_hdr {
	u16 plen;
	u16 plen;
} __attribute__ ((packed));
} __attribute__ ((packed));


/* platform data */
struct ti_st_plat_data {
	long gpios[ST_MAX_CHANNELS]; /* BT, FM and GPS */
	unsigned char dev_name[UART_DEV_NAME_LEN]; /* uart name */
	unsigned char flow_cntrl; /* flow control flag */
	unsigned long baud_rate;
	int (*suspend)(struct platform_device *, pm_message_t);
	int (*resume)(struct platform_device *);
};

#endif /* TI_WILINK_ST_H */
#endif /* TI_WILINK_ST_H */