brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · 19a7c37 Raw
318 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Linear Technology LTC4306 and LTC4305 I2C multiplexer/switch4 *5 * Copyright (C) 2017 Analog Devices Inc.6 *7 * Based on: i2c-mux-pca954x.c8 *9 * Datasheet: http://cds.linear.com/docs/en/datasheet/4306.pdf10 */11 12#include <linux/gpio/consumer.h>13#include <linux/gpio/driver.h>14#include <linux/i2c-mux.h>15#include <linux/i2c.h>16#include <linux/module.h>17#include <linux/of.h>18#include <linux/property.h>19#include <linux/regmap.h>20#include <linux/slab.h>21 22#define LTC4305_MAX_NCHANS 223#define LTC4306_MAX_NCHANS 424 25#define LTC_REG_STATUS	0x026#define LTC_REG_CONFIG	0x127#define LTC_REG_MODE	0x228#define LTC_REG_SWITCH	0x329 30#define LTC_DOWNSTREAM_ACCL_EN	BIT(6)31#define LTC_UPSTREAM_ACCL_EN	BIT(7)32 33#define LTC_GPIO_ALL_INPUT	0xC034#define LTC_SWITCH_MASK		0xF035 36enum ltc_type {37	ltc_4305,38	ltc_4306,39};40 41struct chip_desc {42	u8 nchans;43	u8 num_gpios;44};45 46struct ltc4306 {47	struct regmap *regmap;48	struct gpio_chip gpiochip;49	const struct chip_desc *chip;50};51 52static const struct chip_desc chips[] = {53	[ltc_4305] = {54		.nchans = LTC4305_MAX_NCHANS,55	},56	[ltc_4306] = {57		.nchans = LTC4306_MAX_NCHANS,58		.num_gpios = 2,59	},60};61 62static bool ltc4306_is_volatile_reg(struct device *dev, unsigned int reg)63{64	return reg == LTC_REG_CONFIG;65}66 67static const struct regmap_config ltc4306_regmap_config = {68	.reg_bits = 8,69	.val_bits = 8,70	.max_register = LTC_REG_SWITCH,71	.volatile_reg = ltc4306_is_volatile_reg,72	.cache_type = REGCACHE_FLAT,73};74 75static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset)76{77	struct ltc4306 *data = gpiochip_get_data(chip);78	unsigned int val;79	int ret;80 81	ret = regmap_read(data->regmap, LTC_REG_CONFIG, &val);82	if (ret < 0)83		return ret;84 85	return !!(val & BIT(1 - offset));86}87 88static void ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset,89			     int value)90{91	struct ltc4306 *data = gpiochip_get_data(chip);92 93	regmap_update_bits(data->regmap, LTC_REG_CONFIG, BIT(5 - offset),94			   value ? BIT(5 - offset) : 0);95}96 97static int ltc4306_gpio_get_direction(struct gpio_chip *chip,98				      unsigned int offset)99{100	struct ltc4306 *data = gpiochip_get_data(chip);101	unsigned int val;102	int ret;103 104	ret = regmap_read(data->regmap, LTC_REG_MODE, &val);105	if (ret < 0)106		return ret;107 108	return !!(val & BIT(7 - offset));109}110 111static int ltc4306_gpio_direction_input(struct gpio_chip *chip,112					unsigned int offset)113{114	struct ltc4306 *data = gpiochip_get_data(chip);115 116	return regmap_update_bits(data->regmap, LTC_REG_MODE,117				  BIT(7 - offset), BIT(7 - offset));118}119 120static int ltc4306_gpio_direction_output(struct gpio_chip *chip,121					 unsigned int offset, int value)122{123	struct ltc4306 *data = gpiochip_get_data(chip);124 125	ltc4306_gpio_set(chip, offset, value);126	return regmap_update_bits(data->regmap, LTC_REG_MODE,127				  BIT(7 - offset), 0);128}129 130static int ltc4306_gpio_set_config(struct gpio_chip *chip,131				   unsigned int offset, unsigned long config)132{133	struct ltc4306 *data = gpiochip_get_data(chip);134	unsigned int val;135 136	switch (pinconf_to_config_param(config)) {137	case PIN_CONFIG_DRIVE_OPEN_DRAIN:138		val = 0;139		break;140	case PIN_CONFIG_DRIVE_PUSH_PULL:141		val = BIT(4 - offset);142		break;143	default:144		return -ENOTSUPP;145	}146 147	return regmap_update_bits(data->regmap, LTC_REG_MODE,148				  BIT(4 - offset), val);149}150 151static int ltc4306_gpio_init(struct ltc4306 *data)152{153	struct device *dev = regmap_get_device(data->regmap);154 155	if (!data->chip->num_gpios)156		return 0;157 158	data->gpiochip.label = dev_name(dev);159	data->gpiochip.base = -1;160	data->gpiochip.ngpio = data->chip->num_gpios;161	data->gpiochip.parent = dev;162	data->gpiochip.can_sleep = true;163	data->gpiochip.get_direction = ltc4306_gpio_get_direction;164	data->gpiochip.direction_input = ltc4306_gpio_direction_input;165	data->gpiochip.direction_output = ltc4306_gpio_direction_output;166	data->gpiochip.get = ltc4306_gpio_get;167	data->gpiochip.set = ltc4306_gpio_set;168	data->gpiochip.set_config = ltc4306_gpio_set_config;169	data->gpiochip.owner = THIS_MODULE;170 171	/* gpiolib assumes all GPIOs default input */172	regmap_write(data->regmap, LTC_REG_MODE, LTC_GPIO_ALL_INPUT);173 174	return devm_gpiochip_add_data(dev, &data->gpiochip, data);175}176 177static int ltc4306_select_mux(struct i2c_mux_core *muxc, u32 chan)178{179	struct ltc4306 *data = i2c_mux_priv(muxc);180 181	return regmap_update_bits(data->regmap, LTC_REG_SWITCH,182				  LTC_SWITCH_MASK, BIT(7 - chan));183}184 185static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan)186{187	struct ltc4306 *data = i2c_mux_priv(muxc);188 189	return regmap_update_bits(data->regmap, LTC_REG_SWITCH,190				  LTC_SWITCH_MASK, 0);191}192 193static const struct i2c_device_id ltc4306_id[] = {194	{ "ltc4305", ltc_4305 },195	{ "ltc4306", ltc_4306 },196	{ }197};198MODULE_DEVICE_TABLE(i2c, ltc4306_id);199 200static const struct of_device_id ltc4306_of_match[] = {201	{ .compatible = "lltc,ltc4305", .data = &chips[ltc_4305] },202	{ .compatible = "lltc,ltc4306", .data = &chips[ltc_4306] },203	{ }204};205MODULE_DEVICE_TABLE(of, ltc4306_of_match);206 207static int ltc4306_probe(struct i2c_client *client)208{209	struct i2c_adapter *adap = client->adapter;210	const struct chip_desc *chip;211	struct i2c_mux_core *muxc;212	struct ltc4306 *data;213	struct gpio_desc *gpio;214	bool idle_disc;215	unsigned int val = 0;216	int num, ret;217 218	chip = of_device_get_match_data(&client->dev);219 220	if (!chip)221		chip = &chips[i2c_match_id(ltc4306_id, client)->driver_data];222 223	idle_disc = device_property_read_bool(&client->dev,224					      "i2c-mux-idle-disconnect");225 226	muxc = i2c_mux_alloc(adap, &client->dev,227			     chip->nchans, sizeof(*data),228			     I2C_MUX_LOCKED, ltc4306_select_mux,229			     idle_disc ? ltc4306_deselect_mux : NULL);230	if (!muxc)231		return -ENOMEM;232	data = i2c_mux_priv(muxc);233	data->chip = chip;234 235	i2c_set_clientdata(client, muxc);236 237	data->regmap = devm_regmap_init_i2c(client, &ltc4306_regmap_config);238	if (IS_ERR(data->regmap)) {239		ret = PTR_ERR(data->regmap);240		dev_err(&client->dev, "Failed to allocate register map: %d\n",241			ret);242		return ret;243	}244 245	/* Reset and enable the mux if an enable GPIO is specified. */246	gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW);247	if (IS_ERR(gpio))248		return PTR_ERR(gpio);249 250	if (gpio) {251		udelay(1);252		gpiod_set_value(gpio, 1);253	}254 255	/*256	 * Write the mux register at addr to verify257	 * that the mux is in fact present. This also258	 * initializes the mux to disconnected state.259	 */260	if (regmap_write(data->regmap, LTC_REG_SWITCH, 0) < 0) {261		dev_warn(&client->dev, "probe failed\n");262		return -ENODEV;263	}264 265	if (device_property_read_bool(&client->dev,266				      "ltc,downstream-accelerators-enable"))267		val |= LTC_DOWNSTREAM_ACCL_EN;268 269	if (device_property_read_bool(&client->dev,270				      "ltc,upstream-accelerators-enable"))271		val |= LTC_UPSTREAM_ACCL_EN;272 273	if (regmap_write(data->regmap, LTC_REG_CONFIG, val) < 0)274		return -ENODEV;275 276	ret = ltc4306_gpio_init(data);277	if (ret < 0)278		return ret;279 280	/* Now create an adapter for each channel */281	for (num = 0; num < chip->nchans; num++) {282		ret = i2c_mux_add_adapter(muxc, 0, num);283		if (ret) {284			i2c_mux_del_adapters(muxc);285			return ret;286		}287	}288 289	dev_info(&client->dev,290		 "registered %d multiplexed busses for I2C switch %s\n",291		 num, client->name);292 293	return 0;294}295 296static void ltc4306_remove(struct i2c_client *client)297{298	struct i2c_mux_core *muxc = i2c_get_clientdata(client);299 300	i2c_mux_del_adapters(muxc);301}302 303static struct i2c_driver ltc4306_driver = {304	.driver		= {305		.name	= "ltc4306",306		.of_match_table = of_match_ptr(ltc4306_of_match),307	},308	.probe		= ltc4306_probe,309	.remove		= ltc4306_remove,310	.id_table	= ltc4306_id,311};312 313module_i2c_driver(ltc4306_driver);314 315MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");316MODULE_DESCRIPTION("Linear Technology LTC4306, LTC4305 I2C mux/switch driver");317MODULE_LICENSE("GPL v2");318