fsa4480.c (5693B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2021-2022 Linaro Ltd. 4 * Copyright (C) 2018-2020 The Linux Foundation 5 */ 6 7#include <linux/bits.h> 8#include <linux/i2c.h> 9#include <linux/kernel.h> 10#include <linux/module.h> 11#include <linux/mutex.h> 12#include <linux/regmap.h> 13#include <linux/usb/typec_dp.h> 14#include <linux/usb/typec_mux.h> 15 16#define FSA4480_SWITCH_ENABLE 0x04 17#define FSA4480_SWITCH_SELECT 0x05 18#define FSA4480_SWITCH_STATUS1 0x07 19#define FSA4480_SLOW_L 0x08 20#define FSA4480_SLOW_R 0x09 21#define FSA4480_SLOW_MIC 0x0a 22#define FSA4480_SLOW_SENSE 0x0b 23#define FSA4480_SLOW_GND 0x0c 24#define FSA4480_DELAY_L_R 0x0d 25#define FSA4480_DELAY_L_MIC 0x0e 26#define FSA4480_DELAY_L_SENSE 0x0f 27#define FSA4480_DELAY_L_AGND 0x10 28#define FSA4480_RESET 0x1e 29#define FSA4480_MAX_REGISTER 0x1f 30 31#define FSA4480_ENABLE_DEVICE BIT(7) 32#define FSA4480_ENABLE_SBU GENMASK(6, 5) 33#define FSA4480_ENABLE_USB GENMASK(4, 3) 34 35#define FSA4480_SEL_SBU_REVERSE GENMASK(6, 5) 36#define FSA4480_SEL_USB GENMASK(4, 3) 37 38struct fsa4480 { 39 struct i2c_client *client; 40 41 /* used to serialize concurrent change requests */ 42 struct mutex lock; 43 44 struct typec_switch_dev *sw; 45 struct typec_mux_dev *mux; 46 47 struct regmap *regmap; 48 49 u8 cur_enable; 50 u8 cur_select; 51}; 52 53static const struct regmap_config fsa4480_regmap_config = { 54 .reg_bits = 8, 55 .val_bits = 8, 56 .max_register = FSA4480_MAX_REGISTER, 57 /* Accesses only done under fsa4480->lock */ 58 .disable_locking = true, 59}; 60 61static int fsa4480_switch_set(struct typec_switch_dev *sw, 62 enum typec_orientation orientation) 63{ 64 struct fsa4480 *fsa = typec_switch_get_drvdata(sw); 65 u8 new_sel; 66 67 mutex_lock(&fsa->lock); 68 new_sel = FSA4480_SEL_USB; 69 if (orientation == TYPEC_ORIENTATION_REVERSE) 70 new_sel |= FSA4480_SEL_SBU_REVERSE; 71 72 if (new_sel == fsa->cur_select) 73 goto out_unlock; 74 75 if (fsa->cur_enable & FSA4480_ENABLE_SBU) { 76 /* Disable SBU output while re-configuring the switch */ 77 regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, 78 fsa->cur_enable & ~FSA4480_ENABLE_SBU); 79 80 /* 35us to allow the SBU switch to turn off */ 81 usleep_range(35, 1000); 82 } 83 84 regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, new_sel); 85 fsa->cur_select = new_sel; 86 87 if (fsa->cur_enable & FSA4480_ENABLE_SBU) { 88 regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable); 89 90 /* 15us to allow the SBU switch to turn on again */ 91 usleep_range(15, 1000); 92 } 93 94out_unlock: 95 mutex_unlock(&fsa->lock); 96 97 return 0; 98} 99 100static int fsa4480_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) 101{ 102 struct fsa4480 *fsa = typec_mux_get_drvdata(mux); 103 u8 new_enable; 104 105 mutex_lock(&fsa->lock); 106 107 new_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB; 108 if (state->mode >= TYPEC_DP_STATE_A) 109 new_enable |= FSA4480_ENABLE_SBU; 110 111 if (new_enable == fsa->cur_enable) 112 goto out_unlock; 113 114 regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, new_enable); 115 fsa->cur_enable = new_enable; 116 117 if (new_enable & FSA4480_ENABLE_SBU) { 118 /* 15us to allow the SBU switch to turn off */ 119 usleep_range(15, 1000); 120 } 121 122out_unlock: 123 mutex_unlock(&fsa->lock); 124 125 return 0; 126} 127 128static int fsa4480_probe(struct i2c_client *client) 129{ 130 struct device *dev = &client->dev; 131 struct typec_switch_desc sw_desc = { }; 132 struct typec_mux_desc mux_desc = { }; 133 struct fsa4480 *fsa; 134 135 fsa = devm_kzalloc(dev, sizeof(*fsa), GFP_KERNEL); 136 if (!fsa) 137 return -ENOMEM; 138 139 fsa->client = client; 140 mutex_init(&fsa->lock); 141 142 fsa->regmap = devm_regmap_init_i2c(client, &fsa4480_regmap_config); 143 if (IS_ERR(fsa->regmap)) 144 return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n"); 145 146 fsa->cur_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB; 147 fsa->cur_select = FSA4480_SEL_USB; 148 149 /* set default settings */ 150 regmap_write(fsa->regmap, FSA4480_SLOW_L, 0x00); 151 regmap_write(fsa->regmap, FSA4480_SLOW_R, 0x00); 152 regmap_write(fsa->regmap, FSA4480_SLOW_MIC, 0x00); 153 regmap_write(fsa->regmap, FSA4480_SLOW_SENSE, 0x00); 154 regmap_write(fsa->regmap, FSA4480_SLOW_GND, 0x00); 155 regmap_write(fsa->regmap, FSA4480_DELAY_L_R, 0x00); 156 regmap_write(fsa->regmap, FSA4480_DELAY_L_MIC, 0x00); 157 regmap_write(fsa->regmap, FSA4480_DELAY_L_SENSE, 0x00); 158 regmap_write(fsa->regmap, FSA4480_DELAY_L_AGND, 0x09); 159 regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, fsa->cur_select); 160 regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable); 161 162 sw_desc.drvdata = fsa; 163 sw_desc.fwnode = dev_fwnode(dev); 164 sw_desc.set = fsa4480_switch_set; 165 166 fsa->sw = typec_switch_register(dev, &sw_desc); 167 if (IS_ERR(fsa->sw)) 168 return dev_err_probe(dev, PTR_ERR(fsa->sw), "failed to register typec switch\n"); 169 170 mux_desc.drvdata = fsa; 171 mux_desc.fwnode = dev_fwnode(dev); 172 mux_desc.set = fsa4480_mux_set; 173 174 fsa->mux = typec_mux_register(dev, &mux_desc); 175 if (IS_ERR(fsa->mux)) { 176 typec_switch_unregister(fsa->sw); 177 return dev_err_probe(dev, PTR_ERR(fsa->mux), "failed to register typec mux\n"); 178 } 179 180 i2c_set_clientdata(client, fsa); 181 return 0; 182} 183 184static int fsa4480_remove(struct i2c_client *client) 185{ 186 struct fsa4480 *fsa = i2c_get_clientdata(client); 187 188 typec_mux_unregister(fsa->mux); 189 typec_switch_unregister(fsa->sw); 190 191 return 0; 192} 193 194static const struct i2c_device_id fsa4480_table[] = { 195 { "fsa4480" }, 196 { } 197}; 198MODULE_DEVICE_TABLE(i2c, fsa4480_table); 199 200static const struct of_device_id fsa4480_of_table[] = { 201 { .compatible = "fcs,fsa4480" }, 202 { } 203}; 204MODULE_DEVICE_TABLE(of, fsa4480_of_table); 205 206static struct i2c_driver fsa4480_driver = { 207 .driver = { 208 .name = "fsa4480", 209 .of_match_table = fsa4480_of_table, 210 }, 211 .probe_new = fsa4480_probe, 212 .remove = fsa4480_remove, 213 .id_table = fsa4480_table, 214}; 215module_i2c_driver(fsa4480_driver); 216 217MODULE_DESCRIPTION("ON Semiconductor FSA4480 driver"); 218MODULE_LICENSE("GPL v2");