i2c-mux-reg.c 6.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * I2C multiplexer using a single register
 *
 * Copyright 2015 Freescale Semiconductor
 * York Sun  <yorksun@freescale.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 */

#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_data/i2c-mux-reg.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

struct regmux {
	struct i2c_mux_reg_platform_data data;
};

static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan_id)
{
	if (!mux->data.reg)
		return -EINVAL;

32 33 34 35 36 37
	/*
	 * Write to the register, followed by a read to ensure the write is
	 * completed on a "posted" bus, for example PCI or write buffers.
	 * The endianness of reading doesn't matter and the return data
	 * is not used.
	 */
38 39
	switch (mux->data.reg_size) {
	case 4:
40
		if (mux->data.little_endian)
41
			iowrite32(chan_id, mux->data.reg);
42
		else
43
			iowrite32be(chan_id, mux->data.reg);
44 45
		if (!mux->data.write_only)
			ioread32(mux->data.reg);
46 47
		break;
	case 2:
48
		if (mux->data.little_endian)
49
			iowrite16(chan_id, mux->data.reg);
50
		else
51
			iowrite16be(chan_id, mux->data.reg);
52 53
		if (!mux->data.write_only)
			ioread16(mux->data.reg);
54 55 56 57 58 59 60 61 62 63 64
		break;
	case 1:
		iowrite8(chan_id, mux->data.reg);
		if (!mux->data.write_only)
			ioread8(mux->data.reg);
		break;
	}

	return 0;
}

65
static int i2c_mux_reg_select(struct i2c_mux_core *muxc, u32 chan)
66
{
67
	struct regmux *mux = i2c_mux_priv(muxc);
68 69 70 71

	return i2c_mux_reg_set(mux, chan);
}

72
static int i2c_mux_reg_deselect(struct i2c_mux_core *muxc, u32 chan)
73
{
74
	struct regmux *mux = i2c_mux_priv(muxc);
75 76 77 78 79 80 81 82 83

	if (mux->data.idle_in_use)
		return i2c_mux_reg_set(mux, mux->data.idle);

	return 0;
}

#ifdef CONFIG_OF
static int i2c_mux_reg_probe_dt(struct regmux *mux,
84
				struct platform_device *pdev)
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
{
	struct device_node *np = pdev->dev.of_node;
	struct device_node *adapter_np, *child;
	struct i2c_adapter *adapter;
	struct resource res;
	unsigned *values;
	int i = 0;

	if (!np)
		return -ENODEV;

	adapter_np = of_parse_phandle(np, "i2c-parent", 0);
	if (!adapter_np) {
		dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
		return -ENODEV;
	}
	adapter = of_find_i2c_adapter_by_node(adapter_np);
102
	of_node_put(adapter_np);
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
	if (!adapter)
		return -EPROBE_DEFER;

	mux->data.parent = i2c_adapter_id(adapter);
	put_device(&adapter->dev);

	mux->data.n_values = of_get_child_count(np);
	if (of_find_property(np, "little-endian", NULL)) {
		mux->data.little_endian = true;
	} else if (of_find_property(np, "big-endian", NULL)) {
		mux->data.little_endian = false;
	} else {
#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : \
	defined(__LITTLE_ENDIAN)
		mux->data.little_endian = true;
#elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : \
	defined(__BIG_ENDIAN)
		mux->data.little_endian = false;
#else
#error Endianness not defined?
#endif
	}
	if (of_find_property(np, "write-only", NULL))
		mux->data.write_only = true;
	else
		mux->data.write_only = false;

	values = devm_kzalloc(&pdev->dev,
			      sizeof(*mux->data.values) * mux->data.n_values,
			      GFP_KERNEL);
	if (!values) {
		dev_err(&pdev->dev, "Cannot allocate values array");
		return -ENOMEM;
	}

	for_each_child_of_node(np, child) {
		of_property_read_u32(child, "reg", values + i);
		i++;
	}
	mux->data.values = values;

	if (!of_property_read_u32(np, "idle-state", &mux->data.idle))
		mux->data.idle_in_use = true;

	/* map address from "reg" if exists */
	if (of_address_to_resource(np, 0, &res)) {
		mux->data.reg_size = resource_size(&res);
		mux->data.reg = devm_ioremap_resource(&pdev->dev, &res);
		if (IS_ERR(mux->data.reg))
			return PTR_ERR(mux->data.reg);
	}

	return 0;
}
#else
158
static int i2c_mux_reg_probe_dt(struct regmux *mux,
159
				struct platform_device *pdev)
160 161 162 163 164 165 166
{
	return 0;
}
#endif

static int i2c_mux_reg_probe(struct platform_device *pdev)
{
167
	struct i2c_mux_core *muxc;
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
	struct regmux *mux;
	struct i2c_adapter *parent;
	struct resource *res;
	unsigned int class;
	int i, ret, nr;

	mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
	if (!mux)
		return -ENOMEM;

	if (dev_get_platdata(&pdev->dev)) {
		memcpy(&mux->data, dev_get_platdata(&pdev->dev),
			sizeof(mux->data));
	} else {
		ret = i2c_mux_reg_probe_dt(mux, pdev);
		if (ret < 0) {
			dev_err(&pdev->dev, "Error parsing device tree");
			return ret;
		}
	}

189 190 191 192
	parent = i2c_get_adapter(mux->data.parent);
	if (!parent)
		return -EPROBE_DEFER;

193 194 195 196 197 198 199 200 201 202
	if (!mux->data.reg) {
		dev_info(&pdev->dev,
			"Register not set, using platform resource\n");
		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
		mux->data.reg_size = resource_size(res);
		mux->data.reg = devm_ioremap_resource(&pdev->dev, res);
		if (IS_ERR(mux->data.reg))
			return PTR_ERR(mux->data.reg);
	}

203 204 205 206 207 208
	if (mux->data.reg_size != 4 && mux->data.reg_size != 2 &&
	    mux->data.reg_size != 1) {
		dev_err(&pdev->dev, "Invalid register size\n");
		return -EINVAL;
	}

209 210 211
	muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0,
			     i2c_mux_reg_select, NULL);
	if (!muxc)
212
		return -ENOMEM;
213 214 215
	muxc->priv = mux;

	platform_set_drvdata(pdev, muxc);
216 217

	if (mux->data.idle_in_use)
218
		muxc->deselect = i2c_mux_reg_deselect;
219 220 221 222 223

	for (i = 0; i < mux->data.n_values; i++) {
		nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
		class = mux->data.classes ? mux->data.classes[i] : 0;

224 225
		ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
		if (ret) {
226 227 228 229 230 231
			dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
			goto add_adapter_failed;
		}
	}

	dev_dbg(&pdev->dev, "%d port mux on %s adapter\n",
232
		 mux->data.n_values, muxc->parent->name);
233 234 235 236

	return 0;

add_adapter_failed:
237
	i2c_mux_del_adapters(muxc);
238 239 240 241 242 243

	return ret;
}

static int i2c_mux_reg_remove(struct platform_device *pdev)
{
244
	struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
245

246 247
	i2c_mux_del_adapters(muxc);
	i2c_put_adapter(muxc->parent);
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271

	return 0;
}

static const struct of_device_id i2c_mux_reg_of_match[] = {
	{ .compatible = "i2c-mux-reg", },
	{},
};
MODULE_DEVICE_TABLE(of, i2c_mux_reg_of_match);

static struct platform_driver i2c_mux_reg_driver = {
	.probe	= i2c_mux_reg_probe,
	.remove	= i2c_mux_reg_remove,
	.driver	= {
		.name	= "i2c-mux-reg",
	},
};

module_platform_driver(i2c_mux_reg_driver);

MODULE_DESCRIPTION("Register-based I2C multiplexer driver");
MODULE_AUTHOR("York Sun <yorksun@freescale.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:i2c-mux-reg");