162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SPI access driver for TI TPS6594/TPS6593/LP8764 PMICs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/crc8.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1162306a36Sopenharmony_ci#include <linux/of_device.h> 1262306a36Sopenharmony_ci#include <linux/regmap.h> 1362306a36Sopenharmony_ci#include <linux/spi/spi.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/mfd/tps6594.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define TPS6594_SPI_PAGE_SHIFT 5 1862306a36Sopenharmony_ci#define TPS6594_SPI_READ_BIT BIT(4) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic bool enable_crc; 2162306a36Sopenharmony_cimodule_param(enable_crc, bool, 0444); 2262306a36Sopenharmony_ciMODULE_PARM_DESC(enable_crc, "Enable CRC feature for SPI interface"); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciDECLARE_CRC8_TABLE(tps6594_spi_crc_table); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int tps6594_spi_reg_read(void *context, unsigned int reg, unsigned int *val) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct spi_device *spi = context; 2962306a36Sopenharmony_ci struct tps6594 *tps = spi_get_drvdata(spi); 3062306a36Sopenharmony_ci u8 buf[4] = { 0 }; 3162306a36Sopenharmony_ci size_t count_rx = 1; 3262306a36Sopenharmony_ci int ret; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci buf[0] = reg; 3562306a36Sopenharmony_ci buf[1] = TPS6594_REG_TO_PAGE(reg) << TPS6594_SPI_PAGE_SHIFT | TPS6594_SPI_READ_BIT; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (tps->use_crc) 3862306a36Sopenharmony_ci count_rx++; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci ret = spi_write_then_read(spi, buf, 2, buf + 2, count_rx); 4162306a36Sopenharmony_ci if (ret < 0) 4262306a36Sopenharmony_ci return ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (tps->use_crc && buf[3] != crc8(tps6594_spi_crc_table, buf, 3, CRC8_INIT_VALUE)) 4562306a36Sopenharmony_ci return -EIO; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci *val = buf[2]; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int tps6594_spi_reg_write(void *context, unsigned int reg, unsigned int val) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct spi_device *spi = context; 5562306a36Sopenharmony_ci struct tps6594 *tps = spi_get_drvdata(spi); 5662306a36Sopenharmony_ci u8 buf[4] = { 0 }; 5762306a36Sopenharmony_ci size_t count = 3; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci buf[0] = reg; 6062306a36Sopenharmony_ci buf[1] = TPS6594_REG_TO_PAGE(reg) << TPS6594_SPI_PAGE_SHIFT; 6162306a36Sopenharmony_ci buf[2] = val; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (tps->use_crc) 6462306a36Sopenharmony_ci buf[3] = crc8(tps6594_spi_crc_table, buf, count++, CRC8_INIT_VALUE); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return spi_write(spi, buf, count); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic const struct regmap_config tps6594_spi_regmap_config = { 7062306a36Sopenharmony_ci .reg_bits = 16, 7162306a36Sopenharmony_ci .val_bits = 8, 7262306a36Sopenharmony_ci .max_register = TPS6594_REG_DWD_FAIL_CNT_REG, 7362306a36Sopenharmony_ci .volatile_reg = tps6594_is_volatile_reg, 7462306a36Sopenharmony_ci .reg_read = tps6594_spi_reg_read, 7562306a36Sopenharmony_ci .reg_write = tps6594_spi_reg_write, 7662306a36Sopenharmony_ci .use_single_read = true, 7762306a36Sopenharmony_ci .use_single_write = true, 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic const struct of_device_id tps6594_spi_of_match_table[] = { 8162306a36Sopenharmony_ci { .compatible = "ti,tps6594-q1", .data = (void *)TPS6594, }, 8262306a36Sopenharmony_ci { .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, }, 8362306a36Sopenharmony_ci { .compatible = "ti,lp8764-q1", .data = (void *)LP8764, }, 8462306a36Sopenharmony_ci {} 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tps6594_spi_of_match_table); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int tps6594_spi_probe(struct spi_device *spi) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct device *dev = &spi->dev; 9162306a36Sopenharmony_ci struct tps6594 *tps; 9262306a36Sopenharmony_ci const struct of_device_id *match; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL); 9562306a36Sopenharmony_ci if (!tps) 9662306a36Sopenharmony_ci return -ENOMEM; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci spi_set_drvdata(spi, tps); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci tps->dev = dev; 10162306a36Sopenharmony_ci tps->reg = spi->chip_select; 10262306a36Sopenharmony_ci tps->irq = spi->irq; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci tps->regmap = devm_regmap_init(dev, NULL, spi, &tps6594_spi_regmap_config); 10562306a36Sopenharmony_ci if (IS_ERR(tps->regmap)) 10662306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n"); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci match = of_match_device(tps6594_spi_of_match_table, dev); 10962306a36Sopenharmony_ci if (!match) 11062306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n"); 11162306a36Sopenharmony_ci tps->chip_id = (unsigned long)match->data; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci crc8_populate_msb(tps6594_spi_crc_table, TPS6594_CRC8_POLYNOMIAL); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return tps6594_device_init(tps, enable_crc); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic struct spi_driver tps6594_spi_driver = { 11962306a36Sopenharmony_ci .driver = { 12062306a36Sopenharmony_ci .name = "tps6594", 12162306a36Sopenharmony_ci .of_match_table = tps6594_spi_of_match_table, 12262306a36Sopenharmony_ci }, 12362306a36Sopenharmony_ci .probe = tps6594_spi_probe, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_cimodule_spi_driver(tps6594_spi_driver); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciMODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>"); 12862306a36Sopenharmony_ciMODULE_DESCRIPTION("TPS6594 SPI Interface Driver"); 12962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 130