Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
15dec834
Commit
15dec834
authored
Jul 17, 2017
by
Chanwoo Choi
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/ib-extcon-mfd-4.14' into extcon-next
parents
ff890bc0
a8a5549f
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
522 additions
and
0 deletions
+522
-0
Documentation/devicetree/bindings/extcon/extcon-usbc-cros-ec.txt
...tation/devicetree/bindings/extcon/extcon-usbc-cros-ec.txt
+24
-0
drivers/extcon/Kconfig
drivers/extcon/Kconfig
+7
-0
drivers/extcon/Makefile
drivers/extcon/Makefile
+1
-0
drivers/extcon/extcon-usbc-cros-ec.c
drivers/extcon/extcon-usbc-cros-ec.c
+415
-0
include/linux/mfd/cros_ec_commands.h
include/linux/mfd/cros_ec_commands.h
+75
-0
No files found.
Documentation/devicetree/bindings/extcon/extcon-usbc-cros-ec.txt
0 → 100644
View file @
15dec834
ChromeOS EC USB Type-C cable and accessories detection
On ChromeOS systems with USB Type C ports, the ChromeOS Embedded Controller is
able to detect the state of external accessories such as display adapters
or USB devices when said accessories are attached or detached.
The node for this device must be under a cros-ec node like google,cros-ec-spi
or google,cros-ec-i2c.
Required properties:
- compatible: Should be "google,extcon-usbc-cros-ec".
- google,usb-port-id: Specifies the USB port ID to use.
Example:
cros-ec@0 {
compatible = "google,cros-ec-i2c";
...
extcon {
compatible = "google,extcon-usbc-cros-ec";
google,usb-port-id = <0>;
};
}
drivers/extcon/Kconfig
View file @
15dec834
...
...
@@ -150,4 +150,11 @@ config EXTCON_USB_GPIO
Say Y here to enable GPIO based USB cable detection extcon support.
Used typically if GPIO is used for USB ID pin detection.
config EXTCON_USBC_CROS_EC
tristate "ChromeOS Embedded Controller EXTCON support"
depends on MFD_CROS_EC
help
Say Y here to enable USB Type C cable detection extcon support when
using Chrome OS EC based USB Type-C ports.
endif
drivers/extcon/Makefile
View file @
15dec834
...
...
@@ -20,3 +20,4 @@ obj-$(CONFIG_EXTCON_QCOM_SPMI_MISC) += extcon-qcom-spmi-misc.o
obj-$(CONFIG_EXTCON_RT8973A)
+=
extcon-rt8973a.o
obj-$(CONFIG_EXTCON_SM5502)
+=
extcon-sm5502.o
obj-$(CONFIG_EXTCON_USB_GPIO)
+=
extcon-usb-gpio.o
obj-$(CONFIG_EXTCON_USBC_CROS_EC)
+=
extcon-usbc-cros-ec.o
drivers/extcon/extcon-usbc-cros-ec.c
0 → 100644
View file @
15dec834
/**
* drivers/extcon/extcon-usbc-cros-ec - ChromeOS Embedded Controller extcon
*
* Copyright (C) 2017 Google, Inc
* Author: Benson Leung <bleung@chromium.org>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/
#include <linux/extcon.h>
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sched.h>
struct
cros_ec_extcon_info
{
struct
device
*
dev
;
struct
extcon_dev
*
edev
;
int
port_id
;
struct
cros_ec_device
*
ec
;
struct
notifier_block
notifier
;
bool
dp
;
/* DisplayPort enabled */
bool
mux
;
/* SuperSpeed (usb3) enabled */
unsigned
int
power_type
;
};
static
const
unsigned
int
usb_type_c_cable
[]
=
{
EXTCON_DISP_DP
,
EXTCON_NONE
,
};
/**
* cros_ec_pd_command() - Send a command to the EC.
* @info: pointer to struct cros_ec_extcon_info
* @command: EC command
* @version: EC command version
* @outdata: EC command output data
* @outsize: Size of outdata
* @indata: EC command input data
* @insize: Size of indata
*
* Return: 0 on success, <0 on failure.
*/
static
int
cros_ec_pd_command
(
struct
cros_ec_extcon_info
*
info
,
unsigned
int
command
,
unsigned
int
version
,
void
*
outdata
,
unsigned
int
outsize
,
void
*
indata
,
unsigned
int
insize
)
{
struct
cros_ec_command
*
msg
;
int
ret
;
msg
=
kzalloc
(
sizeof
(
*
msg
)
+
max
(
outsize
,
insize
),
GFP_KERNEL
);
msg
->
version
=
version
;
msg
->
command
=
command
;
msg
->
outsize
=
outsize
;
msg
->
insize
=
insize
;
if
(
outsize
)
memcpy
(
msg
->
data
,
outdata
,
outsize
);
ret
=
cros_ec_cmd_xfer_status
(
info
->
ec
,
msg
);
if
(
ret
>=
0
&&
insize
)
memcpy
(
indata
,
msg
->
data
,
insize
);
kfree
(
msg
);
return
ret
;
}
/**
* cros_ec_usb_get_power_type() - Get power type info about PD device attached
* to given port.
* @info: pointer to struct cros_ec_extcon_info
*
* Return: power type on success, <0 on failure.
*/
static
int
cros_ec_usb_get_power_type
(
struct
cros_ec_extcon_info
*
info
)
{
struct
ec_params_usb_pd_power_info
req
;
struct
ec_response_usb_pd_power_info
resp
;
int
ret
;
req
.
port
=
info
->
port_id
;
ret
=
cros_ec_pd_command
(
info
,
EC_CMD_USB_PD_POWER_INFO
,
0
,
&
req
,
sizeof
(
req
),
&
resp
,
sizeof
(
resp
));
if
(
ret
<
0
)
return
ret
;
return
resp
.
type
;
}
/**
* cros_ec_usb_get_pd_mux_state() - Get PD mux state for given port.
* @info: pointer to struct cros_ec_extcon_info
*
* Return: PD mux state on success, <0 on failure.
*/
static
int
cros_ec_usb_get_pd_mux_state
(
struct
cros_ec_extcon_info
*
info
)
{
struct
ec_params_usb_pd_mux_info
req
;
struct
ec_response_usb_pd_mux_info
resp
;
int
ret
;
req
.
port
=
info
->
port_id
;
ret
=
cros_ec_pd_command
(
info
,
EC_CMD_USB_PD_MUX_INFO
,
0
,
&
req
,
sizeof
(
req
),
&
resp
,
sizeof
(
resp
));
if
(
ret
<
0
)
return
ret
;
return
resp
.
flags
;
}
/**
* cros_ec_usb_get_role() - Get role info about possible PD device attached to a
* given port.
* @info: pointer to struct cros_ec_extcon_info
* @polarity: pointer to cable polarity (return value)
*
* Return: role info on success, -ENOTCONN if no cable is connected, <0 on
* failure.
*/
static
int
cros_ec_usb_get_role
(
struct
cros_ec_extcon_info
*
info
,
bool
*
polarity
)
{
struct
ec_params_usb_pd_control
pd_control
;
struct
ec_response_usb_pd_control_v1
resp
;
int
ret
;
pd_control
.
port
=
info
->
port_id
;
pd_control
.
role
=
USB_PD_CTRL_ROLE_NO_CHANGE
;
pd_control
.
mux
=
USB_PD_CTRL_MUX_NO_CHANGE
;
ret
=
cros_ec_pd_command
(
info
,
EC_CMD_USB_PD_CONTROL
,
1
,
&
pd_control
,
sizeof
(
pd_control
),
&
resp
,
sizeof
(
resp
));
if
(
ret
<
0
)
return
ret
;
if
(
!
(
resp
.
enabled
&
PD_CTRL_RESP_ENABLED_CONNECTED
))
return
-
ENOTCONN
;
*
polarity
=
resp
.
polarity
;
return
resp
.
role
;
}
/**
* cros_ec_pd_get_num_ports() - Get number of EC charge ports.
* @info: pointer to struct cros_ec_extcon_info
*
* Return: number of ports on success, <0 on failure.
*/
static
int
cros_ec_pd_get_num_ports
(
struct
cros_ec_extcon_info
*
info
)
{
struct
ec_response_usb_pd_ports
resp
;
int
ret
;
ret
=
cros_ec_pd_command
(
info
,
EC_CMD_USB_PD_PORTS
,
0
,
NULL
,
0
,
&
resp
,
sizeof
(
resp
));
if
(
ret
<
0
)
return
ret
;
return
resp
.
num_ports
;
}
static
int
extcon_cros_ec_detect_cable
(
struct
cros_ec_extcon_info
*
info
,
bool
force
)
{
struct
device
*
dev
=
info
->
dev
;
int
role
,
power_type
;
bool
polarity
=
false
;
bool
dp
=
false
;
bool
mux
=
false
;
bool
hpd
=
false
;
power_type
=
cros_ec_usb_get_power_type
(
info
);
if
(
power_type
<
0
)
{
dev_err
(
dev
,
"failed getting power type err = %d
\n
"
,
power_type
);
return
power_type
;
}
role
=
cros_ec_usb_get_role
(
info
,
&
polarity
);
if
(
role
<
0
)
{
if
(
role
!=
-
ENOTCONN
)
{
dev_err
(
dev
,
"failed getting role err = %d
\n
"
,
role
);
return
role
;
}
}
else
{
int
pd_mux_state
;
pd_mux_state
=
cros_ec_usb_get_pd_mux_state
(
info
);
if
(
pd_mux_state
<
0
)
pd_mux_state
=
USB_PD_MUX_USB_ENABLED
;
dp
=
pd_mux_state
&
USB_PD_MUX_DP_ENABLED
;
mux
=
pd_mux_state
&
USB_PD_MUX_USB_ENABLED
;
hpd
=
pd_mux_state
&
USB_PD_MUX_HPD_IRQ
;
}
if
(
force
||
info
->
dp
!=
dp
||
info
->
mux
!=
mux
||
info
->
power_type
!=
power_type
)
{
info
->
dp
=
dp
;
info
->
mux
=
mux
;
info
->
power_type
=
power_type
;
extcon_set_state
(
info
->
edev
,
EXTCON_DISP_DP
,
dp
);
extcon_set_property
(
info
->
edev
,
EXTCON_DISP_DP
,
EXTCON_PROP_USB_TYPEC_POLARITY
,
(
union
extcon_property_value
)(
int
)
polarity
);
extcon_set_property
(
info
->
edev
,
EXTCON_DISP_DP
,
EXTCON_PROP_USB_SS
,
(
union
extcon_property_value
)(
int
)
mux
);
extcon_set_property
(
info
->
edev
,
EXTCON_DISP_DP
,
EXTCON_PROP_DISP_HPD
,
(
union
extcon_property_value
)(
int
)
hpd
);
extcon_sync
(
info
->
edev
,
EXTCON_DISP_DP
);
}
else
if
(
hpd
)
{
extcon_set_property
(
info
->
edev
,
EXTCON_DISP_DP
,
EXTCON_PROP_DISP_HPD
,
(
union
extcon_property_value
)(
int
)
hpd
);
extcon_sync
(
info
->
edev
,
EXTCON_DISP_DP
);
}
return
0
;
}
static
int
extcon_cros_ec_event
(
struct
notifier_block
*
nb
,
unsigned
long
queued_during_suspend
,
void
*
_notify
)
{
struct
cros_ec_extcon_info
*
info
;
struct
cros_ec_device
*
ec
;
u32
host_event
;
info
=
container_of
(
nb
,
struct
cros_ec_extcon_info
,
notifier
);
ec
=
info
->
ec
;
host_event
=
cros_ec_get_host_event
(
ec
);
if
(
host_event
&
(
EC_HOST_EVENT_MASK
(
EC_HOST_EVENT_PD_MCU
)
|
EC_HOST_EVENT_MASK
(
EC_HOST_EVENT_USB_MUX
)))
{
extcon_cros_ec_detect_cable
(
info
,
false
);
return
NOTIFY_OK
;
}
return
NOTIFY_DONE
;
}
static
int
extcon_cros_ec_probe
(
struct
platform_device
*
pdev
)
{
struct
cros_ec_extcon_info
*
info
;
struct
cros_ec_device
*
ec
=
dev_get_drvdata
(
pdev
->
dev
.
parent
);
struct
device
*
dev
=
&
pdev
->
dev
;
struct
device_node
*
np
=
dev
->
of_node
;
int
numports
,
ret
;
info
=
devm_kzalloc
(
dev
,
sizeof
(
*
info
),
GFP_KERNEL
);
if
(
!
info
)
return
-
ENOMEM
;
info
->
dev
=
dev
;
info
->
ec
=
ec
;
if
(
np
)
{
u32
port
;
ret
=
of_property_read_u32
(
np
,
"google,usb-port-id"
,
&
port
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"Missing google,usb-port-id property
\n
"
);
return
ret
;
}
info
->
port_id
=
port
;
}
else
{
info
->
port_id
=
pdev
->
id
;
}
numports
=
cros_ec_pd_get_num_ports
(
info
);
if
(
numports
<
0
)
{
dev_err
(
dev
,
"failed getting number of ports! ret = %d
\n
"
,
numports
);
return
numports
;
}
if
(
info
->
port_id
>=
numports
)
{
dev_err
(
dev
,
"This system only supports %d ports
\n
"
,
numports
);
return
-
ENODEV
;
}
info
->
edev
=
devm_extcon_dev_allocate
(
dev
,
usb_type_c_cable
);
if
(
IS_ERR
(
info
->
edev
))
{
dev_err
(
dev
,
"failed to allocate extcon device
\n
"
);
return
-
ENOMEM
;
}
ret
=
devm_extcon_dev_register
(
dev
,
info
->
edev
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"failed to register extcon device
\n
"
);
return
ret
;
}
extcon_set_property_capability
(
info
->
edev
,
EXTCON_DISP_DP
,
EXTCON_PROP_USB_TYPEC_POLARITY
);
extcon_set_property_capability
(
info
->
edev
,
EXTCON_DISP_DP
,
EXTCON_PROP_USB_SS
);
extcon_set_property_capability
(
info
->
edev
,
EXTCON_DISP_DP
,
EXTCON_PROP_DISP_HPD
);
platform_set_drvdata
(
pdev
,
info
);
/* Get PD events from the EC */
info
->
notifier
.
notifier_call
=
extcon_cros_ec_event
;
ret
=
blocking_notifier_chain_register
(
&
info
->
ec
->
event_notifier
,
&
info
->
notifier
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"failed to register notifier
\n
"
);
return
ret
;
}
/* Perform initial detection */
ret
=
extcon_cros_ec_detect_cable
(
info
,
true
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"failed to detect initial cable state
\n
"
);
goto
unregister_notifier
;
}
return
0
;
unregister_notifier:
blocking_notifier_chain_unregister
(
&
info
->
ec
->
event_notifier
,
&
info
->
notifier
);
return
ret
;
}
static
int
extcon_cros_ec_remove
(
struct
platform_device
*
pdev
)
{
struct
cros_ec_extcon_info
*
info
=
platform_get_drvdata
(
pdev
);
blocking_notifier_chain_unregister
(
&
info
->
ec
->
event_notifier
,
&
info
->
notifier
);
return
0
;
}
#ifdef CONFIG_PM_SLEEP
static
int
extcon_cros_ec_suspend
(
struct
device
*
dev
)
{
return
0
;
}
static
int
extcon_cros_ec_resume
(
struct
device
*
dev
)
{
int
ret
;
struct
cros_ec_extcon_info
*
info
=
dev_get_drvdata
(
dev
);
ret
=
extcon_cros_ec_detect_cable
(
info
,
true
);
if
(
ret
<
0
)
dev_err
(
dev
,
"failed to detect cable state on resume
\n
"
);
return
0
;
}
static
const
struct
dev_pm_ops
extcon_cros_ec_dev_pm_ops
=
{
SET_SYSTEM_SLEEP_PM_OPS
(
extcon_cros_ec_suspend
,
extcon_cros_ec_resume
)
};
#define DEV_PM_OPS (&extcon_cros_ec_dev_pm_ops)
#else
#define DEV_PM_OPS NULL
#endif
/* CONFIG_PM_SLEEP */
#ifdef CONFIG_OF
static
const
struct
of_device_id
extcon_cros_ec_of_match
[]
=
{
{
.
compatible
=
"google,extcon-usbc-cros-ec"
},
{
/* sentinel */
}
};
MODULE_DEVICE_TABLE
(
of
,
extcon_cros_ec_of_match
);
#endif
/* CONFIG_OF */
static
struct
platform_driver
extcon_cros_ec_driver
=
{
.
driver
=
{
.
name
=
"extcon-usbc-cros-ec"
,
.
of_match_table
=
of_match_ptr
(
extcon_cros_ec_of_match
),
.
pm
=
DEV_PM_OPS
,
},
.
remove
=
extcon_cros_ec_remove
,
.
probe
=
extcon_cros_ec_probe
,
};
module_platform_driver
(
extcon_cros_ec_driver
);
MODULE_DESCRIPTION
(
"ChromeOS Embedded Controller extcon driver"
);
MODULE_AUTHOR
(
"Benson Leung <bleung@chromium.org>"
);
MODULE_LICENSE
(
"GPL"
);
include/linux/mfd/cros_ec_commands.h
View file @
15dec834
...
...
@@ -285,6 +285,11 @@ enum host_event_code {
EC_HOST_EVENT_HANG_DETECT
=
20
,
/* Hang detect logic detected a hang and warm rebooted the AP */
EC_HOST_EVENT_HANG_REBOOT
=
21
,
/* PD MCU triggering host event */
EC_HOST_EVENT_PD_MCU
=
22
,
/* EC desires to change state of host-controlled USB mux */
EC_HOST_EVENT_USB_MUX
=
28
,
/*
* The high bit of the event mask is not used as a host event code. If
...
...
@@ -2905,6 +2910,76 @@ struct ec_params_usb_pd_control {
uint8_t
mux
;
}
__packed
;
#define PD_CTRL_RESP_ENABLED_COMMS (1 << 0)
/* Communication enabled */
#define PD_CTRL_RESP_ENABLED_CONNECTED (1 << 1)
/* Device connected */
#define PD_CTRL_RESP_ENABLED_PD_CAPABLE (1 << 2)
/* Partner is PD capable */
struct
ec_response_usb_pd_control_v1
{
uint8_t
enabled
;
uint8_t
role
;
uint8_t
polarity
;
char
state
[
32
];
}
__packed
;
#define EC_CMD_USB_PD_PORTS 0x102
struct
ec_response_usb_pd_ports
{
uint8_t
num_ports
;
}
__packed
;
#define EC_CMD_USB_PD_POWER_INFO 0x103
#define PD_POWER_CHARGING_PORT 0xff
struct
ec_params_usb_pd_power_info
{
uint8_t
port
;
}
__packed
;
enum
usb_chg_type
{
USB_CHG_TYPE_NONE
,
USB_CHG_TYPE_PD
,
USB_CHG_TYPE_C
,
USB_CHG_TYPE_PROPRIETARY
,
USB_CHG_TYPE_BC12_DCP
,
USB_CHG_TYPE_BC12_CDP
,
USB_CHG_TYPE_BC12_SDP
,
USB_CHG_TYPE_OTHER
,
USB_CHG_TYPE_VBUS
,
USB_CHG_TYPE_UNKNOWN
,
};
struct
usb_chg_measures
{
uint16_t
voltage_max
;
uint16_t
voltage_now
;
uint16_t
current_max
;
uint16_t
current_lim
;
}
__packed
;
struct
ec_response_usb_pd_power_info
{
uint8_t
role
;
uint8_t
type
;
uint8_t
dualrole
;
uint8_t
reserved1
;
struct
usb_chg_measures
meas
;
uint32_t
max_power
;
}
__packed
;
/* Get info about USB-C SS muxes */
#define EC_CMD_USB_PD_MUX_INFO 0x11a
struct
ec_params_usb_pd_mux_info
{
uint8_t
port
;
/* USB-C port number */
}
__packed
;
/* Flags representing mux state */
#define USB_PD_MUX_USB_ENABLED (1 << 0)
#define USB_PD_MUX_DP_ENABLED (1 << 1)
#define USB_PD_MUX_POLARITY_INVERTED (1 << 2)
#define USB_PD_MUX_HPD_IRQ (1 << 3)
struct
ec_response_usb_pd_mux_info
{
uint8_t
flags
;
/* USB_PD_MUX_*-encoded USB mux state */
}
__packed
;
/*****************************************************************************/
/*
* Passthru commands
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment