11dc24632SBjorn Andersson // SPDX-License-Identifier: GPL-2.0 21dc24632SBjorn Andersson /* 31dc24632SBjorn Andersson * Copyright (C) 2021-2022 Linaro Ltd. 41dc24632SBjorn Andersson * Copyright (C) 2018-2020 The Linux Foundation 51dc24632SBjorn Andersson */ 61dc24632SBjorn Andersson 71dc24632SBjorn Andersson #include <linux/bits.h> 81dc24632SBjorn Andersson #include <linux/i2c.h> 91dc24632SBjorn Andersson #include <linux/kernel.h> 101dc24632SBjorn Andersson #include <linux/module.h> 111dc24632SBjorn Andersson #include <linux/mutex.h> 121dc24632SBjorn Andersson #include <linux/regmap.h> 131dc24632SBjorn Andersson #include <linux/usb/typec_dp.h> 141dc24632SBjorn Andersson #include <linux/usb/typec_mux.h> 151dc24632SBjorn Andersson 161dc24632SBjorn Andersson #define FSA4480_SWITCH_ENABLE 0x04 171dc24632SBjorn Andersson #define FSA4480_SWITCH_SELECT 0x05 181dc24632SBjorn Andersson #define FSA4480_SWITCH_STATUS1 0x07 191dc24632SBjorn Andersson #define FSA4480_SLOW_L 0x08 201dc24632SBjorn Andersson #define FSA4480_SLOW_R 0x09 211dc24632SBjorn Andersson #define FSA4480_SLOW_MIC 0x0a 221dc24632SBjorn Andersson #define FSA4480_SLOW_SENSE 0x0b 231dc24632SBjorn Andersson #define FSA4480_SLOW_GND 0x0c 241dc24632SBjorn Andersson #define FSA4480_DELAY_L_R 0x0d 251dc24632SBjorn Andersson #define FSA4480_DELAY_L_MIC 0x0e 261dc24632SBjorn Andersson #define FSA4480_DELAY_L_SENSE 0x0f 271dc24632SBjorn Andersson #define FSA4480_DELAY_L_AGND 0x10 281dc24632SBjorn Andersson #define FSA4480_RESET 0x1e 291dc24632SBjorn Andersson #define FSA4480_MAX_REGISTER 0x1f 301dc24632SBjorn Andersson 311dc24632SBjorn Andersson #define FSA4480_ENABLE_DEVICE BIT(7) 321dc24632SBjorn Andersson #define FSA4480_ENABLE_SBU GENMASK(6, 5) 331dc24632SBjorn Andersson #define FSA4480_ENABLE_USB GENMASK(4, 3) 341dc24632SBjorn Andersson 351dc24632SBjorn Andersson #define FSA4480_SEL_SBU_REVERSE GENMASK(6, 5) 361dc24632SBjorn Andersson #define FSA4480_SEL_USB GENMASK(4, 3) 371dc24632SBjorn Andersson 381dc24632SBjorn Andersson struct fsa4480 { 391dc24632SBjorn Andersson struct i2c_client *client; 401dc24632SBjorn Andersson 411dc24632SBjorn Andersson /* used to serialize concurrent change requests */ 421dc24632SBjorn Andersson struct mutex lock; 431dc24632SBjorn Andersson 441dc24632SBjorn Andersson struct typec_switch_dev *sw; 451dc24632SBjorn Andersson struct typec_mux_dev *mux; 461dc24632SBjorn Andersson 471dc24632SBjorn Andersson struct regmap *regmap; 481dc24632SBjorn Andersson 491dc24632SBjorn Andersson u8 cur_enable; 501dc24632SBjorn Andersson u8 cur_select; 511dc24632SBjorn Andersson }; 521dc24632SBjorn Andersson 531dc24632SBjorn Andersson static const struct regmap_config fsa4480_regmap_config = { 541dc24632SBjorn Andersson .reg_bits = 8, 551dc24632SBjorn Andersson .val_bits = 8, 561dc24632SBjorn Andersson .max_register = FSA4480_MAX_REGISTER, 571dc24632SBjorn Andersson /* Accesses only done under fsa4480->lock */ 581dc24632SBjorn Andersson .disable_locking = true, 591dc24632SBjorn Andersson }; 601dc24632SBjorn Andersson 611dc24632SBjorn Andersson static int fsa4480_switch_set(struct typec_switch_dev *sw, 621dc24632SBjorn Andersson enum typec_orientation orientation) 631dc24632SBjorn Andersson { 641dc24632SBjorn Andersson struct fsa4480 *fsa = typec_switch_get_drvdata(sw); 651dc24632SBjorn Andersson u8 new_sel; 661dc24632SBjorn Andersson 671dc24632SBjorn Andersson mutex_lock(&fsa->lock); 681dc24632SBjorn Andersson new_sel = FSA4480_SEL_USB; 691dc24632SBjorn Andersson if (orientation == TYPEC_ORIENTATION_REVERSE) 701dc24632SBjorn Andersson new_sel |= FSA4480_SEL_SBU_REVERSE; 711dc24632SBjorn Andersson 721dc24632SBjorn Andersson if (new_sel == fsa->cur_select) 731dc24632SBjorn Andersson goto out_unlock; 741dc24632SBjorn Andersson 751dc24632SBjorn Andersson if (fsa->cur_enable & FSA4480_ENABLE_SBU) { 761dc24632SBjorn Andersson /* Disable SBU output while re-configuring the switch */ 771dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, 781dc24632SBjorn Andersson fsa->cur_enable & ~FSA4480_ENABLE_SBU); 791dc24632SBjorn Andersson 801dc24632SBjorn Andersson /* 35us to allow the SBU switch to turn off */ 811dc24632SBjorn Andersson usleep_range(35, 1000); 821dc24632SBjorn Andersson } 831dc24632SBjorn Andersson 841dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, new_sel); 851dc24632SBjorn Andersson fsa->cur_select = new_sel; 861dc24632SBjorn Andersson 871dc24632SBjorn Andersson if (fsa->cur_enable & FSA4480_ENABLE_SBU) { 881dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable); 891dc24632SBjorn Andersson 901dc24632SBjorn Andersson /* 15us to allow the SBU switch to turn on again */ 911dc24632SBjorn Andersson usleep_range(15, 1000); 921dc24632SBjorn Andersson } 931dc24632SBjorn Andersson 941dc24632SBjorn Andersson out_unlock: 951dc24632SBjorn Andersson mutex_unlock(&fsa->lock); 961dc24632SBjorn Andersson 971dc24632SBjorn Andersson return 0; 981dc24632SBjorn Andersson } 991dc24632SBjorn Andersson 1001dc24632SBjorn Andersson static int fsa4480_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) 1011dc24632SBjorn Andersson { 1021dc24632SBjorn Andersson struct fsa4480 *fsa = typec_mux_get_drvdata(mux); 1031dc24632SBjorn Andersson u8 new_enable; 1041dc24632SBjorn Andersson 1051dc24632SBjorn Andersson mutex_lock(&fsa->lock); 1061dc24632SBjorn Andersson 1071dc24632SBjorn Andersson new_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB; 1081dc24632SBjorn Andersson if (state->mode >= TYPEC_DP_STATE_A) 1091dc24632SBjorn Andersson new_enable |= FSA4480_ENABLE_SBU; 1101dc24632SBjorn Andersson 1111dc24632SBjorn Andersson if (new_enable == fsa->cur_enable) 1121dc24632SBjorn Andersson goto out_unlock; 1131dc24632SBjorn Andersson 1141dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, new_enable); 1151dc24632SBjorn Andersson fsa->cur_enable = new_enable; 1161dc24632SBjorn Andersson 1171dc24632SBjorn Andersson if (new_enable & FSA4480_ENABLE_SBU) { 1181dc24632SBjorn Andersson /* 15us to allow the SBU switch to turn off */ 1191dc24632SBjorn Andersson usleep_range(15, 1000); 1201dc24632SBjorn Andersson } 1211dc24632SBjorn Andersson 1221dc24632SBjorn Andersson out_unlock: 1231dc24632SBjorn Andersson mutex_unlock(&fsa->lock); 1241dc24632SBjorn Andersson 1251dc24632SBjorn Andersson return 0; 1261dc24632SBjorn Andersson } 1271dc24632SBjorn Andersson 1281dc24632SBjorn Andersson static int fsa4480_probe(struct i2c_client *client) 1291dc24632SBjorn Andersson { 1301dc24632SBjorn Andersson struct device *dev = &client->dev; 1311dc24632SBjorn Andersson struct typec_switch_desc sw_desc = { }; 1321dc24632SBjorn Andersson struct typec_mux_desc mux_desc = { }; 1331dc24632SBjorn Andersson struct fsa4480 *fsa; 1341dc24632SBjorn Andersson 1351dc24632SBjorn Andersson fsa = devm_kzalloc(dev, sizeof(*fsa), GFP_KERNEL); 1361dc24632SBjorn Andersson if (!fsa) 1371dc24632SBjorn Andersson return -ENOMEM; 1381dc24632SBjorn Andersson 1391dc24632SBjorn Andersson fsa->client = client; 1401dc24632SBjorn Andersson mutex_init(&fsa->lock); 1411dc24632SBjorn Andersson 1421dc24632SBjorn Andersson fsa->regmap = devm_regmap_init_i2c(client, &fsa4480_regmap_config); 1431dc24632SBjorn Andersson if (IS_ERR(fsa->regmap)) 1441dc24632SBjorn Andersson return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n"); 1451dc24632SBjorn Andersson 1461dc24632SBjorn Andersson fsa->cur_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB; 1471dc24632SBjorn Andersson fsa->cur_select = FSA4480_SEL_USB; 1481dc24632SBjorn Andersson 1491dc24632SBjorn Andersson /* set default settings */ 1501dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SLOW_L, 0x00); 1511dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SLOW_R, 0x00); 1521dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SLOW_MIC, 0x00); 1531dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SLOW_SENSE, 0x00); 1541dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SLOW_GND, 0x00); 1551dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_DELAY_L_R, 0x00); 1561dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_DELAY_L_MIC, 0x00); 1571dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_DELAY_L_SENSE, 0x00); 1581dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_DELAY_L_AGND, 0x09); 1591dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, fsa->cur_select); 1601dc24632SBjorn Andersson regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable); 1611dc24632SBjorn Andersson 1621dc24632SBjorn Andersson sw_desc.drvdata = fsa; 1631dc24632SBjorn Andersson sw_desc.fwnode = dev_fwnode(dev); 1641dc24632SBjorn Andersson sw_desc.set = fsa4480_switch_set; 1651dc24632SBjorn Andersson 1661dc24632SBjorn Andersson fsa->sw = typec_switch_register(dev, &sw_desc); 1671dc24632SBjorn Andersson if (IS_ERR(fsa->sw)) 1681dc24632SBjorn Andersson return dev_err_probe(dev, PTR_ERR(fsa->sw), "failed to register typec switch\n"); 1691dc24632SBjorn Andersson 1701dc24632SBjorn Andersson mux_desc.drvdata = fsa; 1711dc24632SBjorn Andersson mux_desc.fwnode = dev_fwnode(dev); 1721dc24632SBjorn Andersson mux_desc.set = fsa4480_mux_set; 1731dc24632SBjorn Andersson 1741dc24632SBjorn Andersson fsa->mux = typec_mux_register(dev, &mux_desc); 1751dc24632SBjorn Andersson if (IS_ERR(fsa->mux)) { 1761dc24632SBjorn Andersson typec_switch_unregister(fsa->sw); 1771dc24632SBjorn Andersson return dev_err_probe(dev, PTR_ERR(fsa->mux), "failed to register typec mux\n"); 1781dc24632SBjorn Andersson } 1791dc24632SBjorn Andersson 1801dc24632SBjorn Andersson i2c_set_clientdata(client, fsa); 1811dc24632SBjorn Andersson return 0; 1821dc24632SBjorn Andersson } 1831dc24632SBjorn Andersson 184ed5c2f5fSUwe Kleine-König static void fsa4480_remove(struct i2c_client *client) 1851dc24632SBjorn Andersson { 1861dc24632SBjorn Andersson struct fsa4480 *fsa = i2c_get_clientdata(client); 1871dc24632SBjorn Andersson 1881dc24632SBjorn Andersson typec_mux_unregister(fsa->mux); 1891dc24632SBjorn Andersson typec_switch_unregister(fsa->sw); 1901dc24632SBjorn Andersson } 1911dc24632SBjorn Andersson 1921dc24632SBjorn Andersson static const struct i2c_device_id fsa4480_table[] = { 1931dc24632SBjorn Andersson { "fsa4480" }, 1941dc24632SBjorn Andersson { } 1951dc24632SBjorn Andersson }; 1961dc24632SBjorn Andersson MODULE_DEVICE_TABLE(i2c, fsa4480_table); 1971dc24632SBjorn Andersson 1981dc24632SBjorn Andersson static const struct of_device_id fsa4480_of_table[] = { 1991dc24632SBjorn Andersson { .compatible = "fcs,fsa4480" }, 2001dc24632SBjorn Andersson { } 2011dc24632SBjorn Andersson }; 2021dc24632SBjorn Andersson MODULE_DEVICE_TABLE(of, fsa4480_of_table); 2031dc24632SBjorn Andersson 2041dc24632SBjorn Andersson static struct i2c_driver fsa4480_driver = { 2051dc24632SBjorn Andersson .driver = { 2061dc24632SBjorn Andersson .name = "fsa4480", 2071dc24632SBjorn Andersson .of_match_table = fsa4480_of_table, 2081dc24632SBjorn Andersson }, 209*7126a2aeSUwe Kleine-König .probe = fsa4480_probe, 2101dc24632SBjorn Andersson .remove = fsa4480_remove, 2111dc24632SBjorn Andersson .id_table = fsa4480_table, 2121dc24632SBjorn Andersson }; 2131dc24632SBjorn Andersson module_i2c_driver(fsa4480_driver); 2141dc24632SBjorn Andersson 2151dc24632SBjorn Andersson MODULE_DESCRIPTION("ON Semiconductor FSA4480 driver"); 2161dc24632SBjorn Andersson MODULE_LICENSE("GPL v2"); 217