LCOV - code coverage report
Current view: top level - lib - logic_iomem.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 13 104 12.5 %
Date: 2023-08-24 13:40:31 Functions: 1 16 6.2 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * Copyright (C) 2021 Intel Corporation
       4             :  * Author: Johannes Berg <johannes@sipsolutions.net>
       5             :  */
       6             : #include <linux/types.h>
       7             : #include <linux/slab.h>
       8             : #include <linux/logic_iomem.h>
       9             : #include <asm/io.h>
      10             : 
      11             : struct logic_iomem_region {
      12             :         const struct resource *res;
      13             :         const struct logic_iomem_region_ops *ops;
      14             :         struct list_head list;
      15             : };
      16             : 
      17             : struct logic_iomem_area {
      18             :         const struct logic_iomem_ops *ops;
      19             :         void *priv;
      20             : };
      21             : 
      22             : #define AREA_SHIFT      24
      23             : #define MAX_AREA_SIZE   (1 << AREA_SHIFT)
      24             : #define MAX_AREAS       ((1U << 31) / MAX_AREA_SIZE)
      25             : #define AREA_BITS       ((MAX_AREAS - 1) << AREA_SHIFT)
      26             : #define AREA_MASK       (MAX_AREA_SIZE - 1)
      27             : #ifdef CONFIG_64BIT
      28             : #define IOREMAP_BIAS    0xDEAD000000000000UL
      29             : #define IOREMAP_MASK    0xFFFFFFFF00000000UL
      30             : #else
      31             : #define IOREMAP_BIAS    0x80000000UL
      32             : #define IOREMAP_MASK    0x80000000UL
      33             : #endif
      34             : 
      35             : static DEFINE_MUTEX(regions_mtx);
      36             : static LIST_HEAD(regions_list);
      37             : static struct logic_iomem_area mapped_areas[MAX_AREAS];
      38             : 
      39           3 : int logic_iomem_add_region(struct resource *resource,
      40             :                            const struct logic_iomem_region_ops *ops)
      41             : {
      42             :         struct logic_iomem_region *rreg;
      43             :         int err;
      44             : 
      45           3 :         if (WARN_ON(!resource || !ops))
      46             :                 return -EINVAL;
      47             : 
      48           3 :         if (WARN_ON((resource->flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM))
      49             :                 return -EINVAL;
      50             : 
      51           3 :         rreg = kzalloc(sizeof(*rreg), GFP_KERNEL);
      52           3 :         if (!rreg)
      53             :                 return -ENOMEM;
      54             : 
      55           3 :         err = request_resource(&iomem_resource, resource);
      56           3 :         if (err) {
      57           0 :                 kfree(rreg);
      58           0 :                 return -ENOMEM;
      59             :         }
      60             : 
      61           3 :         mutex_lock(&regions_mtx);
      62           3 :         rreg->res = resource;
      63           3 :         rreg->ops = ops;
      64           6 :         list_add_tail(&rreg->list, &regions_list);
      65           3 :         mutex_unlock(&regions_mtx);
      66             : 
      67           3 :         return 0;
      68             : }
      69             : EXPORT_SYMBOL(logic_iomem_add_region);
      70             : 
      71             : #ifndef CONFIG_INDIRECT_IOMEM_FALLBACK
      72             : static void __iomem *real_ioremap(phys_addr_t offset, size_t size)
      73             : {
      74           0 :         WARN(1, "invalid ioremap(0x%llx, 0x%zx)\n",
      75             :              (unsigned long long)offset, size);
      76             :         return NULL;
      77             : }
      78             : 
      79             : static void real_iounmap(volatile void __iomem *addr)
      80             : {
      81           0 :         WARN(1, "invalid iounmap for addr 0x%llx\n",
      82             :              (unsigned long long)(uintptr_t __force)addr);
      83             : }
      84             : #endif /* CONFIG_INDIRECT_IOMEM_FALLBACK */
      85             : 
      86           0 : void __iomem *ioremap(phys_addr_t offset, size_t size)
      87             : {
      88           0 :         void __iomem *ret = NULL;
      89           0 :         struct logic_iomem_region *rreg, *found = NULL;
      90             :         int i;
      91             : 
      92           0 :         mutex_lock(&regions_mtx);
      93           0 :         list_for_each_entry(rreg, &regions_list, list) {
      94           0 :                 if (rreg->res->start > offset)
      95           0 :                         continue;
      96           0 :                 if (rreg->res->end < offset + size - 1)
      97           0 :                         continue;
      98             :                 found = rreg;
      99             :                 break;
     100             :         }
     101             : 
     102           0 :         if (!found)
     103             :                 goto out;
     104             : 
     105           0 :         for (i = 0; i < MAX_AREAS; i++) {
     106             :                 long offs;
     107             : 
     108           0 :                 if (mapped_areas[i].ops)
     109           0 :                         continue;
     110             : 
     111           0 :                 offs = rreg->ops->map(offset - found->res->start,
     112             :                                       size, &mapped_areas[i].ops,
     113             :                                       &mapped_areas[i].priv);
     114           0 :                 if (offs < 0) {
     115           0 :                         mapped_areas[i].ops = NULL;
     116           0 :                         break;
     117             :                 }
     118             : 
     119           0 :                 if (WARN_ON(!mapped_areas[i].ops)) {
     120           0 :                         mapped_areas[i].ops = NULL;
     121           0 :                         break;
     122             :                 }
     123             : 
     124           0 :                 ret = (void __iomem *)(IOREMAP_BIAS + (i << AREA_SHIFT) + offs);
     125           0 :                 break;
     126             :         }
     127             : out:
     128           0 :         mutex_unlock(&regions_mtx);
     129           0 :         if (ret)
     130             :                 return ret;
     131           0 :         return real_ioremap(offset, size);
     132             : }
     133             : EXPORT_SYMBOL(ioremap);
     134             : 
     135             : static inline struct logic_iomem_area *
     136           0 : get_area(const volatile void __iomem *addr)
     137             : {
     138           0 :         unsigned long a = (unsigned long)addr;
     139             :         unsigned int idx;
     140             : 
     141           0 :         if (WARN_ON((a & IOREMAP_MASK) != IOREMAP_BIAS))
     142             :                 return NULL;
     143             : 
     144           0 :         idx = (a & AREA_BITS) >> AREA_SHIFT;
     145             : 
     146           0 :         if (mapped_areas[idx].ops)
     147           0 :                 return &mapped_areas[idx];
     148             : 
     149             :         return NULL;
     150             : }
     151             : 
     152           0 : void iounmap(volatile void __iomem *addr)
     153             : {
     154           0 :         struct logic_iomem_area *area = get_area(addr);
     155             : 
     156           0 :         if (!area) {
     157             :                 real_iounmap(addr);
     158             :                 return;
     159             :         }
     160             : 
     161           0 :         if (area->ops->unmap)
     162           0 :                 area->ops->unmap(area->priv);
     163             : 
     164           0 :         mutex_lock(&regions_mtx);
     165           0 :         area->ops = NULL;
     166           0 :         area->priv = NULL;
     167           0 :         mutex_unlock(&regions_mtx);
     168             : }
     169             : EXPORT_SYMBOL(iounmap);
     170             : 
     171             : #ifndef CONFIG_INDIRECT_IOMEM_FALLBACK
     172             : #define MAKE_FALLBACK(op, sz)                                           \
     173             : static u##sz real_raw_read ## op(const volatile void __iomem *addr)     \
     174             : {                                                                       \
     175             :         WARN(1, "Invalid read" #op " at address %llx\n",            \
     176             :              (unsigned long long)(uintptr_t __force)addr);              \
     177             :         return (u ## sz)~0ULL;                                          \
     178             : }                                                                       \
     179             :                                                                         \
     180             : static void real_raw_write ## op(u ## sz val,                           \
     181             :                                  volatile void __iomem *addr)           \
     182             : {                                                                       \
     183             :         WARN(1, "Invalid writeq" #op " of 0x%llx at address %llx\n",        \
     184             :              (unsigned long long)val,                                   \
     185             :              (unsigned long long)(uintptr_t __force)addr);\
     186             : }                                                                       \
     187             : 
     188           0 : MAKE_FALLBACK(b, 8);
     189           0 : MAKE_FALLBACK(w, 16);
     190           0 : MAKE_FALLBACK(l, 32);
     191             : #ifdef CONFIG_64BIT
     192           0 : MAKE_FALLBACK(q, 64);
     193             : #endif
     194             : 
     195             : static void real_memset_io(volatile void __iomem *addr, int value, size_t size)
     196             : {
     197           0 :         WARN(1, "Invalid memset_io at address 0x%llx\n",
     198             :              (unsigned long long)(uintptr_t __force)addr);
     199             : }
     200             : 
     201           0 : static void real_memcpy_fromio(void *buffer, const volatile void __iomem *addr,
     202             :                                size_t size)
     203             : {
     204           0 :         WARN(1, "Invalid memcpy_fromio at address 0x%llx\n",
     205             :              (unsigned long long)(uintptr_t __force)addr);
     206             : 
     207           0 :         memset(buffer, 0xff, size);
     208           0 : }
     209             : 
     210             : static void real_memcpy_toio(volatile void __iomem *addr, const void *buffer,
     211             :                              size_t size)
     212             : {
     213           0 :         WARN(1, "Invalid memcpy_toio at address 0x%llx\n",
     214             :              (unsigned long long)(uintptr_t __force)addr);
     215             : }
     216             : #endif /* CONFIG_INDIRECT_IOMEM_FALLBACK */
     217             : 
     218             : #define MAKE_OP(op, sz)                                                 \
     219             : u##sz __raw_read ## op(const volatile void __iomem *addr)               \
     220             : {                                                                       \
     221             :         struct logic_iomem_area *area = get_area(addr);                 \
     222             :                                                                         \
     223             :         if (!area)                                                      \
     224             :                 return real_raw_read ## op(addr);                       \
     225             :                                                                         \
     226             :         return (u ## sz) area->ops->read(area->priv,                   \
     227             :                                          (unsigned long)addr & AREA_MASK,\
     228             :                                          sz / 8);                       \
     229             : }                                                                       \
     230             : EXPORT_SYMBOL(__raw_read ## op);                                        \
     231             :                                                                         \
     232             : void __raw_write ## op(u ## sz val, volatile void __iomem *addr)        \
     233             : {                                                                       \
     234             :         struct logic_iomem_area *area = get_area(addr);                 \
     235             :                                                                         \
     236             :         if (!area) {                                                    \
     237             :                 real_raw_write ## op(val, addr);                        \
     238             :                 return;                                                 \
     239             :         }                                                               \
     240             :                                                                         \
     241             :         area->ops->write(area->priv,                                   \
     242             :                          (unsigned long)addr & AREA_MASK,           \
     243             :                          sz / 8, val);                                  \
     244             : }                                                                       \
     245             : EXPORT_SYMBOL(__raw_write ## op)
     246             : 
     247           0 : MAKE_OP(b, 8);
     248           0 : MAKE_OP(w, 16);
     249           0 : MAKE_OP(l, 32);
     250             : #ifdef CONFIG_64BIT
     251           0 : MAKE_OP(q, 64);
     252             : #endif
     253             : 
     254           0 : void memset_io(volatile void __iomem *addr, int value, size_t size)
     255             : {
     256           0 :         struct logic_iomem_area *area = get_area(addr);
     257             :         unsigned long offs, start;
     258             : 
     259           0 :         if (!area) {
     260           0 :                 real_memset_io(addr, value, size);
     261             :                 return;
     262             :         }
     263             : 
     264           0 :         start = (unsigned long)addr & AREA_MASK;
     265             : 
     266           0 :         if (area->ops->set) {
     267           0 :                 area->ops->set(area->priv, start, value, size);
     268           0 :                 return;
     269             :         }
     270             : 
     271           0 :         for (offs = 0; offs < size; offs++)
     272           0 :                 area->ops->write(area->priv, start + offs, 1, value);
     273             : }
     274             : EXPORT_SYMBOL(memset_io);
     275             : 
     276           0 : void memcpy_fromio(void *buffer, const volatile void __iomem *addr,
     277             :                    size_t size)
     278             : {
     279           0 :         struct logic_iomem_area *area = get_area(addr);
     280           0 :         u8 *buf = buffer;
     281             :         unsigned long offs, start;
     282             : 
     283           0 :         if (!area) {
     284           0 :                 real_memcpy_fromio(buffer, addr, size);
     285           0 :                 return;
     286             :         }
     287             : 
     288           0 :         start = (unsigned long)addr & AREA_MASK;
     289             : 
     290           0 :         if (area->ops->copy_from) {
     291           0 :                 area->ops->copy_from(area->priv, buffer, start, size);
     292           0 :                 return;
     293             :         }
     294             : 
     295           0 :         for (offs = 0; offs < size; offs++)
     296           0 :                 buf[offs] = area->ops->read(area->priv, start + offs, 1);
     297             : }
     298             : EXPORT_SYMBOL(memcpy_fromio);
     299             : 
     300           0 : void memcpy_toio(volatile void __iomem *addr, const void *buffer, size_t size)
     301             : {
     302           0 :         struct logic_iomem_area *area = get_area(addr);
     303           0 :         const u8 *buf = buffer;
     304             :         unsigned long offs, start;
     305             : 
     306           0 :         if (!area) {
     307           0 :                 real_memcpy_toio(addr, buffer, size);
     308             :                 return;
     309             :         }
     310             : 
     311           0 :         start = (unsigned long)addr & AREA_MASK;
     312             : 
     313           0 :         if (area->ops->copy_to) {
     314           0 :                 area->ops->copy_to(area->priv, start, buffer, size);
     315           0 :                 return;
     316             :         }
     317             : 
     318           0 :         for (offs = 0; offs < size; offs++)
     319           0 :                 area->ops->write(area->priv, start + offs, 1, buf[offs]);
     320             : }
     321             : EXPORT_SYMBOL(memcpy_toio);

Generated by: LCOV version 1.14