162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Khadas System control Microcontroller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2020 BayLibre SAS
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author(s): Neil Armstrong <narmstrong@baylibre.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/mfd/core.h>
1262306a36Sopenharmony_ci#include <linux/mfd/khadas-mcu.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/regmap.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic bool khadas_mcu_reg_volatile(struct device *dev, unsigned int reg)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	if (reg >= KHADAS_MCU_USER_DATA_0_REG &&
1962306a36Sopenharmony_ci	    reg < KHADAS_MCU_PWR_OFF_CMD_REG)
2062306a36Sopenharmony_ci		return true;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	switch (reg) {
2362306a36Sopenharmony_ci	case KHADAS_MCU_PWR_OFF_CMD_REG:
2462306a36Sopenharmony_ci	case KHADAS_MCU_PASSWD_START_REG:
2562306a36Sopenharmony_ci	case KHADAS_MCU_CHECK_VEN_PASSWD_REG:
2662306a36Sopenharmony_ci	case KHADAS_MCU_CHECK_USER_PASSWD_REG:
2762306a36Sopenharmony_ci	case KHADAS_MCU_WOL_INIT_START_REG:
2862306a36Sopenharmony_ci	case KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG:
2962306a36Sopenharmony_ci		return true;
3062306a36Sopenharmony_ci	default:
3162306a36Sopenharmony_ci		return false;
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic bool khadas_mcu_reg_writeable(struct device *dev, unsigned int reg)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	switch (reg) {
3862306a36Sopenharmony_ci	case KHADAS_MCU_PASSWD_VEN_0_REG:
3962306a36Sopenharmony_ci	case KHADAS_MCU_PASSWD_VEN_1_REG:
4062306a36Sopenharmony_ci	case KHADAS_MCU_PASSWD_VEN_2_REG:
4162306a36Sopenharmony_ci	case KHADAS_MCU_PASSWD_VEN_3_REG:
4262306a36Sopenharmony_ci	case KHADAS_MCU_PASSWD_VEN_4_REG:
4362306a36Sopenharmony_ci	case KHADAS_MCU_PASSWD_VEN_5_REG:
4462306a36Sopenharmony_ci	case KHADAS_MCU_MAC_0_REG:
4562306a36Sopenharmony_ci	case KHADAS_MCU_MAC_1_REG:
4662306a36Sopenharmony_ci	case KHADAS_MCU_MAC_2_REG:
4762306a36Sopenharmony_ci	case KHADAS_MCU_MAC_3_REG:
4862306a36Sopenharmony_ci	case KHADAS_MCU_MAC_4_REG:
4962306a36Sopenharmony_ci	case KHADAS_MCU_MAC_5_REG:
5062306a36Sopenharmony_ci	case KHADAS_MCU_USID_0_REG:
5162306a36Sopenharmony_ci	case KHADAS_MCU_USID_1_REG:
5262306a36Sopenharmony_ci	case KHADAS_MCU_USID_2_REG:
5362306a36Sopenharmony_ci	case KHADAS_MCU_USID_3_REG:
5462306a36Sopenharmony_ci	case KHADAS_MCU_USID_4_REG:
5562306a36Sopenharmony_ci	case KHADAS_MCU_USID_5_REG:
5662306a36Sopenharmony_ci	case KHADAS_MCU_VERSION_0_REG:
5762306a36Sopenharmony_ci	case KHADAS_MCU_VERSION_1_REG:
5862306a36Sopenharmony_ci	case KHADAS_MCU_DEVICE_NO_0_REG:
5962306a36Sopenharmony_ci	case KHADAS_MCU_DEVICE_NO_1_REG:
6062306a36Sopenharmony_ci	case KHADAS_MCU_FACTORY_TEST_REG:
6162306a36Sopenharmony_ci	case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG:
6262306a36Sopenharmony_ci		return false;
6362306a36Sopenharmony_ci	default:
6462306a36Sopenharmony_ci		return true;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic const struct regmap_config khadas_mcu_regmap_config = {
6962306a36Sopenharmony_ci	.reg_bits	= 8,
7062306a36Sopenharmony_ci	.reg_stride	= 1,
7162306a36Sopenharmony_ci	.val_bits	= 8,
7262306a36Sopenharmony_ci	.max_register	= KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
7362306a36Sopenharmony_ci	.volatile_reg	= khadas_mcu_reg_volatile,
7462306a36Sopenharmony_ci	.writeable_reg	= khadas_mcu_reg_writeable,
7562306a36Sopenharmony_ci	.cache_type	= REGCACHE_RBTREE,
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic struct mfd_cell khadas_mcu_fan_cells[] = {
7962306a36Sopenharmony_ci	/* VIM1/2 Rev13+ and VIM3 only */
8062306a36Sopenharmony_ci	{ .name = "khadas-mcu-fan-ctrl", },
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct mfd_cell khadas_mcu_cells[] = {
8462306a36Sopenharmony_ci	{ .name = "khadas-mcu-user-mem", },
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int khadas_mcu_probe(struct i2c_client *client)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct device *dev = &client->dev;
9062306a36Sopenharmony_ci	struct khadas_mcu *ddata;
9162306a36Sopenharmony_ci	int ret;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
9462306a36Sopenharmony_ci	if (!ddata)
9562306a36Sopenharmony_ci		return -ENOMEM;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	i2c_set_clientdata(client, ddata);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	ddata->dev = dev;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	ddata->regmap = devm_regmap_init_i2c(client, &khadas_mcu_regmap_config);
10262306a36Sopenharmony_ci	if (IS_ERR(ddata->regmap)) {
10362306a36Sopenharmony_ci		ret = PTR_ERR(ddata->regmap);
10462306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate register map: %d\n", ret);
10562306a36Sopenharmony_ci		return ret;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
10962306a36Sopenharmony_ci				   khadas_mcu_cells,
11062306a36Sopenharmony_ci				   ARRAY_SIZE(khadas_mcu_cells),
11162306a36Sopenharmony_ci				   NULL, 0, NULL);
11262306a36Sopenharmony_ci	if (ret)
11362306a36Sopenharmony_ci		return ret;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (of_property_present(dev->of_node, "#cooling-cells"))
11662306a36Sopenharmony_ci		return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
11762306a36Sopenharmony_ci					    khadas_mcu_fan_cells,
11862306a36Sopenharmony_ci					    ARRAY_SIZE(khadas_mcu_fan_cells),
11962306a36Sopenharmony_ci					    NULL, 0, NULL);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#ifdef CONFIG_OF
12562306a36Sopenharmony_cistatic const struct of_device_id khadas_mcu_of_match[] = {
12662306a36Sopenharmony_ci	{ .compatible = "khadas,mcu", },
12762306a36Sopenharmony_ci	{},
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, khadas_mcu_of_match);
13062306a36Sopenharmony_ci#endif
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct i2c_driver khadas_mcu_driver = {
13362306a36Sopenharmony_ci	.driver = {
13462306a36Sopenharmony_ci		.name = "khadas-mcu-core",
13562306a36Sopenharmony_ci		.of_match_table = of_match_ptr(khadas_mcu_of_match),
13662306a36Sopenharmony_ci	},
13762306a36Sopenharmony_ci	.probe = khadas_mcu_probe,
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_cimodule_i2c_driver(khadas_mcu_driver);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciMODULE_DESCRIPTION("Khadas MCU core driver");
14262306a36Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
14362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
144