1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * SPI access driver for TI TPS6594/TPS6593/LP8764 PMICs |
4 | * |
5 | * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ |
6 | */ |
7 | |
8 | #include <linux/crc8.h> |
9 | #include <linux/module.h> |
10 | #include <linux/mod_devicetable.h> |
11 | #include <linux/of_device.h> |
12 | #include <linux/regmap.h> |
13 | #include <linux/spi/spi.h> |
14 | |
15 | #include <linux/mfd/tps6594.h> |
16 | |
17 | #define TPS6594_SPI_PAGE_SHIFT 5 |
18 | #define TPS6594_SPI_READ_BIT BIT(4) |
19 | |
20 | static bool enable_crc; |
21 | module_param(enable_crc, bool, 0444); |
22 | MODULE_PARM_DESC(enable_crc, "Enable CRC feature for SPI interface" ); |
23 | |
24 | DECLARE_CRC8_TABLE(tps6594_spi_crc_table); |
25 | |
26 | static int tps6594_spi_reg_read(void *context, unsigned int reg, unsigned int *val) |
27 | { |
28 | struct spi_device *spi = context; |
29 | struct tps6594 *tps = spi_get_drvdata(spi); |
30 | u8 buf[4] = { 0 }; |
31 | size_t count_rx = 1; |
32 | int ret; |
33 | |
34 | buf[0] = reg; |
35 | buf[1] = TPS6594_REG_TO_PAGE(reg) << TPS6594_SPI_PAGE_SHIFT | TPS6594_SPI_READ_BIT; |
36 | |
37 | if (tps->use_crc) |
38 | count_rx++; |
39 | |
40 | ret = spi_write_then_read(spi, txbuf: buf, n_tx: 2, rxbuf: buf + 2, n_rx: count_rx); |
41 | if (ret < 0) |
42 | return ret; |
43 | |
44 | if (tps->use_crc && buf[3] != crc8(table: tps6594_spi_crc_table, pdata: buf, nbytes: 3, CRC8_INIT_VALUE)) |
45 | return -EIO; |
46 | |
47 | *val = buf[2]; |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static int tps6594_spi_reg_write(void *context, unsigned int reg, unsigned int val) |
53 | { |
54 | struct spi_device *spi = context; |
55 | struct tps6594 *tps = spi_get_drvdata(spi); |
56 | u8 buf[4] = { 0 }; |
57 | size_t count = 3; |
58 | |
59 | buf[0] = reg; |
60 | buf[1] = TPS6594_REG_TO_PAGE(reg) << TPS6594_SPI_PAGE_SHIFT; |
61 | buf[2] = val; |
62 | |
63 | if (tps->use_crc) |
64 | buf[3] = crc8(table: tps6594_spi_crc_table, pdata: buf, nbytes: count++, CRC8_INIT_VALUE); |
65 | |
66 | return spi_write(spi, buf, len: count); |
67 | } |
68 | |
69 | static const struct regmap_config tps6594_spi_regmap_config = { |
70 | .reg_bits = 16, |
71 | .val_bits = 8, |
72 | .max_register = TPS6594_REG_DWD_FAIL_CNT_REG, |
73 | .volatile_reg = tps6594_is_volatile_reg, |
74 | .reg_read = tps6594_spi_reg_read, |
75 | .reg_write = tps6594_spi_reg_write, |
76 | .use_single_read = true, |
77 | .use_single_write = true, |
78 | }; |
79 | |
80 | static const struct of_device_id tps6594_spi_of_match_table[] = { |
81 | { .compatible = "ti,tps6594-q1" , .data = (void *)TPS6594, }, |
82 | { .compatible = "ti,tps6593-q1" , .data = (void *)TPS6593, }, |
83 | { .compatible = "ti,lp8764-q1" , .data = (void *)LP8764, }, |
84 | {} |
85 | }; |
86 | MODULE_DEVICE_TABLE(of, tps6594_spi_of_match_table); |
87 | |
88 | static int tps6594_spi_probe(struct spi_device *spi) |
89 | { |
90 | struct device *dev = &spi->dev; |
91 | struct tps6594 *tps; |
92 | const struct of_device_id *match; |
93 | |
94 | tps = devm_kzalloc(dev, size: sizeof(*tps), GFP_KERNEL); |
95 | if (!tps) |
96 | return -ENOMEM; |
97 | |
98 | spi_set_drvdata(spi, data: tps); |
99 | |
100 | tps->dev = dev; |
101 | tps->reg = spi_get_chipselect(spi, idx: 0); |
102 | tps->irq = spi->irq; |
103 | |
104 | tps->regmap = devm_regmap_init(dev, NULL, spi, &tps6594_spi_regmap_config); |
105 | if (IS_ERR(ptr: tps->regmap)) |
106 | return dev_err_probe(dev, err: PTR_ERR(ptr: tps->regmap), fmt: "Failed to init regmap\n" ); |
107 | |
108 | match = of_match_device(matches: tps6594_spi_of_match_table, dev); |
109 | if (!match) |
110 | return dev_err_probe(dev, err: -EINVAL, fmt: "Failed to find matching chip ID\n" ); |
111 | tps->chip_id = (unsigned long)match->data; |
112 | |
113 | crc8_populate_msb(table: tps6594_spi_crc_table, TPS6594_CRC8_POLYNOMIAL); |
114 | |
115 | return tps6594_device_init(tps, enable_crc); |
116 | } |
117 | |
118 | static struct spi_driver tps6594_spi_driver = { |
119 | .driver = { |
120 | .name = "tps6594" , |
121 | .of_match_table = tps6594_spi_of_match_table, |
122 | }, |
123 | .probe = tps6594_spi_probe, |
124 | }; |
125 | module_spi_driver(tps6594_spi_driver); |
126 | |
127 | MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>" ); |
128 | MODULE_DESCRIPTION("TPS6594 SPI Interface Driver" ); |
129 | MODULE_LICENSE("GPL" ); |
130 | |