| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * Copyright (C) 2019 MediaTek Inc. | 
 |  * Author: Argus Lin <argus.lin@mediatek.com> | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/module.h> | 
 | #include <linux/of_platform.h> | 
 | #include <linux/regmap.h> | 
 | #include <linux/spmi.h> | 
 |  | 
 | struct mtk_spmi_pmic_data { | 
 | 	unsigned int max_register; | 
 | 	unsigned int check_reg; | 
 | }; | 
 |  | 
 | #define MTK_SPMI_PMIC_DATA(chip, _max_register, _check_reg)	\ | 
 | static const struct mtk_spmi_pmic_data chip##_data =		\ | 
 | {						\ | 
 | 	.max_register = _max_register,		\ | 
 | 	.check_reg = _check_reg,		\ | 
 | }						\ | 
 |  | 
 | MTK_SPMI_PMIC_DATA(common, 0xFFFF, 0x39E); | 
 | MTK_SPMI_PMIC_DATA(mt6315, 0x16D0, 0x3A5); | 
 | MTK_SPMI_PMIC_DATA(mt6330, 0x2300, 0x39E); | 
 |  | 
 | static const struct of_device_id mtk_spmi_pmic_id_table[] = { | 
 | 	{ .compatible = "mtk,spmi-pmic", .data = &common_data }, | 
 | 	{ .compatible = "mediatek,mt6315", .data = &mt6315_data }, | 
 | 	{ .compatible = "mediatek,mt6330_fpga", .data = &mt6330_data }, | 
 | 	{ } | 
 | }; | 
 |  | 
 | static int mtk_spmi_pmic_rw_test(struct regmap *map, | 
 | 				 const struct mtk_spmi_pmic_data *data) | 
 | { | 
 | 	unsigned int rdata = 0, backup; | 
 | 	u8 wdata[] = {0xAB, 0xCD}; | 
 | 	int i, ret; | 
 |  | 
 | 	ret = regmap_read(map, data->check_reg, &rdata); | 
 | 	if (ret) | 
 | 		return -EIO; | 
 | 	backup = rdata; | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(wdata); i++) { | 
 | 		ret = regmap_write(map, data->check_reg, wdata[i]); | 
 | 		ret |= regmap_read(map, data->check_reg, &rdata); | 
 | 		if (ret || (rdata != wdata[i])) { | 
 | 			pr_notice("%s fail, rdata(0x%x)!=wdata(0x%x)\n", | 
 | 				  __func__, rdata, wdata[i]); | 
 | 			return -EIO; | 
 | 		} | 
 | 	} | 
 | 	ret = regmap_write(map, data->check_reg, backup); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int mtk_spmi_pmic_probe(struct spmi_device *sdev) | 
 | { | 
 | 	struct regmap_config spmi_regmap_config; | 
 | 	struct regmap *regmap; | 
 | 	const struct of_device_id *match; | 
 | 	const struct mtk_spmi_pmic_data *data; | 
 | 	int ret; | 
 |  | 
 | 	match = of_match_device(mtk_spmi_pmic_id_table, &sdev->dev); | 
 | 	if (!match) | 
 | 		return -ENODEV; | 
 | 	data = (struct mtk_spmi_pmic_data *)match->data; | 
 | 	dev_notice(&sdev->dev, "%s usid:%d\n", match->compatible, sdev->usid); | 
 |  | 
 | 	memset(&spmi_regmap_config, 0, sizeof(struct regmap_config)); | 
 | 	spmi_regmap_config.reg_bits = 16; | 
 | 	spmi_regmap_config.val_bits = 8; | 
 | 	spmi_regmap_config.fast_io = true; | 
 | 	spmi_regmap_config.max_register = data->max_register; | 
 | 	regmap = devm_regmap_init_spmi_ext(sdev, &spmi_regmap_config); | 
 | 	if (IS_ERR(regmap)) | 
 | 		return PTR_ERR(regmap); | 
 | 	ret = mtk_spmi_pmic_rw_test(regmap, data); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	return devm_of_platform_populate(&sdev->dev); | 
 | } | 
 |  | 
 | MODULE_DEVICE_TABLE(of, mtk_spmi_pmic_id_table); | 
 |  | 
 | static struct spmi_driver mtk_spmi_pmic_driver = { | 
 | 	.probe = mtk_spmi_pmic_probe, | 
 | 	.driver = { | 
 | 		.name = "mtk-spmi-pmic", | 
 | 		.of_match_table = mtk_spmi_pmic_id_table, | 
 | 	}, | 
 | }; | 
 | module_spmi_driver(mtk_spmi_pmic_driver); | 
 |  | 
 | MODULE_DESCRIPTION("Mediatek SPMI PMIC driver"); | 
 | MODULE_ALIAS("spmi:spmi-pmic"); | 
 | MODULE_LICENSE("GPL v2"); | 
 | MODULE_AUTHOR("Argus Lin <argus.lin@mediatek.com>"); |