18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * u8500 HWSEM driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2011 ST-Ericsson 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Implements u8500 semaphore handling for protocol 1, no interrupts. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author: Mathieu Poirier <mathieu.poirier@linaro.org> 108c2ecf20Sopenharmony_ci * Heavily borrowed from the work of : 118c2ecf20Sopenharmony_ci * Simon Que <sque@ti.com> 128c2ecf20Sopenharmony_ci * Hari Kanigeri <h-kanigeri2@ti.com> 138c2ecf20Sopenharmony_ci * Ohad Ben-Cohen <ohad@wizery.com> 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 218c2ecf20Sopenharmony_ci#include <linux/hwspinlock.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "hwspinlock_internal.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * Implementation of STE's HSem protocol 1 without interrutps. 288c2ecf20Sopenharmony_ci * The only masterID we allow is '0x01' to force people to use 298c2ecf20Sopenharmony_ci * HSems for synchronisation between processors rather than processes 308c2ecf20Sopenharmony_ci * on the ARM core. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define U8500_MAX_SEMAPHORE 32 /* a total of 32 semaphore */ 348c2ecf20Sopenharmony_ci#define RESET_SEMAPHORE (0) /* free */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * CPU ID for master running u8500 kernel. 388c2ecf20Sopenharmony_ci * Hswpinlocks should only be used to synchonise operations 398c2ecf20Sopenharmony_ci * between the Cortex A9 core and the other CPUs. Hence 408c2ecf20Sopenharmony_ci * forcing the masterID to a preset value. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci#define HSEM_MASTER_ID 0x01 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define HSEM_REGISTER_OFFSET 0x08 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define HSEM_CTRL_REG 0x00 478c2ecf20Sopenharmony_ci#define HSEM_ICRALL 0x90 488c2ecf20Sopenharmony_ci#define HSEM_PROTOCOL_1 0x01 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int u8500_hsem_trylock(struct hwspinlock *lock) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci void __iomem *lock_addr = lock->priv; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci writel(HSEM_MASTER_ID, lock_addr); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* get only first 4 bit and compare to masterID. 578c2ecf20Sopenharmony_ci * if equal, we have the semaphore, otherwise 588c2ecf20Sopenharmony_ci * someone else has it. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci return (HSEM_MASTER_ID == (0x0F & readl(lock_addr))); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void u8500_hsem_unlock(struct hwspinlock *lock) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci void __iomem *lock_addr = lock->priv; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* release the lock by writing 0 to it */ 688c2ecf20Sopenharmony_ci writel(RESET_SEMAPHORE, lock_addr); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * u8500: what value is recommended here ? 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic void u8500_hsem_relax(struct hwspinlock *lock) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci ndelay(50); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const struct hwspinlock_ops u8500_hwspinlock_ops = { 808c2ecf20Sopenharmony_ci .trylock = u8500_hsem_trylock, 818c2ecf20Sopenharmony_ci .unlock = u8500_hsem_unlock, 828c2ecf20Sopenharmony_ci .relax = u8500_hsem_relax, 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int u8500_hsem_probe(struct platform_device *pdev) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct hwspinlock_pdata *pdata = pdev->dev.platform_data; 888c2ecf20Sopenharmony_ci struct hwspinlock_device *bank; 898c2ecf20Sopenharmony_ci struct hwspinlock *hwlock; 908c2ecf20Sopenharmony_ci void __iomem *io_base; 918c2ecf20Sopenharmony_ci int i, num_locks = U8500_MAX_SEMAPHORE; 928c2ecf20Sopenharmony_ci ulong val; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (!pdata) 958c2ecf20Sopenharmony_ci return -ENODEV; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci io_base = devm_platform_ioremap_resource(pdev, 0); 988c2ecf20Sopenharmony_ci if (IS_ERR(io_base)) 998c2ecf20Sopenharmony_ci return PTR_ERR(io_base); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* make sure protocol 1 is selected */ 1028c2ecf20Sopenharmony_ci val = readl(io_base + HSEM_CTRL_REG); 1038c2ecf20Sopenharmony_ci writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* clear all interrupts */ 1068c2ecf20Sopenharmony_ci writel(0xFFFF, io_base + HSEM_ICRALL); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci bank = devm_kzalloc(&pdev->dev, struct_size(bank, lock, num_locks), 1098c2ecf20Sopenharmony_ci GFP_KERNEL); 1108c2ecf20Sopenharmony_ci if (!bank) 1118c2ecf20Sopenharmony_ci return -ENOMEM; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, bank); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) 1168c2ecf20Sopenharmony_ci hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return devm_hwspin_lock_register(&pdev->dev, bank, 1198c2ecf20Sopenharmony_ci &u8500_hwspinlock_ops, 1208c2ecf20Sopenharmony_ci pdata->base_id, num_locks); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int u8500_hsem_remove(struct platform_device *pdev) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct hwspinlock_device *bank = platform_get_drvdata(pdev); 1268c2ecf20Sopenharmony_ci void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* clear all interrupts */ 1298c2ecf20Sopenharmony_ci writel(0xFFFF, io_base + HSEM_ICRALL); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic struct platform_driver u8500_hsem_driver = { 1358c2ecf20Sopenharmony_ci .probe = u8500_hsem_probe, 1368c2ecf20Sopenharmony_ci .remove = u8500_hsem_remove, 1378c2ecf20Sopenharmony_ci .driver = { 1388c2ecf20Sopenharmony_ci .name = "u8500_hsem", 1398c2ecf20Sopenharmony_ci }, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int __init u8500_hsem_init(void) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci return platform_driver_register(&u8500_hsem_driver); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci/* board init code might need to reserve hwspinlocks for predefined purposes */ 1478c2ecf20Sopenharmony_cipostcore_initcall(u8500_hsem_init); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void __exit u8500_hsem_exit(void) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci platform_driver_unregister(&u8500_hsem_driver); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_cimodule_exit(u8500_hsem_exit); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hardware Spinlock driver for u8500"); 1578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>"); 158