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

pinctrl-mcp23s08_spi.c (6293B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* MCP23S08 SPI GPIO driver */
      3
      4#include <linux/mod_devicetable.h>
      5#include <linux/module.h>
      6#include <linux/property.h>
      7#include <linux/regmap.h>
      8#include <linux/spi/spi.h>
      9
     10#include "pinctrl-mcp23s08.h"
     11
     12#define MCP_MAX_DEV_PER_CS	8
     13
     14/*
     15 * A given spi_device can represent up to eight mcp23sxx chips
     16 * sharing the same chipselect but using different addresses
     17 * (e.g. chips #0 and #3 might be populated, but not #1 or #2).
     18 * Driver data holds all the per-chip data.
     19 */
     20struct mcp23s08_driver_data {
     21	unsigned		ngpio;
     22	struct mcp23s08		*mcp[8];
     23	struct mcp23s08		chip[];
     24};
     25
     26static int mcp23sxx_spi_write(void *context, const void *data, size_t count)
     27{
     28	struct mcp23s08 *mcp = context;
     29	struct spi_device *spi = to_spi_device(mcp->dev);
     30	struct spi_message m;
     31	struct spi_transfer t[2] = { { .tx_buf = &mcp->addr, .len = 1, },
     32				     { .tx_buf = data, .len = count, }, };
     33
     34	spi_message_init(&m);
     35	spi_message_add_tail(&t[0], &m);
     36	spi_message_add_tail(&t[1], &m);
     37
     38	return spi_sync(spi, &m);
     39}
     40
     41static int mcp23sxx_spi_gather_write(void *context,
     42				const void *reg, size_t reg_size,
     43				const void *val, size_t val_size)
     44{
     45	struct mcp23s08 *mcp = context;
     46	struct spi_device *spi = to_spi_device(mcp->dev);
     47	struct spi_message m;
     48	struct spi_transfer t[3] = { { .tx_buf = &mcp->addr, .len = 1, },
     49				     { .tx_buf = reg, .len = reg_size, },
     50				     { .tx_buf = val, .len = val_size, }, };
     51
     52	spi_message_init(&m);
     53	spi_message_add_tail(&t[0], &m);
     54	spi_message_add_tail(&t[1], &m);
     55	spi_message_add_tail(&t[2], &m);
     56
     57	return spi_sync(spi, &m);
     58}
     59
     60static int mcp23sxx_spi_read(void *context, const void *reg, size_t reg_size,
     61				void *val, size_t val_size)
     62{
     63	struct mcp23s08 *mcp = context;
     64	struct spi_device *spi = to_spi_device(mcp->dev);
     65	u8 tx[2];
     66
     67	if (reg_size != 1)
     68		return -EINVAL;
     69
     70	tx[0] = mcp->addr | 0x01;
     71	tx[1] = *((u8 *) reg);
     72
     73	return spi_write_then_read(spi, tx, sizeof(tx), val, val_size);
     74}
     75
     76static const struct regmap_bus mcp23sxx_spi_regmap = {
     77	.write = mcp23sxx_spi_write,
     78	.gather_write = mcp23sxx_spi_gather_write,
     79	.read = mcp23sxx_spi_read,
     80};
     81
     82static int mcp23s08_spi_regmap_init(struct mcp23s08 *mcp, struct device *dev,
     83				    unsigned int addr, unsigned int type)
     84{
     85	const struct regmap_config *config;
     86	struct regmap_config *copy;
     87	const char *name;
     88
     89	switch (type) {
     90	case MCP_TYPE_S08:
     91		mcp->reg_shift = 0;
     92		mcp->chip.ngpio = 8;
     93		mcp->chip.label = devm_kasprintf(dev, GFP_KERNEL, "mcp23s08.%d", addr);
     94
     95		config = &mcp23x08_regmap;
     96		name = devm_kasprintf(dev, GFP_KERNEL, "%d", addr);
     97		break;
     98
     99	case MCP_TYPE_S17:
    100		mcp->reg_shift = 1;
    101		mcp->chip.ngpio = 16;
    102		mcp->chip.label = devm_kasprintf(dev, GFP_KERNEL, "mcp23s17.%d", addr);
    103
    104		config = &mcp23x17_regmap;
    105		name = devm_kasprintf(dev, GFP_KERNEL, "%d", addr);
    106		break;
    107
    108	case MCP_TYPE_S18:
    109		mcp->reg_shift = 1;
    110		mcp->chip.ngpio = 16;
    111		mcp->chip.label = "mcp23s18";
    112
    113		config = &mcp23x17_regmap;
    114		name = config->name;
    115		break;
    116
    117	default:
    118		dev_err(dev, "invalid device type (%d)\n", type);
    119		return -EINVAL;
    120	}
    121
    122	copy = devm_kmemdup(dev, config, sizeof(*config), GFP_KERNEL);
    123	if (!copy)
    124		return -ENOMEM;
    125
    126	copy->name = name;
    127
    128	mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, copy);
    129	if (IS_ERR(mcp->regmap))
    130		dev_err(dev, "regmap init failed for %s\n", mcp->chip.label);
    131	return PTR_ERR_OR_ZERO(mcp->regmap);
    132}
    133
    134static int mcp23s08_probe(struct spi_device *spi)
    135{
    136	struct device *dev = &spi->dev;
    137	struct mcp23s08_driver_data *data;
    138	unsigned long spi_present_mask;
    139	const void *match;
    140	unsigned int addr;
    141	unsigned int ngpio = 0;
    142	int chips;
    143	int type;
    144	int ret;
    145	u32 v;
    146
    147	match = device_get_match_data(dev);
    148	if (match)
    149		type = (int)(uintptr_t)match;
    150	else
    151		type = spi_get_device_id(spi)->driver_data;
    152
    153	ret = device_property_read_u32(dev, "microchip,spi-present-mask", &v);
    154	if (ret) {
    155		ret = device_property_read_u32(dev, "mcp,spi-present-mask", &v);
    156		if (ret) {
    157			dev_err(dev, "missing spi-present-mask");
    158			return ret;
    159		}
    160	}
    161	spi_present_mask = v;
    162
    163	if (!spi_present_mask || spi_present_mask >= BIT(MCP_MAX_DEV_PER_CS)) {
    164		dev_err(dev, "invalid spi-present-mask");
    165		return -ENODEV;
    166	}
    167
    168	chips = hweight_long(spi_present_mask);
    169
    170	data = devm_kzalloc(dev, struct_size(data, chip, chips), GFP_KERNEL);
    171	if (!data)
    172		return -ENOMEM;
    173
    174	spi_set_drvdata(spi, data);
    175
    176	for_each_set_bit(addr, &spi_present_mask, MCP_MAX_DEV_PER_CS) {
    177		data->mcp[addr] = &data->chip[--chips];
    178		data->mcp[addr]->irq = spi->irq;
    179
    180		ret = mcp23s08_spi_regmap_init(data->mcp[addr], dev, addr, type);
    181		if (ret)
    182			return ret;
    183
    184		data->mcp[addr]->pinctrl_desc.name = devm_kasprintf(dev, GFP_KERNEL,
    185								    "mcp23xxx-pinctrl.%d",
    186								    addr);
    187		if (!data->mcp[addr]->pinctrl_desc.name)
    188			return -ENOMEM;
    189
    190		ret = mcp23s08_probe_one(data->mcp[addr], dev, 0x40 | (addr << 1), type, -1);
    191		if (ret < 0)
    192			return ret;
    193
    194		ngpio += data->mcp[addr]->chip.ngpio;
    195	}
    196	data->ngpio = ngpio;
    197
    198	return 0;
    199}
    200
    201static const struct spi_device_id mcp23s08_ids[] = {
    202	{ "mcp23s08", MCP_TYPE_S08 },
    203	{ "mcp23s17", MCP_TYPE_S17 },
    204	{ "mcp23s18", MCP_TYPE_S18 },
    205	{ }
    206};
    207MODULE_DEVICE_TABLE(spi, mcp23s08_ids);
    208
    209static const struct of_device_id mcp23s08_spi_of_match[] = {
    210	{
    211		.compatible = "microchip,mcp23s08",
    212		.data = (void *) MCP_TYPE_S08,
    213	},
    214	{
    215		.compatible = "microchip,mcp23s17",
    216		.data = (void *) MCP_TYPE_S17,
    217	},
    218	{
    219		.compatible = "microchip,mcp23s18",
    220		.data = (void *) MCP_TYPE_S18,
    221	},
    222/* NOTE: The use of the mcp prefix is deprecated and will be removed. */
    223	{
    224		.compatible = "mcp,mcp23s08",
    225		.data = (void *) MCP_TYPE_S08,
    226	},
    227	{
    228		.compatible = "mcp,mcp23s17",
    229		.data = (void *) MCP_TYPE_S17,
    230	},
    231	{ }
    232};
    233MODULE_DEVICE_TABLE(of, mcp23s08_spi_of_match);
    234
    235static struct spi_driver mcp23s08_driver = {
    236	.probe		= mcp23s08_probe,
    237	.id_table	= mcp23s08_ids,
    238	.driver = {
    239		.name	= "mcp23s08",
    240		.of_match_table = mcp23s08_spi_of_match,
    241	},
    242};
    243
    244static int __init mcp23s08_spi_init(void)
    245{
    246	return spi_register_driver(&mcp23s08_driver);
    247}
    248
    249/*
    250 * Register after SPI postcore initcall and before
    251 * subsys initcalls that may rely on these GPIOs.
    252 */
    253subsys_initcall(mcp23s08_spi_init);
    254
    255static void mcp23s08_spi_exit(void)
    256{
    257	spi_unregister_driver(&mcp23s08_driver);
    258}
    259module_exit(mcp23s08_spi_exit);
    260
    261MODULE_LICENSE("GPL");