khadas-mcu.c (3674B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Driver for Khadas System control Microcontroller 4 * 5 * Copyright (C) 2020 BayLibre SAS 6 * 7 * Author(s): Neil Armstrong <narmstrong@baylibre.com> 8 */ 9#include <linux/bitfield.h> 10#include <linux/i2c.h> 11#include <linux/mfd/core.h> 12#include <linux/mfd/khadas-mcu.h> 13#include <linux/module.h> 14#include <linux/regmap.h> 15 16static bool khadas_mcu_reg_volatile(struct device *dev, unsigned int reg) 17{ 18 if (reg >= KHADAS_MCU_USER_DATA_0_REG && 19 reg < KHADAS_MCU_PWR_OFF_CMD_REG) 20 return true; 21 22 switch (reg) { 23 case KHADAS_MCU_PWR_OFF_CMD_REG: 24 case KHADAS_MCU_PASSWD_START_REG: 25 case KHADAS_MCU_CHECK_VEN_PASSWD_REG: 26 case KHADAS_MCU_CHECK_USER_PASSWD_REG: 27 case KHADAS_MCU_WOL_INIT_START_REG: 28 case KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG: 29 return true; 30 default: 31 return false; 32 } 33} 34 35static bool khadas_mcu_reg_writeable(struct device *dev, unsigned int reg) 36{ 37 switch (reg) { 38 case KHADAS_MCU_PASSWD_VEN_0_REG: 39 case KHADAS_MCU_PASSWD_VEN_1_REG: 40 case KHADAS_MCU_PASSWD_VEN_2_REG: 41 case KHADAS_MCU_PASSWD_VEN_3_REG: 42 case KHADAS_MCU_PASSWD_VEN_4_REG: 43 case KHADAS_MCU_PASSWD_VEN_5_REG: 44 case KHADAS_MCU_MAC_0_REG: 45 case KHADAS_MCU_MAC_1_REG: 46 case KHADAS_MCU_MAC_2_REG: 47 case KHADAS_MCU_MAC_3_REG: 48 case KHADAS_MCU_MAC_4_REG: 49 case KHADAS_MCU_MAC_5_REG: 50 case KHADAS_MCU_USID_0_REG: 51 case KHADAS_MCU_USID_1_REG: 52 case KHADAS_MCU_USID_2_REG: 53 case KHADAS_MCU_USID_3_REG: 54 case KHADAS_MCU_USID_4_REG: 55 case KHADAS_MCU_USID_5_REG: 56 case KHADAS_MCU_VERSION_0_REG: 57 case KHADAS_MCU_VERSION_1_REG: 58 case KHADAS_MCU_DEVICE_NO_0_REG: 59 case KHADAS_MCU_DEVICE_NO_1_REG: 60 case KHADAS_MCU_FACTORY_TEST_REG: 61 case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG: 62 return false; 63 default: 64 return true; 65 } 66} 67 68static const struct regmap_config khadas_mcu_regmap_config = { 69 .reg_bits = 8, 70 .reg_stride = 1, 71 .val_bits = 8, 72 .max_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, 73 .volatile_reg = khadas_mcu_reg_volatile, 74 .writeable_reg = khadas_mcu_reg_writeable, 75 .cache_type = REGCACHE_RBTREE, 76}; 77 78static struct mfd_cell khadas_mcu_fan_cells[] = { 79 /* VIM1/2 Rev13+ and VIM3 only */ 80 { .name = "khadas-mcu-fan-ctrl", }, 81}; 82 83static struct mfd_cell khadas_mcu_cells[] = { 84 { .name = "khadas-mcu-user-mem", }, 85}; 86 87static int khadas_mcu_probe(struct i2c_client *client, 88 const struct i2c_device_id *id) 89{ 90 struct device *dev = &client->dev; 91 struct khadas_mcu *ddata; 92 int ret; 93 94 ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); 95 if (!ddata) 96 return -ENOMEM; 97 98 i2c_set_clientdata(client, ddata); 99 100 ddata->dev = dev; 101 102 ddata->regmap = devm_regmap_init_i2c(client, &khadas_mcu_regmap_config); 103 if (IS_ERR(ddata->regmap)) { 104 ret = PTR_ERR(ddata->regmap); 105 dev_err(dev, "Failed to allocate register map: %d\n", ret); 106 return ret; 107 } 108 109 ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 110 khadas_mcu_cells, 111 ARRAY_SIZE(khadas_mcu_cells), 112 NULL, 0, NULL); 113 if (ret) 114 return ret; 115 116 if (of_find_property(dev->of_node, "#cooling-cells", NULL)) 117 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 118 khadas_mcu_fan_cells, 119 ARRAY_SIZE(khadas_mcu_fan_cells), 120 NULL, 0, NULL); 121 122 return 0; 123} 124 125#ifdef CONFIG_OF 126static const struct of_device_id khadas_mcu_of_match[] = { 127 { .compatible = "khadas,mcu", }, 128 {}, 129}; 130MODULE_DEVICE_TABLE(of, khadas_mcu_of_match); 131#endif 132 133static struct i2c_driver khadas_mcu_driver = { 134 .driver = { 135 .name = "khadas-mcu-core", 136 .of_match_table = of_match_ptr(khadas_mcu_of_match), 137 }, 138 .probe = khadas_mcu_probe, 139}; 140module_i2c_driver(khadas_mcu_driver); 141 142MODULE_DESCRIPTION("Khadas MCU core driver"); 143MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 144MODULE_LICENSE("GPL v2");