xref: /openbmc/linux/drivers/mfd/tps6594-spi.c (revision cc5f2eb7)
1325bec71SJulien Panis // SPDX-License-Identifier: GPL-2.0
2325bec71SJulien Panis /*
3325bec71SJulien Panis  * SPI access driver for TI TPS6594/TPS6593/LP8764 PMICs
4325bec71SJulien Panis  *
5325bec71SJulien Panis  * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
6325bec71SJulien Panis  */
7325bec71SJulien Panis 
8325bec71SJulien Panis #include <linux/crc8.h>
9325bec71SJulien Panis #include <linux/module.h>
10325bec71SJulien Panis #include <linux/mod_devicetable.h>
11325bec71SJulien Panis #include <linux/of_device.h>
12325bec71SJulien Panis #include <linux/regmap.h>
13325bec71SJulien Panis #include <linux/spi/spi.h>
14325bec71SJulien Panis 
15325bec71SJulien Panis #include <linux/mfd/tps6594.h>
16325bec71SJulien Panis 
17325bec71SJulien Panis #define TPS6594_SPI_PAGE_SHIFT	5
18325bec71SJulien Panis #define TPS6594_SPI_READ_BIT	BIT(4)
19325bec71SJulien Panis 
20325bec71SJulien Panis static bool enable_crc;
21325bec71SJulien Panis module_param(enable_crc, bool, 0444);
22325bec71SJulien Panis MODULE_PARM_DESC(enable_crc, "Enable CRC feature for SPI interface");
23325bec71SJulien Panis 
24325bec71SJulien Panis DECLARE_CRC8_TABLE(tps6594_spi_crc_table);
25325bec71SJulien Panis 
tps6594_spi_reg_read(void * context,unsigned int reg,unsigned int * val)26325bec71SJulien Panis static int tps6594_spi_reg_read(void *context, unsigned int reg, unsigned int *val)
27325bec71SJulien Panis {
28325bec71SJulien Panis 	struct spi_device *spi = context;
29325bec71SJulien Panis 	struct tps6594 *tps = spi_get_drvdata(spi);
30325bec71SJulien Panis 	u8 buf[4] = { 0 };
31325bec71SJulien Panis 	size_t count_rx = 1;
32325bec71SJulien Panis 	int ret;
33325bec71SJulien Panis 
34325bec71SJulien Panis 	buf[0] = reg;
35325bec71SJulien Panis 	buf[1] = TPS6594_REG_TO_PAGE(reg) << TPS6594_SPI_PAGE_SHIFT | TPS6594_SPI_READ_BIT;
36325bec71SJulien Panis 
37325bec71SJulien Panis 	if (tps->use_crc)
38325bec71SJulien Panis 		count_rx++;
39325bec71SJulien Panis 
40325bec71SJulien Panis 	ret = spi_write_then_read(spi, buf, 2, buf + 2, count_rx);
41325bec71SJulien Panis 	if (ret < 0)
42325bec71SJulien Panis 		return ret;
43325bec71SJulien Panis 
44325bec71SJulien Panis 	if (tps->use_crc && buf[3] != crc8(tps6594_spi_crc_table, buf, 3, CRC8_INIT_VALUE))
45325bec71SJulien Panis 		return -EIO;
46325bec71SJulien Panis 
47325bec71SJulien Panis 	*val = buf[2];
48325bec71SJulien Panis 
49325bec71SJulien Panis 	return 0;
50325bec71SJulien Panis }
51325bec71SJulien Panis 
tps6594_spi_reg_write(void * context,unsigned int reg,unsigned int val)52325bec71SJulien Panis static int tps6594_spi_reg_write(void *context, unsigned int reg, unsigned int val)
53325bec71SJulien Panis {
54325bec71SJulien Panis 	struct spi_device *spi = context;
55325bec71SJulien Panis 	struct tps6594 *tps = spi_get_drvdata(spi);
56325bec71SJulien Panis 	u8 buf[4] = { 0 };
57325bec71SJulien Panis 	size_t count = 3;
58325bec71SJulien Panis 
59325bec71SJulien Panis 	buf[0] = reg;
60325bec71SJulien Panis 	buf[1] = TPS6594_REG_TO_PAGE(reg) << TPS6594_SPI_PAGE_SHIFT;
61325bec71SJulien Panis 	buf[2] = val;
62325bec71SJulien Panis 
63325bec71SJulien Panis 	if (tps->use_crc)
64325bec71SJulien Panis 		buf[3] = crc8(tps6594_spi_crc_table, buf, count++, CRC8_INIT_VALUE);
65325bec71SJulien Panis 
66325bec71SJulien Panis 	return spi_write(spi, buf, count);
67325bec71SJulien Panis }
68325bec71SJulien Panis 
69325bec71SJulien Panis static const struct regmap_config tps6594_spi_regmap_config = {
70325bec71SJulien Panis 	.reg_bits = 16,
71325bec71SJulien Panis 	.val_bits = 8,
72325bec71SJulien Panis 	.max_register = TPS6594_REG_DWD_FAIL_CNT_REG,
73325bec71SJulien Panis 	.volatile_reg = tps6594_is_volatile_reg,
74325bec71SJulien Panis 	.reg_read = tps6594_spi_reg_read,
75325bec71SJulien Panis 	.reg_write = tps6594_spi_reg_write,
76325bec71SJulien Panis 	.use_single_read = true,
77325bec71SJulien Panis 	.use_single_write = true,
78325bec71SJulien Panis };
79325bec71SJulien Panis 
80325bec71SJulien Panis static const struct of_device_id tps6594_spi_of_match_table[] = {
81325bec71SJulien Panis 	{ .compatible = "ti,tps6594-q1", .data = (void *)TPS6594, },
82325bec71SJulien Panis 	{ .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, },
83325bec71SJulien Panis 	{ .compatible = "ti,lp8764-q1",  .data = (void *)LP8764,  },
84325bec71SJulien Panis 	{}
85325bec71SJulien Panis };
86325bec71SJulien Panis MODULE_DEVICE_TABLE(of, tps6594_spi_of_match_table);
87325bec71SJulien Panis 
tps6594_spi_probe(struct spi_device * spi)88325bec71SJulien Panis static int tps6594_spi_probe(struct spi_device *spi)
89325bec71SJulien Panis {
90325bec71SJulien Panis 	struct device *dev = &spi->dev;
91325bec71SJulien Panis 	struct tps6594 *tps;
92325bec71SJulien Panis 	const struct of_device_id *match;
93325bec71SJulien Panis 
94325bec71SJulien Panis 	tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL);
95325bec71SJulien Panis 	if (!tps)
96325bec71SJulien Panis 		return -ENOMEM;
97325bec71SJulien Panis 
98325bec71SJulien Panis 	spi_set_drvdata(spi, tps);
99325bec71SJulien Panis 
100325bec71SJulien Panis 	tps->dev = dev;
101325bec71SJulien Panis 	tps->reg = spi->chip_select;
102325bec71SJulien Panis 	tps->irq = spi->irq;
103325bec71SJulien Panis 
104325bec71SJulien Panis 	tps->regmap = devm_regmap_init(dev, NULL, spi, &tps6594_spi_regmap_config);
105325bec71SJulien Panis 	if (IS_ERR(tps->regmap))
106325bec71SJulien Panis 		return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n");
107325bec71SJulien Panis 
108325bec71SJulien Panis 	match = of_match_device(tps6594_spi_of_match_table, dev);
109325bec71SJulien Panis 	if (!match)
110*cc5f2eb7SDan Carpenter 		return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n");
111325bec71SJulien Panis 	tps->chip_id = (unsigned long)match->data;
112325bec71SJulien Panis 
113325bec71SJulien Panis 	crc8_populate_msb(tps6594_spi_crc_table, TPS6594_CRC8_POLYNOMIAL);
114325bec71SJulien Panis 
115325bec71SJulien Panis 	return tps6594_device_init(tps, enable_crc);
116325bec71SJulien Panis }
117325bec71SJulien Panis 
118325bec71SJulien Panis static struct spi_driver tps6594_spi_driver = {
119325bec71SJulien Panis 	.driver	= {
120325bec71SJulien Panis 		.name = "tps6594",
121325bec71SJulien Panis 		.of_match_table = tps6594_spi_of_match_table,
122325bec71SJulien Panis 	},
123325bec71SJulien Panis 	.probe = tps6594_spi_probe,
124325bec71SJulien Panis };
125325bec71SJulien Panis module_spi_driver(tps6594_spi_driver);
126325bec71SJulien Panis 
127325bec71SJulien Panis MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
128325bec71SJulien Panis MODULE_DESCRIPTION("TPS6594 SPI Interface Driver");
129325bec71SJulien Panis MODULE_LICENSE("GPL");
130