cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");