linux/drivers/firmware/meson/meson_sm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Amlogic Secure Monitor driver
   4 *
   5 * Copyright (C) 2016 Endless Mobile, Inc.
   6 * Author: Carlo Caione <carlo@endlessm.com>
   7 */
   8
   9#define pr_fmt(fmt) "meson-sm: " fmt
  10
  11#include <linux/arm-smccc.h>
  12#include <linux/bug.h>
  13#include <linux/io.h>
  14#include <linux/module.h>
  15#include <linux/of.h>
  16#include <linux/of_device.h>
  17#include <linux/platform_device.h>
  18#include <linux/printk.h>
  19#include <linux/types.h>
  20#include <linux/sizes.h>
  21 #include <linux/slab.h>
  22
  23#include <linux/firmware/meson/meson_sm.h>
  24
  25struct meson_sm_cmd {
  26        unsigned int index;
  27        u32 smc_id;
  28};
  29#define CMD(d, s) { .index = (d), .smc_id = (s), }
  30
  31struct meson_sm_chip {
  32        unsigned int shmem_size;
  33        u32 cmd_shmem_in_base;
  34        u32 cmd_shmem_out_base;
  35        struct meson_sm_cmd cmd[];
  36};
  37
  38static const struct meson_sm_chip gxbb_chip = {
  39        .shmem_size             = SZ_4K,
  40        .cmd_shmem_in_base      = 0x82000020,
  41        .cmd_shmem_out_base     = 0x82000021,
  42        .cmd = {
  43                CMD(SM_EFUSE_READ,      0x82000030),
  44                CMD(SM_EFUSE_WRITE,     0x82000031),
  45                CMD(SM_EFUSE_USER_MAX,  0x82000033),
  46                CMD(SM_GET_CHIP_ID,     0x82000044),
  47                CMD(SM_A1_PWRC_SET,     0x82000093),
  48                CMD(SM_A1_PWRC_GET,     0x82000095),
  49                { /* sentinel */ },
  50        },
  51};
  52
  53struct meson_sm_firmware {
  54        const struct meson_sm_chip *chip;
  55        void __iomem *sm_shmem_in_base;
  56        void __iomem *sm_shmem_out_base;
  57};
  58
  59static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip,
  60                            unsigned int cmd_index)
  61{
  62        const struct meson_sm_cmd *cmd = chip->cmd;
  63
  64        while (cmd->smc_id && cmd->index != cmd_index)
  65                cmd++;
  66
  67        return cmd->smc_id;
  68}
  69
  70static u32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2,
  71                           u32 arg3, u32 arg4)
  72{
  73        struct arm_smccc_res res;
  74
  75        arm_smccc_smc(cmd, arg0, arg1, arg2, arg3, arg4, 0, 0, &res);
  76        return res.a0;
  77}
  78
  79static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size)
  80{
  81        u32 sm_phy_base;
  82
  83        sm_phy_base = __meson_sm_call(cmd_shmem, 0, 0, 0, 0, 0);
  84        if (!sm_phy_base)
  85                return 0;
  86
  87        return ioremap_cache(sm_phy_base, size);
  88}
  89
  90/**
  91 * meson_sm_call - generic SMC32 call to the secure-monitor
  92 *
  93 * @fw:         Pointer to secure-monitor firmware
  94 * @cmd_index:  Index of the SMC32 function ID
  95 * @ret:        Returned value
  96 * @arg0:       SMC32 Argument 0
  97 * @arg1:       SMC32 Argument 1
  98 * @arg2:       SMC32 Argument 2
  99 * @arg3:       SMC32 Argument 3
 100 * @arg4:       SMC32 Argument 4
 101 *
 102 * Return:      0 on success, a negative value on error
 103 */
 104int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index,
 105                  u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
 106{
 107        u32 cmd, lret;
 108
 109        if (!fw->chip)
 110                return -ENOENT;
 111
 112        cmd = meson_sm_get_cmd(fw->chip, cmd_index);
 113        if (!cmd)
 114                return -EINVAL;
 115
 116        lret = __meson_sm_call(cmd, arg0, arg1, arg2, arg3, arg4);
 117
 118        if (ret)
 119                *ret = lret;
 120
 121        return 0;
 122}
 123EXPORT_SYMBOL(meson_sm_call);
 124
 125/**
 126 * meson_sm_call_read - retrieve data from secure-monitor
 127 *
 128 * @fw:         Pointer to secure-monitor firmware
 129 * @buffer:     Buffer to store the retrieved data
 130 * @bsize:      Size of the buffer
 131 * @cmd_index:  Index of the SMC32 function ID
 132 * @arg0:       SMC32 Argument 0
 133 * @arg1:       SMC32 Argument 1
 134 * @arg2:       SMC32 Argument 2
 135 * @arg3:       SMC32 Argument 3
 136 * @arg4:       SMC32 Argument 4
 137 *
 138 * Return:      size of read data on success, a negative value on error
 139 *              When 0 is returned there is no guarantee about the amount of
 140 *              data read and bsize bytes are copied in buffer.
 141 */
 142int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer,
 143                       unsigned int bsize, unsigned int cmd_index, u32 arg0,
 144                       u32 arg1, u32 arg2, u32 arg3, u32 arg4)
 145{
 146        u32 size;
 147        int ret;
 148
 149        if (!fw->chip)
 150                return -ENOENT;
 151
 152        if (!fw->chip->cmd_shmem_out_base)
 153                return -EINVAL;
 154
 155        if (bsize > fw->chip->shmem_size)
 156                return -EINVAL;
 157
 158        if (meson_sm_call(fw, cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0)
 159                return -EINVAL;
 160
 161        if (size > bsize)
 162                return -EINVAL;
 163
 164        ret = size;
 165
 166        if (!size)
 167                size = bsize;
 168
 169        if (buffer)
 170                memcpy(buffer, fw->sm_shmem_out_base, size);
 171
 172        return ret;
 173}
 174EXPORT_SYMBOL(meson_sm_call_read);
 175
 176/**
 177 * meson_sm_call_write - send data to secure-monitor
 178 *
 179 * @fw:         Pointer to secure-monitor firmware
 180 * @buffer:     Buffer containing data to send
 181 * @size:       Size of the data to send
 182 * @cmd_index:  Index of the SMC32 function ID
 183 * @arg0:       SMC32 Argument 0
 184 * @arg1:       SMC32 Argument 1
 185 * @arg2:       SMC32 Argument 2
 186 * @arg3:       SMC32 Argument 3
 187 * @arg4:       SMC32 Argument 4
 188 *
 189 * Return:      size of sent data on success, a negative value on error
 190 */
 191int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer,
 192                        unsigned int size, unsigned int cmd_index, u32 arg0,
 193                        u32 arg1, u32 arg2, u32 arg3, u32 arg4)
 194{
 195        u32 written;
 196
 197        if (!fw->chip)
 198                return -ENOENT;
 199
 200        if (size > fw->chip->shmem_size)
 201                return -EINVAL;
 202
 203        if (!fw->chip->cmd_shmem_in_base)
 204                return -EINVAL;
 205
 206        memcpy(fw->sm_shmem_in_base, buffer, size);
 207
 208        if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
 209                return -EINVAL;
 210
 211        if (!written)
 212                return -EINVAL;
 213
 214        return written;
 215}
 216EXPORT_SYMBOL(meson_sm_call_write);
 217
 218/**
 219 * meson_sm_get - get pointer to meson_sm_firmware structure.
 220 *
 221 * @sm_node:            Pointer to the secure-monitor Device Tree node.
 222 *
 223 * Return:              NULL is the secure-monitor device is not ready.
 224 */
 225struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node)
 226{
 227        struct platform_device *pdev = of_find_device_by_node(sm_node);
 228
 229        if (!pdev)
 230                return NULL;
 231
 232        return platform_get_drvdata(pdev);
 233}
 234EXPORT_SYMBOL_GPL(meson_sm_get);
 235
 236#define SM_CHIP_ID_LENGTH       119
 237#define SM_CHIP_ID_OFFSET       4
 238#define SM_CHIP_ID_SIZE         12
 239
 240static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
 241                         char *buf)
 242{
 243        struct platform_device *pdev = to_platform_device(dev);
 244        struct meson_sm_firmware *fw;
 245        uint8_t *id_buf;
 246        int ret;
 247
 248        fw = platform_get_drvdata(pdev);
 249
 250        id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL);
 251        if (!id_buf)
 252                return -ENOMEM;
 253
 254        ret = meson_sm_call_read(fw, id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID,
 255                                 0, 0, 0, 0, 0);
 256        if (ret < 0) {
 257                kfree(id_buf);
 258                return ret;
 259        }
 260
 261        ret = sprintf(buf, "%12phN\n", &id_buf[SM_CHIP_ID_OFFSET]);
 262
 263        kfree(id_buf);
 264
 265        return ret;
 266}
 267
 268static DEVICE_ATTR_RO(serial);
 269
 270static struct attribute *meson_sm_sysfs_attributes[] = {
 271        &dev_attr_serial.attr,
 272        NULL,
 273};
 274
 275static const struct attribute_group meson_sm_sysfs_attr_group = {
 276        .attrs = meson_sm_sysfs_attributes,
 277};
 278
 279static const struct of_device_id meson_sm_ids[] = {
 280        { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
 281        { /* sentinel */ },
 282};
 283
 284static int __init meson_sm_probe(struct platform_device *pdev)
 285{
 286        struct device *dev = &pdev->dev;
 287        const struct meson_sm_chip *chip;
 288        struct meson_sm_firmware *fw;
 289
 290        fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL);
 291        if (!fw)
 292                return -ENOMEM;
 293
 294        chip = of_match_device(meson_sm_ids, dev)->data;
 295
 296        if (chip->cmd_shmem_in_base) {
 297                fw->sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base,
 298                                                          chip->shmem_size);
 299                if (WARN_ON(!fw->sm_shmem_in_base))
 300                        goto out;
 301        }
 302
 303        if (chip->cmd_shmem_out_base) {
 304                fw->sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base,
 305                                                           chip->shmem_size);
 306                if (WARN_ON(!fw->sm_shmem_out_base))
 307                        goto out_in_base;
 308        }
 309
 310        fw->chip = chip;
 311
 312        platform_set_drvdata(pdev, fw);
 313
 314        pr_info("secure-monitor enabled\n");
 315
 316        if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
 317                goto out_in_base;
 318
 319        return 0;
 320
 321out_in_base:
 322        iounmap(fw->sm_shmem_in_base);
 323out:
 324        return -EINVAL;
 325}
 326
 327static struct platform_driver meson_sm_driver = {
 328        .driver = {
 329                .name = "meson-sm",
 330                .of_match_table = of_match_ptr(meson_sm_ids),
 331        },
 332};
 333module_platform_driver_probe(meson_sm_driver, meson_sm_probe);
 334MODULE_LICENSE("GPL v2");
 335