Loading drivers/soc/qcom/fsa4480-i2c.c +194 −14 Original line number Diff line number Diff line Loading @@ -4,12 +4,14 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/usb/typec.h> #include <linux/usb/ucsi_glink.h> #include <linux/soc/qcom/fsa4480-i2c.h> #include <linux/iio/consumer.h> #define FSA4480_I2C_NAME "fsa4480-driver" Loading @@ -30,11 +32,14 @@ struct fsa4480_priv { struct regmap *regmap; struct device *dev; struct notifier_block ucsi_nb; struct power_supply *usb_psy; struct notifier_block nb; struct iio_channel *iio_ch; atomic_t usbc_mode; struct work_struct usbc_analog_work; struct blocking_notifier_head fsa4480_notifier; struct mutex notification_lock; u32 use_powersupply; }; struct fsa4480_reg_val { Loading Loading @@ -76,11 +81,28 @@ static void fsa4480_usbc_update_settings(struct fsa4480_priv *fsa_priv, regmap_write(fsa_priv->regmap, FSA4480_SWITCH_SETTINGS, switch_enable); } static int fsa4480_usbc_event_changed(struct notifier_block *nb, static int fsa4480_usbc_event_changed_psupply(struct fsa4480_priv *fsa_priv, unsigned long evt, void *ptr) { struct device *dev = NULL; if (!fsa_priv) return -EINVAL; dev = fsa_priv->dev; if (!dev) return -EINVAL; dev_dbg(dev, "%s: queueing usbc_analog_work\n", __func__); pm_stay_awake(fsa_priv->dev); queue_work(system_freezable_wq, &fsa_priv->usbc_analog_work); return 0; } static int fsa4480_usbc_event_changed_ucsi(struct fsa4480_priv *fsa_priv, unsigned long evt, void *ptr) { struct fsa4480_priv *fsa_priv = container_of(nb, struct fsa4480_priv, ucsi_nb); struct device *dev; enum typec_accessory acc = ((struct ucsi_glink_constat_info *)ptr)->acc; Loading Loading @@ -114,7 +136,85 @@ static int fsa4480_usbc_event_changed(struct notifier_block *nb, return 0; } static int fsa4480_usbc_analog_setup_switches(struct fsa4480_priv *fsa_priv) static int fsa4480_usbc_event_changed(struct notifier_block *nb_ptr, unsigned long evt, void *ptr) { struct fsa4480_priv *fsa_priv = container_of(nb_ptr, struct fsa4480_priv, nb); struct device *dev; if (!fsa_priv) return -EINVAL; dev = fsa_priv->dev; if (!dev) return -EINVAL; if (fsa_priv->use_powersupply) return fsa4480_usbc_event_changed_psupply(fsa_priv, evt, ptr); else return fsa4480_usbc_event_changed_ucsi(fsa_priv, evt, ptr); } static int fsa4480_usbc_analog_setup_switches_psupply( struct fsa4480_priv *fsa_priv) { int rc = 0; union power_supply_propval mode; struct device *dev; if (!fsa_priv) return -EINVAL; dev = fsa_priv->dev; if (!dev) return -EINVAL; rc = iio_read_channel_processed(fsa_priv->iio_ch, &mode.intval); mutex_lock(&fsa_priv->notification_lock); /* get latest mode again within locked context */ if (rc < 0) { dev_err(dev, "%s: Unable to read USB TYPEC_MODE: %d\n", __func__, rc); goto done; } dev_dbg(dev, "%s: setting GPIOs active = %d rcvd intval 0x%X\n", __func__, mode.intval != TYPEC_ACCESSORY_NONE, mode.intval); if (atomic_read(&(fsa_priv->usbc_mode)) == mode.intval) goto done; /* filter notifications received before */ atomic_set(&(fsa_priv->usbc_mode), mode.intval); switch (mode.intval) { /* add all modes FSA should notify for in here */ case TYPEC_ACCESSORY_AUDIO: /* activate switches */ fsa4480_usbc_update_settings(fsa_priv, 0x00, 0x9F); /* notify call chain on event */ blocking_notifier_call_chain(&fsa_priv->fsa4480_notifier, mode.intval, NULL); break; case TYPEC_ACCESSORY_NONE: /* notify call chain on event */ blocking_notifier_call_chain(&fsa_priv->fsa4480_notifier, TYPEC_ACCESSORY_NONE, NULL); /* deactivate switches */ fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); break; default: /* ignore other usb connection modes */ break; } done: mutex_unlock(&fsa_priv->notification_lock); return rc; } static int fsa4480_usbc_analog_setup_switches_ucsi( struct fsa4480_priv *fsa_priv) { int rc = 0; int mode; Loading Loading @@ -160,6 +260,14 @@ static int fsa4480_usbc_analog_setup_switches(struct fsa4480_priv *fsa_priv) return rc; } static int fsa4480_usbc_analog_setup_switches(struct fsa4480_priv *fsa_priv) { if (fsa_priv->use_powersupply) return fsa4480_usbc_analog_setup_switches_psupply(fsa_priv); else return fsa4480_usbc_analog_setup_switches_ucsi(fsa_priv); } /* * fsa4480_reg_notifier - register notifier block with fsa driver * Loading Loading @@ -210,8 +318,11 @@ EXPORT_SYMBOL(fsa4480_reg_notifier); int fsa4480_unreg_notifier(struct notifier_block *nb, struct device_node *node) { int rc = 0; struct i2c_client *client = of_find_i2c_device_by_node(node); struct fsa4480_priv *fsa_priv; struct device *dev; union power_supply_propval mode; if (!client) return -EINVAL; Loading @@ -219,11 +330,35 @@ int fsa4480_unreg_notifier(struct notifier_block *nb, fsa_priv = (struct fsa4480_priv *)i2c_get_clientdata(client); if (!fsa_priv) return -EINVAL; if (fsa_priv->use_powersupply) { dev = fsa_priv->dev; if (!dev) return -EINVAL; mutex_lock(&fsa_priv->notification_lock); /* get latest mode within locked context */ rc = iio_read_channel_processed(fsa_priv->iio_ch, &mode.intval); if (rc) { dev_dbg(dev, "%s: Unable to read USB TYPEC_MODE: %d\n", __func__, rc); mutex_unlock(&fsa_priv->notification_lock); return rc; } /* Do not reset switch settings for usb digital hs */ if (mode.intval == TYPEC_ACCESSORY_AUDIO) fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); rc = blocking_notifier_chain_unregister (&fsa_priv->fsa4480_notifier, nb); mutex_unlock(&fsa_priv->notification_lock); } else { fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); return blocking_notifier_chain_unregister rc = blocking_notifier_chain_unregister (&fsa_priv->fsa4480_notifier, nb); } return rc; } EXPORT_SYMBOL(fsa4480_unreg_notifier); static int fsa4480_validate_display_port_settings(struct fsa4480_priv *fsa_priv) Loading Loading @@ -317,6 +452,7 @@ static int fsa4480_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct fsa4480_priv *fsa_priv; u32 use_powersupply = 0; int rc = 0; fsa_priv = devm_kzalloc(&i2c->dev, sizeof(*fsa_priv), Loading @@ -324,6 +460,7 @@ static int fsa4480_probe(struct i2c_client *i2c, if (!fsa_priv) return -ENOMEM; memset(fsa_priv, 0, sizeof(struct fsa4480_priv)); fsa_priv->dev = &i2c->dev; fsa_priv->regmap = devm_regmap_init_i2c(i2c, &fsa4480_regmap_config); Loading @@ -340,15 +477,50 @@ static int fsa4480_probe(struct i2c_client *i2c, fsa4480_update_reg_defaults(fsa_priv->regmap); fsa_priv->ucsi_nb.notifier_call = fsa4480_usbc_event_changed; fsa_priv->ucsi_nb.priority = 0; rc = register_ucsi_glink_notifier(&fsa_priv->ucsi_nb); fsa_priv->nb.notifier_call = fsa4480_usbc_event_changed; fsa_priv->nb.priority = 0; rc = of_property_read_u32(fsa_priv->dev->of_node, "qcom,use-power-supply", &use_powersupply); if (rc || use_powersupply == 0) { dev_dbg(fsa_priv->dev, "%s: Looking up %s property failed or disabled\n", __func__, "qcom,use-power-supply"); fsa_priv->use_powersupply = 0; rc = register_ucsi_glink_notifier(&fsa_priv->nb); if (rc) { dev_err(fsa_priv->dev, "%s: ucsi glink notifier registration failed: %d\n", dev_err(fsa_priv->dev, "%s: ucsi glink notifier registration failed: %d\n", __func__, rc); goto err_data; } } else { fsa_priv->use_powersupply = 1; fsa_priv->usb_psy = power_supply_get_by_name("usb"); if (!fsa_priv->usb_psy) { rc = -EPROBE_DEFER; dev_dbg(fsa_priv->dev, "%s: could not get USB psy info: %d\n", __func__, rc); goto err_data; } fsa_priv->iio_ch = iio_channel_get(fsa_priv->dev, "typec_mode"); if (!fsa_priv->iio_ch) { dev_err(fsa_priv->dev, "%s: iio_channel_get failed for typec_mode\n", __func__); goto err_supply; } rc = power_supply_reg_notifier(&fsa_priv->nb); if (rc) { dev_err(fsa_priv->dev, "%s: power supply reg failed: %d\n", __func__, rc); goto err_supply; } } mutex_init(&fsa_priv->notification_lock); i2c_set_clientdata(i2c, fsa_priv); Loading @@ -362,6 +534,8 @@ static int fsa4480_probe(struct i2c_client *i2c, return 0; err_supply: power_supply_put(fsa_priv->usb_psy); err_data: devm_kfree(&i2c->dev, fsa_priv); return rc; Loading @@ -375,7 +549,13 @@ static int fsa4480_remove(struct i2c_client *i2c) if (!fsa_priv) return -EINVAL; unregister_ucsi_glink_notifier(&fsa_priv->ucsi_nb); if (fsa_priv->use_powersupply) { /* deregister from PMI */ power_supply_unreg_notifier(&fsa_priv->nb); power_supply_put(fsa_priv->usb_psy); } else { unregister_ucsi_glink_notifier(&fsa_priv->nb); } fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); cancel_work_sync(&fsa_priv->usbc_analog_work); pm_relax(fsa_priv->dev); Loading Loading
drivers/soc/qcom/fsa4480-i2c.c +194 −14 Original line number Diff line number Diff line Loading @@ -4,12 +4,14 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/usb/typec.h> #include <linux/usb/ucsi_glink.h> #include <linux/soc/qcom/fsa4480-i2c.h> #include <linux/iio/consumer.h> #define FSA4480_I2C_NAME "fsa4480-driver" Loading @@ -30,11 +32,14 @@ struct fsa4480_priv { struct regmap *regmap; struct device *dev; struct notifier_block ucsi_nb; struct power_supply *usb_psy; struct notifier_block nb; struct iio_channel *iio_ch; atomic_t usbc_mode; struct work_struct usbc_analog_work; struct blocking_notifier_head fsa4480_notifier; struct mutex notification_lock; u32 use_powersupply; }; struct fsa4480_reg_val { Loading Loading @@ -76,11 +81,28 @@ static void fsa4480_usbc_update_settings(struct fsa4480_priv *fsa_priv, regmap_write(fsa_priv->regmap, FSA4480_SWITCH_SETTINGS, switch_enable); } static int fsa4480_usbc_event_changed(struct notifier_block *nb, static int fsa4480_usbc_event_changed_psupply(struct fsa4480_priv *fsa_priv, unsigned long evt, void *ptr) { struct device *dev = NULL; if (!fsa_priv) return -EINVAL; dev = fsa_priv->dev; if (!dev) return -EINVAL; dev_dbg(dev, "%s: queueing usbc_analog_work\n", __func__); pm_stay_awake(fsa_priv->dev); queue_work(system_freezable_wq, &fsa_priv->usbc_analog_work); return 0; } static int fsa4480_usbc_event_changed_ucsi(struct fsa4480_priv *fsa_priv, unsigned long evt, void *ptr) { struct fsa4480_priv *fsa_priv = container_of(nb, struct fsa4480_priv, ucsi_nb); struct device *dev; enum typec_accessory acc = ((struct ucsi_glink_constat_info *)ptr)->acc; Loading Loading @@ -114,7 +136,85 @@ static int fsa4480_usbc_event_changed(struct notifier_block *nb, return 0; } static int fsa4480_usbc_analog_setup_switches(struct fsa4480_priv *fsa_priv) static int fsa4480_usbc_event_changed(struct notifier_block *nb_ptr, unsigned long evt, void *ptr) { struct fsa4480_priv *fsa_priv = container_of(nb_ptr, struct fsa4480_priv, nb); struct device *dev; if (!fsa_priv) return -EINVAL; dev = fsa_priv->dev; if (!dev) return -EINVAL; if (fsa_priv->use_powersupply) return fsa4480_usbc_event_changed_psupply(fsa_priv, evt, ptr); else return fsa4480_usbc_event_changed_ucsi(fsa_priv, evt, ptr); } static int fsa4480_usbc_analog_setup_switches_psupply( struct fsa4480_priv *fsa_priv) { int rc = 0; union power_supply_propval mode; struct device *dev; if (!fsa_priv) return -EINVAL; dev = fsa_priv->dev; if (!dev) return -EINVAL; rc = iio_read_channel_processed(fsa_priv->iio_ch, &mode.intval); mutex_lock(&fsa_priv->notification_lock); /* get latest mode again within locked context */ if (rc < 0) { dev_err(dev, "%s: Unable to read USB TYPEC_MODE: %d\n", __func__, rc); goto done; } dev_dbg(dev, "%s: setting GPIOs active = %d rcvd intval 0x%X\n", __func__, mode.intval != TYPEC_ACCESSORY_NONE, mode.intval); if (atomic_read(&(fsa_priv->usbc_mode)) == mode.intval) goto done; /* filter notifications received before */ atomic_set(&(fsa_priv->usbc_mode), mode.intval); switch (mode.intval) { /* add all modes FSA should notify for in here */ case TYPEC_ACCESSORY_AUDIO: /* activate switches */ fsa4480_usbc_update_settings(fsa_priv, 0x00, 0x9F); /* notify call chain on event */ blocking_notifier_call_chain(&fsa_priv->fsa4480_notifier, mode.intval, NULL); break; case TYPEC_ACCESSORY_NONE: /* notify call chain on event */ blocking_notifier_call_chain(&fsa_priv->fsa4480_notifier, TYPEC_ACCESSORY_NONE, NULL); /* deactivate switches */ fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); break; default: /* ignore other usb connection modes */ break; } done: mutex_unlock(&fsa_priv->notification_lock); return rc; } static int fsa4480_usbc_analog_setup_switches_ucsi( struct fsa4480_priv *fsa_priv) { int rc = 0; int mode; Loading Loading @@ -160,6 +260,14 @@ static int fsa4480_usbc_analog_setup_switches(struct fsa4480_priv *fsa_priv) return rc; } static int fsa4480_usbc_analog_setup_switches(struct fsa4480_priv *fsa_priv) { if (fsa_priv->use_powersupply) return fsa4480_usbc_analog_setup_switches_psupply(fsa_priv); else return fsa4480_usbc_analog_setup_switches_ucsi(fsa_priv); } /* * fsa4480_reg_notifier - register notifier block with fsa driver * Loading Loading @@ -210,8 +318,11 @@ EXPORT_SYMBOL(fsa4480_reg_notifier); int fsa4480_unreg_notifier(struct notifier_block *nb, struct device_node *node) { int rc = 0; struct i2c_client *client = of_find_i2c_device_by_node(node); struct fsa4480_priv *fsa_priv; struct device *dev; union power_supply_propval mode; if (!client) return -EINVAL; Loading @@ -219,11 +330,35 @@ int fsa4480_unreg_notifier(struct notifier_block *nb, fsa_priv = (struct fsa4480_priv *)i2c_get_clientdata(client); if (!fsa_priv) return -EINVAL; if (fsa_priv->use_powersupply) { dev = fsa_priv->dev; if (!dev) return -EINVAL; mutex_lock(&fsa_priv->notification_lock); /* get latest mode within locked context */ rc = iio_read_channel_processed(fsa_priv->iio_ch, &mode.intval); if (rc) { dev_dbg(dev, "%s: Unable to read USB TYPEC_MODE: %d\n", __func__, rc); mutex_unlock(&fsa_priv->notification_lock); return rc; } /* Do not reset switch settings for usb digital hs */ if (mode.intval == TYPEC_ACCESSORY_AUDIO) fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); rc = blocking_notifier_chain_unregister (&fsa_priv->fsa4480_notifier, nb); mutex_unlock(&fsa_priv->notification_lock); } else { fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); return blocking_notifier_chain_unregister rc = blocking_notifier_chain_unregister (&fsa_priv->fsa4480_notifier, nb); } return rc; } EXPORT_SYMBOL(fsa4480_unreg_notifier); static int fsa4480_validate_display_port_settings(struct fsa4480_priv *fsa_priv) Loading Loading @@ -317,6 +452,7 @@ static int fsa4480_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct fsa4480_priv *fsa_priv; u32 use_powersupply = 0; int rc = 0; fsa_priv = devm_kzalloc(&i2c->dev, sizeof(*fsa_priv), Loading @@ -324,6 +460,7 @@ static int fsa4480_probe(struct i2c_client *i2c, if (!fsa_priv) return -ENOMEM; memset(fsa_priv, 0, sizeof(struct fsa4480_priv)); fsa_priv->dev = &i2c->dev; fsa_priv->regmap = devm_regmap_init_i2c(i2c, &fsa4480_regmap_config); Loading @@ -340,15 +477,50 @@ static int fsa4480_probe(struct i2c_client *i2c, fsa4480_update_reg_defaults(fsa_priv->regmap); fsa_priv->ucsi_nb.notifier_call = fsa4480_usbc_event_changed; fsa_priv->ucsi_nb.priority = 0; rc = register_ucsi_glink_notifier(&fsa_priv->ucsi_nb); fsa_priv->nb.notifier_call = fsa4480_usbc_event_changed; fsa_priv->nb.priority = 0; rc = of_property_read_u32(fsa_priv->dev->of_node, "qcom,use-power-supply", &use_powersupply); if (rc || use_powersupply == 0) { dev_dbg(fsa_priv->dev, "%s: Looking up %s property failed or disabled\n", __func__, "qcom,use-power-supply"); fsa_priv->use_powersupply = 0; rc = register_ucsi_glink_notifier(&fsa_priv->nb); if (rc) { dev_err(fsa_priv->dev, "%s: ucsi glink notifier registration failed: %d\n", dev_err(fsa_priv->dev, "%s: ucsi glink notifier registration failed: %d\n", __func__, rc); goto err_data; } } else { fsa_priv->use_powersupply = 1; fsa_priv->usb_psy = power_supply_get_by_name("usb"); if (!fsa_priv->usb_psy) { rc = -EPROBE_DEFER; dev_dbg(fsa_priv->dev, "%s: could not get USB psy info: %d\n", __func__, rc); goto err_data; } fsa_priv->iio_ch = iio_channel_get(fsa_priv->dev, "typec_mode"); if (!fsa_priv->iio_ch) { dev_err(fsa_priv->dev, "%s: iio_channel_get failed for typec_mode\n", __func__); goto err_supply; } rc = power_supply_reg_notifier(&fsa_priv->nb); if (rc) { dev_err(fsa_priv->dev, "%s: power supply reg failed: %d\n", __func__, rc); goto err_supply; } } mutex_init(&fsa_priv->notification_lock); i2c_set_clientdata(i2c, fsa_priv); Loading @@ -362,6 +534,8 @@ static int fsa4480_probe(struct i2c_client *i2c, return 0; err_supply: power_supply_put(fsa_priv->usb_psy); err_data: devm_kfree(&i2c->dev, fsa_priv); return rc; Loading @@ -375,7 +549,13 @@ static int fsa4480_remove(struct i2c_client *i2c) if (!fsa_priv) return -EINVAL; unregister_ucsi_glink_notifier(&fsa_priv->ucsi_nb); if (fsa_priv->use_powersupply) { /* deregister from PMI */ power_supply_unreg_notifier(&fsa_priv->nb); power_supply_put(fsa_priv->usb_psy); } else { unregister_ucsi_glink_notifier(&fsa_priv->nb); } fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); cancel_work_sync(&fsa_priv->usbc_analog_work); pm_relax(fsa_priv->dev); Loading