LCOV - code coverage report
Current view: top level - lib - logic_pio.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 3 66 4.5 %
Date: 2020-09-30 20:25:40 Functions: 1 7 14.3 %
Branches: 1 64 1.6 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0+
       2                 :            : /*
       3                 :            :  * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
       4                 :            :  * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
       5                 :            :  * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
       6                 :            :  */
       7                 :            : 
       8                 :            : #define pr_fmt(fmt)     "LOGIC PIO: " fmt
       9                 :            : 
      10                 :            : #include <linux/of.h>
      11                 :            : #include <linux/io.h>
      12                 :            : #include <linux/logic_pio.h>
      13                 :            : #include <linux/mm.h>
      14                 :            : #include <linux/rculist.h>
      15                 :            : #include <linux/sizes.h>
      16                 :            : #include <linux/slab.h>
      17                 :            : 
      18                 :            : /* The unique hardware address list */
      19                 :            : static LIST_HEAD(io_range_list);
      20                 :            : static DEFINE_MUTEX(io_range_mutex);
      21                 :            : 
      22                 :            : /* Consider a kernel general helper for this */
      23                 :            : #define in_range(b, first, len)        ((b) >= (first) && (b) < (first) + (len))
      24                 :            : 
      25                 :            : /**
      26                 :            :  * logic_pio_register_range - register logical PIO range for a host
      27                 :            :  * @new_range: pointer to the IO range to be registered.
      28                 :            :  *
      29                 :            :  * Returns 0 on success, the error code in case of failure.
      30                 :            :  *
      31                 :            :  * Register a new IO range node in the IO range list.
      32                 :            :  */
      33                 :          0 : int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
      34                 :            : {
      35                 :            :         struct logic_pio_hwaddr *range;
      36                 :            :         resource_size_t start;
      37                 :            :         resource_size_t end;
      38                 :            :         resource_size_t mmio_end = 0;
      39                 :            :         resource_size_t iio_sz = MMIO_UPPER_LIMIT;
      40                 :            :         int ret = 0;
      41                 :            : 
      42   [ #  #  #  #  :          0 :         if (!new_range || !new_range->fwnode || !new_range->size)
                   #  # ]
      43                 :            :                 return -EINVAL;
      44                 :            : 
      45                 :          0 :         start = new_range->hw_start;
      46                 :          0 :         end = new_range->hw_start + new_range->size;
      47                 :            : 
      48                 :          0 :         mutex_lock(&io_range_mutex);
      49         [ #  # ]:          0 :         list_for_each_entry(range, &io_range_list, list) {
      50         [ #  # ]:          0 :                 if (range->fwnode == new_range->fwnode) {
      51                 :            :                         /* range already there */
      52                 :            :                         goto end_register;
      53                 :            :                 }
      54   [ #  #  #  # ]:          0 :                 if (range->flags == LOGIC_PIO_CPU_MMIO &&
      55                 :          0 :                     new_range->flags == LOGIC_PIO_CPU_MMIO) {
      56                 :            :                         /* for MMIO ranges we need to check for overlap */
      57   [ #  #  #  # ]:          0 :                         if (start >= range->hw_start + range->size ||
      58                 :            :                             end < range->hw_start) {
      59                 :          0 :                                 mmio_end = range->io_start + range->size;
      60                 :            :                         } else {
      61                 :            :                                 ret = -EFAULT;
      62                 :            :                                 goto end_register;
      63                 :            :                         }
      64   [ #  #  #  # ]:          0 :                 } else if (range->flags == LOGIC_PIO_INDIRECT &&
      65                 :          0 :                            new_range->flags == LOGIC_PIO_INDIRECT) {
      66                 :          0 :                         iio_sz += range->size;
      67                 :            :                 }
      68                 :            :         }
      69                 :            : 
      70                 :            :         /* range not registered yet, check for available space */
      71         [ #  # ]:          0 :         if (new_range->flags == LOGIC_PIO_CPU_MMIO) {
      72         [ #  # ]:          0 :                 if (mmio_end + new_range->size - 1 > MMIO_UPPER_LIMIT) {
      73                 :            :                         /* if it's too big check if 64K space can be reserved */
      74         [ #  # ]:          0 :                         if (mmio_end + SZ_64K - 1 > MMIO_UPPER_LIMIT) {
      75                 :            :                                 ret = -E2BIG;
      76                 :            :                                 goto end_register;
      77                 :            :                         }
      78                 :          0 :                         new_range->size = SZ_64K;
      79                 :          0 :                         pr_warn("Requested IO range too big, new size set to 64K\n");
      80                 :            :                 }
      81                 :          0 :                 new_range->io_start = mmio_end;
      82         [ #  # ]:          0 :         } else if (new_range->flags == LOGIC_PIO_INDIRECT) {
      83         [ #  # ]:          0 :                 if (iio_sz + new_range->size - 1 > IO_SPACE_LIMIT) {
      84                 :            :                         ret = -E2BIG;
      85                 :            :                         goto end_register;
      86                 :            :                 }
      87                 :          0 :                 new_range->io_start = iio_sz;
      88                 :            :         } else {
      89                 :            :                 /* invalid flag */
      90                 :            :                 ret = -EINVAL;
      91                 :            :                 goto end_register;
      92                 :            :         }
      93                 :            : 
      94                 :          0 :         list_add_tail_rcu(&new_range->list, &io_range_list);
      95                 :            : 
      96                 :            : end_register:
      97                 :          0 :         mutex_unlock(&io_range_mutex);
      98                 :          0 :         return ret;
      99                 :            : }
     100                 :            : 
     101                 :            : /**
     102                 :            :  * logic_pio_unregister_range - unregister a logical PIO range for a host
     103                 :            :  * @range: pointer to the IO range which has been already registered.
     104                 :            :  *
     105                 :            :  * Unregister a previously-registered IO range node.
     106                 :            :  */
     107                 :          0 : void logic_pio_unregister_range(struct logic_pio_hwaddr *range)
     108                 :            : {
     109                 :          0 :         mutex_lock(&io_range_mutex);
     110                 :            :         list_del_rcu(&range->list);
     111                 :          0 :         mutex_unlock(&io_range_mutex);
     112                 :          0 :         synchronize_rcu();
     113                 :          0 : }
     114                 :            : 
     115                 :            : /**
     116                 :            :  * find_io_range_by_fwnode - find logical PIO range for given FW node
     117                 :            :  * @fwnode: FW node handle associated with logical PIO range
     118                 :            :  *
     119                 :            :  * Returns pointer to node on success, NULL otherwise.
     120                 :            :  *
     121                 :            :  * Traverse the io_range_list to find the registered node for @fwnode.
     122                 :            :  */
     123                 :       9936 : struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
     124                 :            : {
     125                 :            :         struct logic_pio_hwaddr *range, *found_range = NULL;
     126                 :            : 
     127                 :            :         rcu_read_lock();
     128   [ #  #  -  + ]:       9936 :         list_for_each_entry_rcu(range, &io_range_list, list) {
     129   [ #  #  #  # ]:          0 :                 if (range->fwnode == fwnode) {
     130                 :          0 :                         found_range = range;
     131                 :          0 :                         break;
     132                 :            :                 }
     133                 :            :         }
     134                 :            :         rcu_read_unlock();
     135                 :            : 
     136                 :       9936 :         return found_range;
     137                 :            : }
     138                 :            : 
     139                 :            : /* Return a registered range given an input PIO token */
     140                 :          0 : static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
     141                 :            : {
     142                 :            :         struct logic_pio_hwaddr *range, *found_range = NULL;
     143                 :            : 
     144                 :            :         rcu_read_lock();
     145         [ #  # ]:          0 :         list_for_each_entry_rcu(range, &io_range_list, list) {
     146   [ #  #  #  # ]:          0 :                 if (in_range(pio, range->io_start, range->size)) {
     147                 :          0 :                         found_range = range;
     148                 :          0 :                         break;
     149                 :            :                 }
     150                 :            :         }
     151                 :            :         rcu_read_unlock();
     152                 :            : 
     153         [ #  # ]:          0 :         if (!found_range)
     154                 :          0 :                 pr_err("PIO entry token 0x%lx invalid\n", pio);
     155                 :            : 
     156                 :          0 :         return found_range;
     157                 :            : }
     158                 :            : 
     159                 :            : /**
     160                 :            :  * logic_pio_to_hwaddr - translate logical PIO to HW address
     161                 :            :  * @pio: logical PIO value
     162                 :            :  *
     163                 :            :  * Returns HW address if valid, ~0 otherwise.
     164                 :            :  *
     165                 :            :  * Translate the input logical PIO to the corresponding hardware address.
     166                 :            :  * The input PIO should be unique in the whole logical PIO space.
     167                 :            :  */
     168                 :          0 : resource_size_t logic_pio_to_hwaddr(unsigned long pio)
     169                 :            : {
     170                 :            :         struct logic_pio_hwaddr *range;
     171                 :            : 
     172                 :          0 :         range = find_io_range(pio);
     173         [ #  # ]:          0 :         if (range)
     174                 :          0 :                 return range->hw_start + pio - range->io_start;
     175                 :            : 
     176                 :            :         return (resource_size_t)~0;
     177                 :            : }
     178                 :            : 
     179                 :            : /**
     180                 :            :  * logic_pio_trans_hwaddr - translate HW address to logical PIO
     181                 :            :  * @fwnode: FW node reference for the host
     182                 :            :  * @addr: Host-relative HW address
     183                 :            :  * @size: size to translate
     184                 :            :  *
     185                 :            :  * Returns Logical PIO value if successful, ~0UL otherwise
     186                 :            :  */
     187                 :          0 : unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
     188                 :            :                                      resource_size_t addr, resource_size_t size)
     189                 :            : {
     190                 :            :         struct logic_pio_hwaddr *range;
     191                 :            : 
     192                 :            :         range = find_io_range_by_fwnode(fwnode);
     193   [ #  #  #  # ]:          0 :         if (!range || range->flags == LOGIC_PIO_CPU_MMIO) {
     194                 :          0 :                 pr_err("IO range not found or invalid\n");
     195                 :          0 :                 return ~0UL;
     196                 :            :         }
     197         [ #  # ]:          0 :         if (range->size < size) {
     198                 :          0 :                 pr_err("resource size %pa cannot fit in IO range size %pa\n",
     199                 :            :                        &size, &range->size);
     200                 :          0 :                 return ~0UL;
     201                 :            :         }
     202                 :          0 :         return addr - range->hw_start + range->io_start;
     203                 :            : }
     204                 :            : 
     205                 :          0 : unsigned long logic_pio_trans_cpuaddr(resource_size_t addr)
     206                 :            : {
     207                 :            :         struct logic_pio_hwaddr *range;
     208                 :            : 
     209                 :            :         rcu_read_lock();
     210         [ #  # ]:          0 :         list_for_each_entry_rcu(range, &io_range_list, list) {
     211         [ #  # ]:          0 :                 if (range->flags != LOGIC_PIO_CPU_MMIO)
     212                 :          0 :                         continue;
     213   [ #  #  #  # ]:          0 :                 if (in_range(addr, range->hw_start, range->size)) {
     214                 :            :                         unsigned long cpuaddr;
     215                 :            : 
     216                 :          0 :                         cpuaddr = addr - range->hw_start + range->io_start;
     217                 :            : 
     218                 :            :                         rcu_read_unlock();
     219                 :          0 :                         return cpuaddr;
     220                 :            :                 }
     221                 :            :         }
     222                 :            :         rcu_read_unlock();
     223                 :            : 
     224                 :          0 :         pr_err("addr %pa not registered in io_range_list\n", &addr);
     225                 :            : 
     226                 :          0 :         return ~0UL;
     227                 :            : }
     228                 :            : 
     229                 :            : #if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE)
     230                 :            : #define BUILD_LOGIC_IO(bw, type)                                        \
     231                 :            : type logic_in##bw(unsigned long addr)                                   \
     232                 :            : {                                                                       \
     233                 :            :         type ret = (type)~0;                                            \
     234                 :            :                                                                         \
     235                 :            :         if (addr < MMIO_UPPER_LIMIT) {                                       \
     236                 :            :                 ret = read##bw(PCI_IOBASE + addr);                      \
     237                 :            :         } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
     238                 :            :                 struct logic_pio_hwaddr *entry = find_io_range(addr);   \
     239                 :            :                                                                         \
     240                 :            :                 if (entry && entry->ops)                             \
     241                 :            :                         ret = entry->ops->in(entry->hostdata,          \
     242                 :            :                                         addr, sizeof(type));            \
     243                 :            :                 else                                                    \
     244                 :            :                         WARN_ON_ONCE(1);                                \
     245                 :            :         }                                                               \
     246                 :            :         return ret;                                                     \
     247                 :            : }                                                                       \
     248                 :            :                                                                         \
     249                 :            : void logic_out##bw(type value, unsigned long addr)                      \
     250                 :            : {                                                                       \
     251                 :            :         if (addr < MMIO_UPPER_LIMIT) {                                       \
     252                 :            :                 write##bw(value, PCI_IOBASE + addr);                    \
     253                 :            :         } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {   \
     254                 :            :                 struct logic_pio_hwaddr *entry = find_io_range(addr);   \
     255                 :            :                                                                         \
     256                 :            :                 if (entry && entry->ops)                             \
     257                 :            :                         entry->ops->out(entry->hostdata,               \
     258                 :            :                                         addr, value, sizeof(type));     \
     259                 :            :                 else                                                    \
     260                 :            :                         WARN_ON_ONCE(1);                                \
     261                 :            :         }                                                               \
     262                 :            : }                                                                       \
     263                 :            :                                                                         \
     264                 :            : void logic_ins##bw(unsigned long addr, void *buffer,            \
     265                 :            :                    unsigned int count)                                  \
     266                 :            : {                                                                       \
     267                 :            :         if (addr < MMIO_UPPER_LIMIT) {                                       \
     268                 :            :                 reads##bw(PCI_IOBASE + addr, buffer, count);            \
     269                 :            :         } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {   \
     270                 :            :                 struct logic_pio_hwaddr *entry = find_io_range(addr);   \
     271                 :            :                                                                         \
     272                 :            :                 if (entry && entry->ops)                             \
     273                 :            :                         entry->ops->ins(entry->hostdata,               \
     274                 :            :                                 addr, buffer, sizeof(type), count);     \
     275                 :            :                 else                                                    \
     276                 :            :                         WARN_ON_ONCE(1);                                \
     277                 :            :         }                                                               \
     278                 :            :                                                                         \
     279                 :            : }                                                                       \
     280                 :            :                                                                         \
     281                 :            : void logic_outs##bw(unsigned long addr, const void *buffer,             \
     282                 :            :                     unsigned int count)                                 \
     283                 :            : {                                                                       \
     284                 :            :         if (addr < MMIO_UPPER_LIMIT) {                                       \
     285                 :            :                 writes##bw(PCI_IOBASE + addr, buffer, count);           \
     286                 :            :         } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {   \
     287                 :            :                 struct logic_pio_hwaddr *entry = find_io_range(addr);   \
     288                 :            :                                                                         \
     289                 :            :                 if (entry && entry->ops)                             \
     290                 :            :                         entry->ops->outs(entry->hostdata,              \
     291                 :            :                                 addr, buffer, sizeof(type), count);     \
     292                 :            :                 else                                                    \
     293                 :            :                         WARN_ON_ONCE(1);                                \
     294                 :            :         }                                                               \
     295                 :            : }
     296                 :            : 
     297                 :            : BUILD_LOGIC_IO(b, u8)
     298                 :            : EXPORT_SYMBOL(logic_inb);
     299                 :            : EXPORT_SYMBOL(logic_insb);
     300                 :            : EXPORT_SYMBOL(logic_outb);
     301                 :            : EXPORT_SYMBOL(logic_outsb);
     302                 :            : 
     303                 :            : BUILD_LOGIC_IO(w, u16)
     304                 :            : EXPORT_SYMBOL(logic_inw);
     305                 :            : EXPORT_SYMBOL(logic_insw);
     306                 :            : EXPORT_SYMBOL(logic_outw);
     307                 :            : EXPORT_SYMBOL(logic_outsw);
     308                 :            : 
     309                 :            : BUILD_LOGIC_IO(l, u32)
     310                 :            : EXPORT_SYMBOL(logic_inl);
     311                 :            : EXPORT_SYMBOL(logic_insl);
     312                 :            : EXPORT_SYMBOL(logic_outl);
     313                 :            : EXPORT_SYMBOL(logic_outsl);
     314                 :            : 
     315                 :            : #endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */

Generated by: LCOV version 1.14