^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * u8500 HWSEM driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2010-2011 ST-Ericsson
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Implements u8500 semaphore handling for protocol 1, no interrupts.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Heavily borrowed from the work of :
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * Simon Que <sque@ti.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * Hari Kanigeri <h-kanigeri2@ti.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * Ohad Ben-Cohen <ohad@wizery.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/hwspinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include "hwspinlock_internal.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * Implementation of STE's HSem protocol 1 without interrutps.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * The only masterID we allow is '0x01' to force people to use
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * HSems for synchronisation between processors rather than processes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * on the ARM core.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #define U8500_MAX_SEMAPHORE 32 /* a total of 32 semaphore */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define RESET_SEMAPHORE (0) /* free */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * CPU ID for master running u8500 kernel.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * Hswpinlocks should only be used to synchonise operations
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) * between the Cortex A9 core and the other CPUs. Hence
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) * forcing the masterID to a preset value.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #define HSEM_MASTER_ID 0x01
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #define HSEM_REGISTER_OFFSET 0x08
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #define HSEM_CTRL_REG 0x00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #define HSEM_ICRALL 0x90
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) #define HSEM_PROTOCOL_1 0x01
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) static int u8500_hsem_trylock(struct hwspinlock *lock)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) void __iomem *lock_addr = lock->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) writel(HSEM_MASTER_ID, lock_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) /* get only first 4 bit and compare to masterID.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) * if equal, we have the semaphore, otherwise
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) * someone else has it.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) return (HSEM_MASTER_ID == (0x0F & readl(lock_addr)));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) static void u8500_hsem_unlock(struct hwspinlock *lock)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) void __iomem *lock_addr = lock->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) /* release the lock by writing 0 to it */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) writel(RESET_SEMAPHORE, lock_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) * u8500: what value is recommended here ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) static void u8500_hsem_relax(struct hwspinlock *lock)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) ndelay(50);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) static const struct hwspinlock_ops u8500_hwspinlock_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) .trylock = u8500_hsem_trylock,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) .unlock = u8500_hsem_unlock,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) .relax = u8500_hsem_relax,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) static int u8500_hsem_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) struct hwspinlock_pdata *pdata = pdev->dev.platform_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) struct hwspinlock_device *bank;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) struct hwspinlock *hwlock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) void __iomem *io_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) int i, num_locks = U8500_MAX_SEMAPHORE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) ulong val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if (!pdata)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) io_base = devm_platform_ioremap_resource(pdev, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) if (IS_ERR(io_base))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) return PTR_ERR(io_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) /* make sure protocol 1 is selected */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) val = readl(io_base + HSEM_CTRL_REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) /* clear all interrupts */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) writel(0xFFFF, io_base + HSEM_ICRALL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) bank = devm_kzalloc(&pdev->dev, struct_size(bank, lock, num_locks),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if (!bank)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) platform_set_drvdata(pdev, bank);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) return devm_hwspin_lock_register(&pdev->dev, bank,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) &u8500_hwspinlock_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) pdata->base_id, num_locks);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) static int u8500_hsem_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) struct hwspinlock_device *bank = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) /* clear all interrupts */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) writel(0xFFFF, io_base + HSEM_ICRALL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) static struct platform_driver u8500_hsem_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .probe = u8500_hsem_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) .remove = u8500_hsem_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) .name = "u8500_hsem",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) static int __init u8500_hsem_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) return platform_driver_register(&u8500_hsem_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) /* board init code might need to reserve hwspinlocks for predefined purposes */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) postcore_initcall(u8500_hsem_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) static void __exit u8500_hsem_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) platform_driver_unregister(&u8500_hsem_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) module_exit(u8500_hsem_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) MODULE_DESCRIPTION("Hardware Spinlock driver for u8500");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");