aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Thomson <Adam.Thomson.Opensource@diasemi.com>2018-04-23 10:11:00 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-04-25 08:20:20 -0400
commitf2a8aa053c1761232ce561e4fa725f02b8bd13fd (patch)
treefae6318a59c2cc64b73fc436a22c64f5b29bf6e2
parentcf45004195efea6b479a1d710d6fc21c2b19353e (diff)
typec: tcpm: Represent source supply through power_supply
This commit adds a power_supply class instance to represent a PD source's voltage and current properties. This provides an interface for reading these properties from user-space or other drivers. For PPS enabled Sources, this also provides write access to set the current and voltage and allows for swapping between standard PDO and PPS APDO. As this represents a superset of the information provided in the fusb302 driver, the power_supply instance in that code is removed as part of this change, so reverting the commit titled 'typec: tcpm: Represent source supply through power_supply class' Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/typec/Kconfig1
-rw-r--r--drivers/usb/typec/fusb302/Kconfig2
-rw-r--r--drivers/usb/typec/fusb302/fusb302.c63
-rw-r--r--drivers/usb/typec/tcpm.c251
4 files changed, 251 insertions, 66 deletions
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 030f88cb0c3f..2c8eab11a493 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -49,6 +49,7 @@ config TYPEC_TCPM
49 tristate "USB Type-C Port Controller Manager" 49 tristate "USB Type-C Port Controller Manager"
50 depends on USB 50 depends on USB
51 select USB_ROLE_SWITCH 51 select USB_ROLE_SWITCH
52 select POWER_SUPPLY
52 help 53 help
53 The Type-C Port Controller Manager provides a USB PD and USB Type-C 54 The Type-C Port Controller Manager provides a USB PD and USB Type-C
54 state machine for use with Type-C Port Controllers. 55 state machine for use with Type-C Port Controllers.
diff --git a/drivers/usb/typec/fusb302/Kconfig b/drivers/usb/typec/fusb302/Kconfig
index 48a4f2fcee03..fce099ff39fe 100644
--- a/drivers/usb/typec/fusb302/Kconfig
+++ b/drivers/usb/typec/fusb302/Kconfig
@@ -1,6 +1,6 @@
1config TYPEC_FUSB302 1config TYPEC_FUSB302
2 tristate "Fairchild FUSB302 Type-C chip driver" 2 tristate "Fairchild FUSB302 Type-C chip driver"
3 depends on I2C && POWER_SUPPLY 3 depends on I2C
4 help 4 help
5 The Fairchild FUSB302 Type-C chip driver that works with 5 The Fairchild FUSB302 Type-C chip driver that works with
6 Type-C Port Controller Manager to provide USB PD and USB 6 Type-C Port Controller Manager to provide USB PD and USB
diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c
index 664463de7098..eba6bb890b17 100644
--- a/drivers/usb/typec/fusb302/fusb302.c
+++ b/drivers/usb/typec/fusb302/fusb302.c
@@ -18,7 +18,6 @@
18#include <linux/of_device.h> 18#include <linux/of_device.h>
19#include <linux/of_gpio.h> 19#include <linux/of_gpio.h>
20#include <linux/pinctrl/consumer.h> 20#include <linux/pinctrl/consumer.h>
21#include <linux/power_supply.h>
22#include <linux/proc_fs.h> 21#include <linux/proc_fs.h>
23#include <linux/regulator/consumer.h> 22#include <linux/regulator/consumer.h>
24#include <linux/sched/clock.h> 23#include <linux/sched/clock.h>
@@ -99,11 +98,6 @@ struct fusb302_chip {
99 /* lock for sharing chip states */ 98 /* lock for sharing chip states */
100 struct mutex lock; 99 struct mutex lock;
101 100
102 /* psy + psy status */
103 struct power_supply *psy;
104 u32 current_limit;
105 u32 supply_voltage;
106
107 /* chip status */ 101 /* chip status */
108 enum toggling_mode toggling_mode; 102 enum toggling_mode toggling_mode;
109 enum src_current_status src_current_status; 103 enum src_current_status src_current_status;
@@ -862,13 +856,11 @@ static int tcpm_set_vbus(struct tcpc_dev *dev, bool on, bool charge)
862 chip->vbus_on = on; 856 chip->vbus_on = on;
863 fusb302_log(chip, "vbus := %s", on ? "On" : "Off"); 857 fusb302_log(chip, "vbus := %s", on ? "On" : "Off");
864 } 858 }
865 if (chip->charge_on == charge) { 859 if (chip->charge_on == charge)
866 fusb302_log(chip, "charge is already %s", 860 fusb302_log(chip, "charge is already %s",
867 charge ? "On" : "Off"); 861 charge ? "On" : "Off");
868 } else { 862 else
869 chip->charge_on = charge; 863 chip->charge_on = charge;
870 power_supply_changed(chip->psy);
871 }
872 864
873done: 865done:
874 mutex_unlock(&chip->lock); 866 mutex_unlock(&chip->lock);
@@ -884,11 +876,6 @@ static int tcpm_set_current_limit(struct tcpc_dev *dev, u32 max_ma, u32 mv)
884 fusb302_log(chip, "current limit: %d ma, %d mv (not implemented)", 876 fusb302_log(chip, "current limit: %d ma, %d mv (not implemented)",
885 max_ma, mv); 877 max_ma, mv);
886 878
887 chip->supply_voltage = mv;
888 chip->current_limit = max_ma;
889
890 power_supply_changed(chip->psy);
891
892 return 0; 879 return 0;
893} 880}
894 881
@@ -1682,43 +1669,6 @@ done:
1682 return IRQ_HANDLED; 1669 return IRQ_HANDLED;
1683} 1670}
1684 1671
1685static int fusb302_psy_get_property(struct power_supply *psy,
1686 enum power_supply_property psp,
1687 union power_supply_propval *val)
1688{
1689 struct fusb302_chip *chip = power_supply_get_drvdata(psy);
1690
1691 switch (psp) {
1692 case POWER_SUPPLY_PROP_ONLINE:
1693 val->intval = chip->charge_on;
1694 break;
1695 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
1696 val->intval = chip->supply_voltage * 1000; /* mV -> µV */
1697 break;
1698 case POWER_SUPPLY_PROP_CURRENT_MAX:
1699 val->intval = chip->current_limit * 1000; /* mA -> µA */
1700 break;
1701 default:
1702 return -ENODATA;
1703 }
1704
1705 return 0;
1706}
1707
1708static enum power_supply_property fusb302_psy_properties[] = {
1709 POWER_SUPPLY_PROP_ONLINE,
1710 POWER_SUPPLY_PROP_VOLTAGE_NOW,
1711 POWER_SUPPLY_PROP_CURRENT_MAX,
1712};
1713
1714static const struct power_supply_desc fusb302_psy_desc = {
1715 .name = "fusb302-typec-source",
1716 .type = POWER_SUPPLY_TYPE_USB_TYPE_C,
1717 .properties = fusb302_psy_properties,
1718 .num_properties = ARRAY_SIZE(fusb302_psy_properties),
1719 .get_property = fusb302_psy_get_property,
1720};
1721
1722static int init_gpio(struct fusb302_chip *chip) 1672static int init_gpio(struct fusb302_chip *chip)
1723{ 1673{
1724 struct device_node *node; 1674 struct device_node *node;
@@ -1781,7 +1731,6 @@ static int fusb302_probe(struct i2c_client *client,
1781 struct fusb302_chip *chip; 1731 struct fusb302_chip *chip;
1782 struct i2c_adapter *adapter; 1732 struct i2c_adapter *adapter;
1783 struct device *dev = &client->dev; 1733 struct device *dev = &client->dev;
1784 struct power_supply_config cfg = {};
1785 const char *name; 1734 const char *name;
1786 int ret = 0; 1735 int ret = 0;
1787 u32 v; 1736 u32 v;
@@ -1823,14 +1772,6 @@ static int fusb302_probe(struct i2c_client *client,
1823 return -EPROBE_DEFER; 1772 return -EPROBE_DEFER;
1824 } 1773 }
1825 1774
1826 cfg.drv_data = chip;
1827 chip->psy = devm_power_supply_register(dev, &fusb302_psy_desc, &cfg);
1828 if (IS_ERR(chip->psy)) {
1829 ret = PTR_ERR(chip->psy);
1830 dev_err(chip->dev, "Error registering power-supply: %d\n", ret);
1831 return ret;
1832 }
1833
1834 ret = fusb302_debugfs_init(chip); 1775 ret = fusb302_debugfs_init(chip);
1835 if (ret < 0) 1776 if (ret < 0)
1836 return ret; 1777 return ret;
diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
index b160da35605f..7547097f8751 100644
--- a/drivers/usb/typec/tcpm.c
+++ b/drivers/usb/typec/tcpm.c
@@ -12,6 +12,7 @@
12#include <linux/kernel.h> 12#include <linux/kernel.h>
13#include <linux/module.h> 13#include <linux/module.h>
14#include <linux/mutex.h> 14#include <linux/mutex.h>
15#include <linux/power_supply.h>
15#include <linux/proc_fs.h> 16#include <linux/proc_fs.h>
16#include <linux/sched/clock.h> 17#include <linux/sched/clock.h>
17#include <linux/seq_file.h> 18#include <linux/seq_file.h>
@@ -276,6 +277,11 @@ struct tcpm_port {
276 u32 current_limit; 277 u32 current_limit;
277 u32 supply_voltage; 278 u32 supply_voltage;
278 279
280 /* Used to export TA voltage and current */
281 struct power_supply *psy;
282 struct power_supply_desc psy_desc;
283 enum power_supply_usb_type usb_type;
284
279 u32 bist_request; 285 u32 bist_request;
280 286
281 /* PD state for Vendor Defined Messages */ 287 /* PD state for Vendor Defined Messages */
@@ -1895,6 +1901,7 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
1895 int ret = -EINVAL; 1901 int ret = -EINVAL;
1896 1902
1897 port->pps_data.supported = false; 1903 port->pps_data.supported = false;
1904 port->usb_type = POWER_SUPPLY_USB_TYPE_PD;
1898 1905
1899 /* 1906 /*
1900 * Select the source PDO providing the most power which has a 1907 * Select the source PDO providing the most power which has a
@@ -1915,8 +1922,11 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
1915 min_src_mv = pdo_min_voltage(pdo); 1922 min_src_mv = pdo_min_voltage(pdo);
1916 break; 1923 break;
1917 case PDO_TYPE_APDO: 1924 case PDO_TYPE_APDO:
1918 if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) 1925 if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) {
1919 port->pps_data.supported = true; 1926 port->pps_data.supported = true;
1927 port->usb_type =
1928 POWER_SUPPLY_USB_TYPE_PD_PPS;
1929 }
1920 continue; 1930 continue;
1921 default: 1931 default:
1922 tcpm_log(port, "Invalid source PDO type, ignoring"); 1932 tcpm_log(port, "Invalid source PDO type, ignoring");
@@ -2472,6 +2482,9 @@ static void tcpm_reset_port(struct tcpm_port *port)
2472 port->try_snk_count = 0; 2482 port->try_snk_count = 0;
2473 port->supply_voltage = 0; 2483 port->supply_voltage = 0;
2474 port->current_limit = 0; 2484 port->current_limit = 0;
2485 port->usb_type = POWER_SUPPLY_USB_TYPE_C;
2486
2487 power_supply_changed(port->psy);
2475} 2488}
2476 2489
2477static void tcpm_detach(struct tcpm_port *port) 2490static void tcpm_detach(struct tcpm_port *port)
@@ -2999,6 +3012,8 @@ static void run_state_machine(struct tcpm_port *port)
2999 tcpm_check_send_discover(port); 3012 tcpm_check_send_discover(port);
3000 tcpm_pps_complete(port, port->pps_status); 3013 tcpm_pps_complete(port, port->pps_status);
3001 3014
3015 power_supply_changed(port->psy);
3016
3002 break; 3017 break;
3003 3018
3004 /* Accessory states */ 3019 /* Accessory states */
@@ -3849,7 +3864,7 @@ static int tcpm_try_role(const struct typec_capability *cap, int role)
3849 return ret; 3864 return ret;
3850} 3865}
3851 3866
3852static int __maybe_unused tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr) 3867static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr)
3853{ 3868{
3854 unsigned int target_mw; 3869 unsigned int target_mw;
3855 int ret; 3870 int ret;
@@ -3901,7 +3916,7 @@ swap_unlock:
3901 return ret; 3916 return ret;
3902} 3917}
3903 3918
3904static int __maybe_unused tcpm_pps_set_out_volt(struct tcpm_port *port, u16 out_volt) 3919static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 out_volt)
3905{ 3920{
3906 unsigned int target_mw; 3921 unsigned int target_mw;
3907 int ret; 3922 int ret;
@@ -3954,7 +3969,7 @@ swap_unlock:
3954 return ret; 3969 return ret;
3955} 3970}
3956 3971
3957static int __maybe_unused tcpm_pps_activate(struct tcpm_port *port, bool activate) 3972static int tcpm_pps_activate(struct tcpm_port *port, bool activate)
3958{ 3973{
3959 int ret = 0; 3974 int ret = 0;
3960 3975
@@ -4159,6 +4174,230 @@ int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
4159} 4174}
4160EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities); 4175EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities);
4161 4176
4177/* Power Supply access to expose source power information */
4178enum tcpm_psy_online_states {
4179 TCPM_PSY_OFFLINE = 0,
4180 TCPM_PSY_FIXED_ONLINE,
4181 TCPM_PSY_PROG_ONLINE,
4182};
4183
4184static enum power_supply_property tcpm_psy_props[] = {
4185 POWER_SUPPLY_PROP_USB_TYPE,
4186 POWER_SUPPLY_PROP_ONLINE,
4187 POWER_SUPPLY_PROP_VOLTAGE_MIN,
4188 POWER_SUPPLY_PROP_VOLTAGE_MAX,
4189 POWER_SUPPLY_PROP_VOLTAGE_NOW,
4190 POWER_SUPPLY_PROP_CURRENT_MAX,
4191 POWER_SUPPLY_PROP_CURRENT_NOW,
4192};
4193
4194static int tcpm_psy_get_online(struct tcpm_port *port,
4195 union power_supply_propval *val)
4196{
4197 if (port->vbus_charge) {
4198 if (port->pps_data.active)
4199 val->intval = TCPM_PSY_PROG_ONLINE;
4200 else
4201 val->intval = TCPM_PSY_FIXED_ONLINE;
4202 } else {
4203 val->intval = TCPM_PSY_OFFLINE;
4204 }
4205
4206 return 0;
4207}
4208
4209static int tcpm_psy_get_voltage_min(struct tcpm_port *port,
4210 union power_supply_propval *val)
4211{
4212 if (port->pps_data.active)
4213 val->intval = port->pps_data.min_volt * 1000;
4214 else
4215 val->intval = port->supply_voltage * 1000;
4216
4217 return 0;
4218}
4219
4220static int tcpm_psy_get_voltage_max(struct tcpm_port *port,
4221 union power_supply_propval *val)
4222{
4223 if (port->pps_data.active)
4224 val->intval = port->pps_data.max_volt * 1000;
4225 else
4226 val->intval = port->supply_voltage * 1000;
4227
4228 return 0;
4229}
4230
4231static int tcpm_psy_get_voltage_now(struct tcpm_port *port,
4232 union power_supply_propval *val)
4233{
4234 val->intval = port->supply_voltage * 1000;
4235
4236 return 0;
4237}
4238
4239static int tcpm_psy_get_current_max(struct tcpm_port *port,
4240 union power_supply_propval *val)
4241{
4242 if (port->pps_data.active)
4243 val->intval = port->pps_data.max_curr * 1000;
4244 else
4245 val->intval = port->current_limit * 1000;
4246
4247 return 0;
4248}
4249
4250static int tcpm_psy_get_current_now(struct tcpm_port *port,
4251 union power_supply_propval *val)
4252{
4253 val->intval = port->current_limit * 1000;
4254
4255 return 0;
4256}
4257
4258static int tcpm_psy_get_prop(struct power_supply *psy,
4259 enum power_supply_property psp,
4260 union power_supply_propval *val)
4261{
4262 struct tcpm_port *port = power_supply_get_drvdata(psy);
4263 int ret = 0;
4264
4265 switch (psp) {
4266 case POWER_SUPPLY_PROP_USB_TYPE:
4267 val->intval = port->usb_type;
4268 break;
4269 case POWER_SUPPLY_PROP_ONLINE:
4270 ret = tcpm_psy_get_online(port, val);
4271 break;
4272 case POWER_SUPPLY_PROP_VOLTAGE_MIN:
4273 ret = tcpm_psy_get_voltage_min(port, val);
4274 break;
4275 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
4276 ret = tcpm_psy_get_voltage_max(port, val);
4277 break;
4278 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
4279 ret = tcpm_psy_get_voltage_now(port, val);
4280 break;
4281 case POWER_SUPPLY_PROP_CURRENT_MAX:
4282 ret = tcpm_psy_get_current_max(port, val);
4283 break;
4284 case POWER_SUPPLY_PROP_CURRENT_NOW:
4285 ret = tcpm_psy_get_current_now(port, val);
4286 break;
4287 default:
4288 ret = -EINVAL;
4289 break;
4290 }
4291
4292 return ret;
4293}
4294
4295static int tcpm_psy_set_online(struct tcpm_port *port,
4296 const union power_supply_propval *val)
4297{
4298 int ret;
4299
4300 switch (val->intval) {
4301 case TCPM_PSY_FIXED_ONLINE:
4302 ret = tcpm_pps_activate(port, false);
4303 break;
4304 case TCPM_PSY_PROG_ONLINE:
4305 ret = tcpm_pps_activate(port, true);
4306 break;
4307 default:
4308 ret = -EINVAL;
4309 break;
4310 }
4311
4312 return ret;
4313}
4314
4315static int tcpm_psy_set_prop(struct power_supply *psy,
4316 enum power_supply_property psp,
4317 const union power_supply_propval *val)
4318{
4319 struct tcpm_port *port = power_supply_get_drvdata(psy);
4320 int ret;
4321
4322 switch (psp) {
4323 case POWER_SUPPLY_PROP_ONLINE:
4324 ret = tcpm_psy_set_online(port, val);
4325 break;
4326 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
4327 if (val->intval < port->pps_data.min_volt * 1000 ||
4328 val->intval > port->pps_data.max_volt * 1000)
4329 ret = -EINVAL;
4330 else
4331 ret = tcpm_pps_set_out_volt(port, val->intval / 1000);
4332 break;
4333 case POWER_SUPPLY_PROP_CURRENT_NOW:
4334 if (val->intval > port->pps_data.max_curr * 1000)
4335 ret = -EINVAL;
4336 else
4337 ret = tcpm_pps_set_op_curr(port, val->intval / 1000);
4338 break;
4339 default:
4340 ret = -EINVAL;
4341 break;
4342 }
4343
4344 return ret;
4345}
4346
4347static int tcpm_psy_prop_writeable(struct power_supply *psy,
4348 enum power_supply_property psp)
4349{
4350 switch (psp) {
4351 case POWER_SUPPLY_PROP_ONLINE:
4352 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
4353 case POWER_SUPPLY_PROP_CURRENT_NOW:
4354 return 1;
4355 default:
4356 return 0;
4357 }
4358}
4359
4360static enum power_supply_usb_type tcpm_psy_usb_types[] = {
4361 POWER_SUPPLY_USB_TYPE_C,
4362 POWER_SUPPLY_USB_TYPE_PD,
4363 POWER_SUPPLY_USB_TYPE_PD_PPS,
4364};
4365
4366static const char *tcpm_psy_name_prefix = "tcpm-source-psy-";
4367
4368static int devm_tcpm_psy_register(struct tcpm_port *port)
4369{
4370 struct power_supply_config psy_cfg = {};
4371 const char *port_dev_name = dev_name(port->dev);
4372 size_t psy_name_len = strlen(tcpm_psy_name_prefix) +
4373 strlen(port_dev_name) + 1;
4374 char *psy_name;
4375
4376 psy_cfg.drv_data = port;
4377 psy_name = devm_kzalloc(port->dev, psy_name_len, GFP_KERNEL);
4378 if (!psy_name)
4379 return -ENOMEM;
4380
4381 snprintf(psy_name, psy_name_len, "%s%s", tcpm_psy_name_prefix,
4382 port_dev_name);
4383 port->psy_desc.name = psy_name;
4384 port->psy_desc.type = POWER_SUPPLY_TYPE_USB,
4385 port->psy_desc.usb_types = tcpm_psy_usb_types;
4386 port->psy_desc.num_usb_types = ARRAY_SIZE(tcpm_psy_usb_types);
4387 port->psy_desc.properties = tcpm_psy_props,
4388 port->psy_desc.num_properties = ARRAY_SIZE(tcpm_psy_props),
4389 port->psy_desc.get_property = tcpm_psy_get_prop,
4390 port->psy_desc.set_property = tcpm_psy_set_prop,
4391 port->psy_desc.property_is_writeable = tcpm_psy_prop_writeable,
4392
4393 port->usb_type = POWER_SUPPLY_USB_TYPE_C;
4394
4395 port->psy = devm_power_supply_register(port->dev, &port->psy_desc,
4396 &psy_cfg);
4397
4398 return PTR_ERR_OR_ZERO(port->psy);
4399}
4400
4162struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) 4401struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
4163{ 4402{
4164 struct tcpm_port *port; 4403 struct tcpm_port *port;
@@ -4234,6 +4473,10 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
4234 goto out_destroy_wq; 4473 goto out_destroy_wq;
4235 } 4474 }
4236 4475
4476 err = devm_tcpm_psy_register(port);
4477 if (err)
4478 goto out_destroy_wq;
4479
4237 port->typec_port = typec_register_port(port->dev, &port->typec_caps); 4480 port->typec_port = typec_register_port(port->dev, &port->typec_caps);
4238 if (IS_ERR(port->typec_port)) { 4481 if (IS_ERR(port->typec_port)) {
4239 err = PTR_ERR(port->typec_port); 4482 err = PTR_ERR(port->typec_port);