300 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * AMD Secure Processor driver4 *5 * Copyright (C) 2017-2018 Advanced Micro Devices, Inc.6 *7 * Author: Tom Lendacky <thomas.lendacky@amd.com>8 * Author: Gary R Hook <gary.hook@amd.com>9 * Author: Brijesh Singh <brijesh.singh@amd.com>10 */11 12#include <linux/module.h>13#include <linux/kernel.h>14#include <linux/kthread.h>15#include <linux/sched.h>16#include <linux/interrupt.h>17#include <linux/spinlock.h>18#include <linux/spinlock_types.h>19#include <linux/types.h>20#include <linux/ccp.h>21 22#include "ccp-dev.h"23#include "sp-dev.h"24 25MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");26MODULE_AUTHOR("Gary R Hook <gary.hook@amd.com>");27MODULE_LICENSE("GPL");28MODULE_VERSION("1.1.0");29MODULE_DESCRIPTION("AMD Secure Processor driver");30 31/* List of SPs, SP count, read-write access lock, and access functions32 *33 * Lock structure: get sp_unit_lock for reading whenever we need to34 * examine the SP list.35 */36static DEFINE_RWLOCK(sp_unit_lock);37static LIST_HEAD(sp_units);38 39/* Ever-increasing value to produce unique unit numbers */40static atomic_t sp_ordinal;41 42static void sp_add_device(struct sp_device *sp)43{44 unsigned long flags;45 46 write_lock_irqsave(&sp_unit_lock, flags);47 48 list_add_tail(&sp->entry, &sp_units);49 50 write_unlock_irqrestore(&sp_unit_lock, flags);51}52 53static void sp_del_device(struct sp_device *sp)54{55 unsigned long flags;56 57 write_lock_irqsave(&sp_unit_lock, flags);58 59 list_del(&sp->entry);60 61 write_unlock_irqrestore(&sp_unit_lock, flags);62}63 64static irqreturn_t sp_irq_handler(int irq, void *data)65{66 struct sp_device *sp = data;67 68 if (sp->ccp_irq_handler)69 sp->ccp_irq_handler(irq, sp->ccp_irq_data);70 71 if (sp->psp_irq_handler)72 sp->psp_irq_handler(irq, sp->psp_irq_data);73 74 return IRQ_HANDLED;75}76 77int sp_request_ccp_irq(struct sp_device *sp, irq_handler_t handler,78 const char *name, void *data)79{80 int ret;81 82 if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->psp_vdata) {83 /* Need a common routine to manage all interrupts */84 sp->ccp_irq_data = data;85 sp->ccp_irq_handler = handler;86 87 if (!sp->irq_registered) {88 ret = request_irq(sp->ccp_irq, sp_irq_handler, 0,89 sp->name, sp);90 if (ret)91 return ret;92 93 sp->irq_registered = true;94 }95 } else {96 /* Each sub-device can manage it's own interrupt */97 ret = request_irq(sp->ccp_irq, handler, 0, name, data);98 if (ret)99 return ret;100 }101 102 return 0;103}104 105int sp_request_psp_irq(struct sp_device *sp, irq_handler_t handler,106 const char *name, void *data)107{108 int ret;109 110 if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->ccp_vdata) {111 /* Need a common routine to manage all interrupts */112 sp->psp_irq_data = data;113 sp->psp_irq_handler = handler;114 115 if (!sp->irq_registered) {116 ret = request_irq(sp->psp_irq, sp_irq_handler, 0,117 sp->name, sp);118 if (ret)119 return ret;120 121 sp->irq_registered = true;122 }123 } else {124 /* Each sub-device can manage it's own interrupt */125 ret = request_irq(sp->psp_irq, handler, 0, name, data);126 if (ret)127 return ret;128 }129 130 return 0;131}132 133void sp_free_ccp_irq(struct sp_device *sp, void *data)134{135 if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->psp_vdata) {136 /* Using common routine to manage all interrupts */137 if (!sp->psp_irq_handler) {138 /* Nothing else using it, so free it */139 free_irq(sp->ccp_irq, sp);140 141 sp->irq_registered = false;142 }143 144 sp->ccp_irq_handler = NULL;145 sp->ccp_irq_data = NULL;146 } else {147 /* Each sub-device can manage it's own interrupt */148 free_irq(sp->ccp_irq, data);149 }150}151 152void sp_free_psp_irq(struct sp_device *sp, void *data)153{154 if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->ccp_vdata) {155 /* Using common routine to manage all interrupts */156 if (!sp->ccp_irq_handler) {157 /* Nothing else using it, so free it */158 free_irq(sp->psp_irq, sp);159 160 sp->irq_registered = false;161 }162 163 sp->psp_irq_handler = NULL;164 sp->psp_irq_data = NULL;165 } else {166 /* Each sub-device can manage it's own interrupt */167 free_irq(sp->psp_irq, data);168 }169}170 171/**172 * sp_alloc_struct - allocate and initialize the sp_device struct173 *174 * @dev: device struct of the SP175 */176struct sp_device *sp_alloc_struct(struct device *dev)177{178 struct sp_device *sp;179 180 sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);181 if (!sp)182 return NULL;183 184 sp->dev = dev;185 sp->ord = atomic_inc_return(&sp_ordinal);186 snprintf(sp->name, SP_MAX_NAME_LEN, "sp-%u", sp->ord);187 188 return sp;189}190 191int sp_init(struct sp_device *sp)192{193 sp_add_device(sp);194 195 if (sp->dev_vdata->ccp_vdata)196 ccp_dev_init(sp);197 198 if (sp->dev_vdata->psp_vdata)199 psp_dev_init(sp);200 return 0;201}202 203void sp_destroy(struct sp_device *sp)204{205 if (sp->dev_vdata->ccp_vdata)206 ccp_dev_destroy(sp);207 208 if (sp->dev_vdata->psp_vdata)209 psp_dev_destroy(sp);210 211 sp_del_device(sp);212}213 214int sp_suspend(struct sp_device *sp)215{216 if (sp->dev_vdata->ccp_vdata) {217 ccp_dev_suspend(sp);218 }219 220 return 0;221}222 223int sp_resume(struct sp_device *sp)224{225 if (sp->dev_vdata->ccp_vdata) {226 ccp_dev_resume(sp);227 }228 229 return 0;230}231 232struct sp_device *sp_get_psp_master_device(void)233{234 struct sp_device *i, *ret = NULL;235 unsigned long flags;236 237 write_lock_irqsave(&sp_unit_lock, flags);238 if (list_empty(&sp_units))239 goto unlock;240 241 list_for_each_entry(i, &sp_units, entry) {242 if (i->psp_data && i->get_psp_master_device) {243 ret = i->get_psp_master_device();244 break;245 }246 }247 248unlock:249 write_unlock_irqrestore(&sp_unit_lock, flags);250 return ret;251}252 253static int __init sp_mod_init(void)254{255#ifdef CONFIG_X86256 int ret;257 258 ret = sp_pci_init();259 if (ret)260 return ret;261 262#ifdef CONFIG_CRYPTO_DEV_SP_PSP263 psp_pci_init();264#endif265 266 return 0;267#endif268 269#ifdef CONFIG_ARM64270 int ret;271 272 ret = sp_platform_init();273 if (ret)274 return ret;275 276 return 0;277#endif278 279 return -ENODEV;280}281 282static void __exit sp_mod_exit(void)283{284#ifdef CONFIG_X86285 286#ifdef CONFIG_CRYPTO_DEV_SP_PSP287 psp_pci_exit();288#endif289 290 sp_pci_exit();291#endif292 293#ifdef CONFIG_ARM64294 sp_platform_exit();295#endif296}297 298module_init(sp_mod_init);299module_exit(sp_mod_exit);300