Commit 806d9dd7 authored by MyungJoo Ham's avatar MyungJoo Ham Committed by Greg Kroah-Hartman

Extcon: support multiple states at a device.

One switch device (e.g., MUIC(MAX8997, MAX77686, ...), and some 30-pin
devices) may have multiple cables attached. For example, one
30-pin port may inhabit a USB cable, an HDMI cable, and a mic.
Thus, one switch device requires multiple state bits each representing
a type of cable.

For such purpose, we use the 32bit state variable; thus, up to 32
different type of cables may be defined for a switch device. The list of
possible cables is defined by the array of cable names in the switch_dev
struct given to the class.
Signed-off-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: default avatarMyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>

--
Changes from V7
- Bugfixed in _call_per_cable() (incorrect nb) (Chanwoo Choi)
- Compiler error in header for !CONFIG_EXTCON (Chanwoo Choi)

Changes from V5
- Sysfs style reformed: subdirectory per cable.
- Updated standard cable names
- Removed unnecessary printf
- Bugfixes after testing

Changes from V4
- Bugfixes after more testing at Exynos4412 boards with userspace
  processses.

Changes from V3
- Bugfixes after more testing at Exynos4412 boards.

Changes from V2
- State can be stored by user
- Documentation updated

Changes from RFC
- Switch is renamed to extcon
- Added kerneldoc comments
- Added APIs to support "standard" cable names
- Added helper APIs to support notifier block registration with cable
  name.
- Regrouped function list in the header file.
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 74c5d09b
What: /sys/class/extcon/.../ What: /sys/class/extcon/.../
Date: December 2011 Date: February 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com> Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description: Description:
Provide a place in sysfs for the extcon objects. Provide a place in sysfs for the extcon objects.
...@@ -7,8 +7,16 @@ Description: ...@@ -7,8 +7,16 @@ Description:
The name of extcon object denoted as ... is the name given The name of extcon object denoted as ... is the name given
with extcon_dev_register. with extcon_dev_register.
One extcon device denotes a single external connector
port. An external connector may have multiple cables
attached simultaneously. Many of docks, cradles, and
accessory cables have such capability. For example,
the 30-pin port of Nuri board (/arch/arm/mach-exynos)
may have both HDMI and Charger attached, or analog audio,
video, and USB cables attached simulteneously.
What: /sys/class/extcon/.../name What: /sys/class/extcon/.../name
Date: December 2011 Date: February 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com> Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description: Description:
The /sys/class/extcon/.../name shows the name of the extcon The /sys/class/extcon/.../name shows the name of the extcon
...@@ -17,10 +25,51 @@ Description: ...@@ -17,10 +25,51 @@ Description:
this sysfs node. this sysfs node.
What: /sys/class/extcon/.../state What: /sys/class/extcon/.../state
Date: December 2011 Date: February 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/extcon/.../state shows and stores the cable
attach/detach information of the corresponding extcon object.
If the extcon object has an optional callback "show_state"
defined, the showing function is overriden with the optional
callback.
If the default callback for showing function is used, the
format is like this:
# cat state
USB_OTG=1
HDMI=0
TA=1
EAR_JACK=0
#
In this example, the extcon device have USB_OTG and TA
cables attached and HDMI and EAR_JACK cables detached.
In order to update the state of an extcon device, enter a hex
state number starting with 0x.
echo 0xHEX > state
This updates the whole state of the extcon dev.
Inputs of all the methods are required to meet the
mutually_exclusive contidions if they exist.
It is recommended to use this "global" state interface if
you need to enter the value atomically. The later state
interface associated with each cable cannot update
multiple cable states of an extcon device simultaneously.
What: /sys/class/extcon/.../cable.x/name
Date: February 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/extcon/.../cable.x/name shows the name of cable
"x" (integer between 0 and 31) of an extcon device.
What: /sys/class/extcon/.../cable.x/state
Date: February 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com> Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description: Description:
The /sys/class/extcon/.../state shows the cable attach/detach The /sys/class/extcon/.../cable.x/name shows and stores the
information of the corresponding extcon object. If the extcon state of cable "x" (integer between 0 and 31) of an extcon
objecct has an optional callback "show_state" defined, the device. The state value is either 0 (detached) or 1
callback will provide the name with this sysfs node. (attached).
This diff is collapsed.
...@@ -24,10 +24,60 @@ ...@@ -24,10 +24,60 @@
#define __LINUX_EXTCON_H__ #define __LINUX_EXTCON_H__
#include <linux/notifier.h> #include <linux/notifier.h>
#define SUPPORTED_CABLE_MAX 32
#define CABLE_NAME_MAX 30
/*
* The standard cable name is to help support general notifier
* and notifee device drivers to share the common names.
* Please use standard cable names unless your notifier device has
* a very unique and abnormal cable or
* the cable type is supposed to be used with only one unique
* pair of notifier/notifee devices.
*
* Please add any other "standard" cables used with extcon dev.
*
* You may add a dot and number to specify version or specification
* of the specific cable if it is required. (e.g., "Fast-charger.18"
* and "Fast-charger.10" for 1.8A and 1.0A chargers)
* However, the notifee and notifier should be able to handle such
* string and if the notifee can negotiate the protocol or idenify,
* you don't need such convention. This convention is helpful when
* notifier can distinguish but notifiee cannot.
*/
enum extcon_cable_name {
EXTCON_USB = 0,
EXTCON_USB_HOST,
EXTCON_TA, /* Travel Adaptor */
EXTCON_FAST_CHARGER,
EXTCON_SLOW_CHARGER,
EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */
EXTCON_HDMI,
EXTCON_MHL,
EXTCON_DVI,
EXTCON_VGA,
EXTCON_DOCK,
EXTCON_LINE_IN,
EXTCON_LINE_OUT,
EXTCON_MIC_IN,
EXTCON_HEADPHONE_OUT,
EXTCON_SPDIF_IN,
EXTCON_SPDIF_OUT,
EXTCON_VIDEO_IN,
EXTCON_VIDEO_OUT,
};
extern const char *extcon_cable_name[];
struct extcon_cable;
/** /**
* struct extcon_dev - An extcon device represents one external connector. * struct extcon_dev - An extcon device represents one external connector.
* @name The name of this extcon device. Parent device name is used * @name The name of this extcon device. Parent device name is used
* if NULL. * if NULL.
* @supported_cable Array of supported cable name ending with NULL.
* If supported_cable is NULL, cable name related APIs
* are disabled.
* @print_name An optional callback to override the method to print the * @print_name An optional callback to override the method to print the
* name of the extcon device. * name of the extcon device.
* @print_state An optional callback to override the method to print the * @print_state An optional callback to override the method to print the
...@@ -38,6 +88,11 @@ ...@@ -38,6 +88,11 @@
* @nh Notifier for the state change events from this extcon * @nh Notifier for the state change events from this extcon
* @entry To support list of extcon devices so that uses can search * @entry To support list of extcon devices so that uses can search
* for extcon devices based on the extcon name. * for extcon devices based on the extcon name.
* @lock
* @max_supported Internal value to store the number of cables.
* @extcon_dev_type Device_type struct to provide attribute_groups
* customized for each extcon device.
* @cables Sysfs subdirectories. Each represents one cable.
* *
* In most cases, users only need to provide "User initializing data" of * In most cases, users only need to provide "User initializing data" of
* this struct when registering an extcon. In some exceptional cases, * this struct when registering an extcon. In some exceptional cases,
...@@ -47,6 +102,7 @@ ...@@ -47,6 +102,7 @@
struct extcon_dev { struct extcon_dev {
/* --- Optional user initializing data --- */ /* --- Optional user initializing data --- */
const char *name; const char *name;
const char **supported_cable;
/* --- Optional callbacks to override class functions --- */ /* --- Optional callbacks to override class functions --- */
ssize_t (*print_name)(struct extcon_dev *edev, char *buf); ssize_t (*print_name)(struct extcon_dev *edev, char *buf);
...@@ -57,6 +113,49 @@ struct extcon_dev { ...@@ -57,6 +113,49 @@ struct extcon_dev {
u32 state; u32 state;
struct raw_notifier_head nh; struct raw_notifier_head nh;
struct list_head entry; struct list_head entry;
spinlock_t lock; /* could be called by irq handler */
int max_supported;
/* /sys/class/extcon/.../cable.n/... */
struct device_type extcon_dev_type;
struct extcon_cable *cables;
};
/**
* struct extcon_cable - An internal data for each cable of extcon device.
* @edev The extcon device
* @cable_index Index of this cable in the edev
* @attr_g Attribute group for the cable
* @attr_name "name" sysfs entry
* @attr_state "state" sysfs entry
* @attrs Array pointing to attr_name and attr_state for attr_g
*/
struct extcon_cable {
struct extcon_dev *edev;
int cable_index;
struct attribute_group attr_g;
struct device_attribute attr_name;
struct device_attribute attr_state;
struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
};
/**
* struct extcon_specific_cable_nb - An internal data for
* extcon_register_interest().
* @internal_nb a notifier block bridging extcon notifier and cable notifier.
* @user_nb user provided notifier block for events from a specific cable.
* @cable_index the target cable.
* @edev the target extcon device.
* @previous_value the saved previous event value.
*/
struct extcon_specific_cable_nb {
struct notifier_block internal_nb;
struct notifier_block *user_nb;
int cable_index;
struct extcon_dev *edev;
unsigned long previous_value;
}; };
#if IS_ENABLED(CONFIG_EXTCON) #if IS_ENABLED(CONFIG_EXTCON)
...@@ -69,16 +168,54 @@ extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev); ...@@ -69,16 +168,54 @@ extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
extern void extcon_dev_unregister(struct extcon_dev *edev); extern void extcon_dev_unregister(struct extcon_dev *edev);
extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
/*
* get/set/update_state access the 32b encoded state value, which represents
* states of all possible cables of the multistate port. For example, if one
* calls extcon_set_state(edev, 0x7), it may mean that all the three cables
* are attached to the port.
*/
static inline u32 extcon_get_state(struct extcon_dev *edev) static inline u32 extcon_get_state(struct extcon_dev *edev)
{ {
return edev->state; return edev->state;
} }
extern void extcon_set_state(struct extcon_dev *edev, u32 state); extern void extcon_set_state(struct extcon_dev *edev, u32 state);
extern void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state);
/*
* get/set_cable_state access each bit of the 32b encoded state value.
* They are used to access the status of each cable based on the cable_name
* or cable_index, which is retrived by extcon_find_cable_index
*/
extern int extcon_find_cable_index(struct extcon_dev *sdev,
const char *cable_name);
extern int extcon_get_cable_state_(struct extcon_dev *edev, int cable_index);
extern int extcon_set_cable_state_(struct extcon_dev *edev, int cable_index,
bool cable_state);
extern int extcon_get_cable_state(struct extcon_dev *edev,
const char *cable_name);
extern int extcon_set_cable_state(struct extcon_dev *edev,
const char *cable_name, bool cable_state);
/*
* Following APIs are for notifiees (those who want to be notified)
* to register a callback for events from a specific cable of the extcon.
* Notifiees are the connected device drivers wanting to get notified by
* a specific external port of a connection device.
*/
extern int extcon_register_interest(struct extcon_specific_cable_nb *obj,
const char *extcon_name,
const char *cable_name,
struct notifier_block *nb);
extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb);
/* /*
* Following APIs are to monitor every action of a notifier. * Following APIs are to monitor every action of a notifier.
* Registerer gets notified for every external port of a connection device. * Registerer gets notified for every external port of a connection device.
* Probably this could be used to debug an action of notifier; however,
* we do not recommend to use this at normal 'notifiee' device drivers who
* want to be notified by a specific external port of the notifier.
*/ */
extern int extcon_register_notifier(struct extcon_dev *edev, extern int extcon_register_notifier(struct extcon_dev *edev,
struct notifier_block *nb); struct notifier_block *nb);
...@@ -99,6 +236,41 @@ static inline u32 extcon_get_state(struct extcon_dev *edev) ...@@ -99,6 +236,41 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
} }
static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
static inline void extcon_update_state(struct extcon_dev *edev, u32 mask,
u32 state)
{ }
static inline int extcon_find_cable_index(struct extcon_dev *edev,
const char *cable_name)
{
return 0;
}
static inline int extcon_get_cable_state_(struct extcon_dev *edev,
int cable_index)
{
return 0;
}
static inline int extcon_set_cable_state_(struct extcon_dev *edev,
int cable_index, bool cable_state)
{
return 0;
}
static inline int extcon_get_cable_state(struct extcon_dev *edev,
const char *cable_name)
{
return 0;
}
static inline int extcon_set_cable_state(struct extcon_dev *edev,
const char *cable_name, int state)
{
return 0;
}
static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
{ {
return NULL; return NULL;
...@@ -116,5 +288,18 @@ static inline int extcon_unregister_notifier(struct extcon_dev *edev, ...@@ -116,5 +288,18 @@ static inline int extcon_unregister_notifier(struct extcon_dev *edev,
return 0; return 0;
} }
static inline int extcon_register_interest(struct extcon_specific_cable_nb *obj,
const char *extcon_name,
const char *cable_name,
struct notifier_block *nb)
{
return 0;
}
static inline int extcon_unregister_interest(struct extcon_specific_cable_nb
*obj)
{
return 0;
}
#endif /* CONFIG_EXTCON */ #endif /* CONFIG_EXTCON */
#endif /* __LINUX_EXTCON_H__ */ #endif /* __LINUX_EXTCON_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment