162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * u8500 HWSEM driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010-2011 ST-Ericsson 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Implements u8500 semaphore handling for protocol 1, no interrupts. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Author: Mathieu Poirier <mathieu.poirier@linaro.org> 1062306a36Sopenharmony_ci * Heavily borrowed from the work of : 1162306a36Sopenharmony_ci * Simon Que <sque@ti.com> 1262306a36Sopenharmony_ci * Hari Kanigeri <h-kanigeri2@ti.com> 1362306a36Sopenharmony_ci * Ohad Ben-Cohen <ohad@wizery.com> 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/spinlock.h> 2162306a36Sopenharmony_ci#include <linux/hwspinlock.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "hwspinlock_internal.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * Implementation of STE's HSem protocol 1 without interrutps. 2862306a36Sopenharmony_ci * The only masterID we allow is '0x01' to force people to use 2962306a36Sopenharmony_ci * HSems for synchronisation between processors rather than processes 3062306a36Sopenharmony_ci * on the ARM core. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define U8500_MAX_SEMAPHORE 32 /* a total of 32 semaphore */ 3462306a36Sopenharmony_ci#define RESET_SEMAPHORE (0) /* free */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * CPU ID for master running u8500 kernel. 3862306a36Sopenharmony_ci * Hswpinlocks should only be used to synchonise operations 3962306a36Sopenharmony_ci * between the Cortex A9 core and the other CPUs. Hence 4062306a36Sopenharmony_ci * forcing the masterID to a preset value. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci#define HSEM_MASTER_ID 0x01 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define HSEM_REGISTER_OFFSET 0x08 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define HSEM_CTRL_REG 0x00 4762306a36Sopenharmony_ci#define HSEM_ICRALL 0x90 4862306a36Sopenharmony_ci#define HSEM_PROTOCOL_1 0x01 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int u8500_hsem_trylock(struct hwspinlock *lock) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci void __iomem *lock_addr = lock->priv; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci writel(HSEM_MASTER_ID, lock_addr); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* get only first 4 bit and compare to masterID. 5762306a36Sopenharmony_ci * if equal, we have the semaphore, otherwise 5862306a36Sopenharmony_ci * someone else has it. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci return (HSEM_MASTER_ID == (0x0F & readl(lock_addr))); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void u8500_hsem_unlock(struct hwspinlock *lock) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci void __iomem *lock_addr = lock->priv; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* release the lock by writing 0 to it */ 6862306a36Sopenharmony_ci writel(RESET_SEMAPHORE, lock_addr); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * u8500: what value is recommended here ? 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistatic void u8500_hsem_relax(struct hwspinlock *lock) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci ndelay(50); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic const struct hwspinlock_ops u8500_hwspinlock_ops = { 8062306a36Sopenharmony_ci .trylock = u8500_hsem_trylock, 8162306a36Sopenharmony_ci .unlock = u8500_hsem_unlock, 8262306a36Sopenharmony_ci .relax = u8500_hsem_relax, 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int u8500_hsem_probe(struct platform_device *pdev) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct hwspinlock_pdata *pdata = pdev->dev.platform_data; 8862306a36Sopenharmony_ci struct hwspinlock_device *bank; 8962306a36Sopenharmony_ci struct hwspinlock *hwlock; 9062306a36Sopenharmony_ci void __iomem *io_base; 9162306a36Sopenharmony_ci int i, num_locks = U8500_MAX_SEMAPHORE; 9262306a36Sopenharmony_ci ulong val; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (!pdata) 9562306a36Sopenharmony_ci return -ENODEV; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci io_base = devm_platform_ioremap_resource(pdev, 0); 9862306a36Sopenharmony_ci if (IS_ERR(io_base)) 9962306a36Sopenharmony_ci return PTR_ERR(io_base); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* make sure protocol 1 is selected */ 10262306a36Sopenharmony_ci val = readl(io_base + HSEM_CTRL_REG); 10362306a36Sopenharmony_ci writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* clear all interrupts */ 10662306a36Sopenharmony_ci writel(0xFFFF, io_base + HSEM_ICRALL); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci bank = devm_kzalloc(&pdev->dev, struct_size(bank, lock, num_locks), 10962306a36Sopenharmony_ci GFP_KERNEL); 11062306a36Sopenharmony_ci if (!bank) 11162306a36Sopenharmony_ci return -ENOMEM; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci platform_set_drvdata(pdev, bank); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) 11662306a36Sopenharmony_ci hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return devm_hwspin_lock_register(&pdev->dev, bank, 11962306a36Sopenharmony_ci &u8500_hwspinlock_ops, 12062306a36Sopenharmony_ci pdata->base_id, num_locks); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void u8500_hsem_remove(struct platform_device *pdev) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct hwspinlock_device *bank = platform_get_drvdata(pdev); 12662306a36Sopenharmony_ci void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* clear all interrupts */ 12962306a36Sopenharmony_ci writel(0xFFFF, io_base + HSEM_ICRALL); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic struct platform_driver u8500_hsem_driver = { 13362306a36Sopenharmony_ci .probe = u8500_hsem_probe, 13462306a36Sopenharmony_ci .remove_new = u8500_hsem_remove, 13562306a36Sopenharmony_ci .driver = { 13662306a36Sopenharmony_ci .name = "u8500_hsem", 13762306a36Sopenharmony_ci }, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int __init u8500_hsem_init(void) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci return platform_driver_register(&u8500_hsem_driver); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci/* board init code might need to reserve hwspinlocks for predefined purposes */ 14562306a36Sopenharmony_cipostcore_initcall(u8500_hsem_init); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void __exit u8500_hsem_exit(void) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci platform_driver_unregister(&u8500_hsem_driver); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_cimodule_exit(u8500_hsem_exit); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 15462306a36Sopenharmony_ciMODULE_DESCRIPTION("Hardware Spinlock driver for u8500"); 15562306a36Sopenharmony_ciMODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>"); 156