Home | History | Annotate | Line # | Download | only in arc
      1 /*	$NetBSD: bus_space.c,v 1.14 2023/12/20 06:36:02 thorpej Exp $	*/
      2 /*	NetBSD: bus_machdep.c,v 1.1 2000/01/26 18:48:00 drochner Exp 	*/
      3 
      4 /*-
      5  * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
      6  * All rights reserved.
      7  *
      8  * This code is derived from software contributed to The NetBSD Foundation
      9  * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
     10  * Simulation Facility, NASA Ames Research Center.
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted provided that the following conditions
     14  * are met:
     15  * 1. Redistributions of source code must retain the above copyright
     16  *    notice, this list of conditions and the following disclaimer.
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     31  * POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 
     34 #include <sys/cdefs.h>
     35 __KERNEL_RCSID(0, "$NetBSD: bus_space.c,v 1.14 2023/12/20 06:36:02 thorpej Exp $");
     36 
     37 #include <sys/param.h>
     38 #include <sys/systm.h>
     39 #include <sys/vmem_impl.h>
     40 
     41 #include <uvm/uvm_extern.h>
     42 
     43 #include <sys/bus.h>
     44 
     45 /*
     46  *	uintN_t bus_space_read_N(bus_space_tag_t tag,
     47  *	    bus_space_handle_t bsh, bus_size_t offset);
     48  *
     49  * Read a 1, 2, 4, or 8 byte quantity from bus space
     50  * described by tag/handle/offset.
     51  */
     52 
     53 #define bus_space_read(BYTES,BITS)					\
     54 __CONCAT3(uint,BITS,_t)					\
     55 __CONCAT(bus_space_read_,BYTES)(bus_space_tag_t bst,			\
     56     bus_space_handle_t bsh, bus_size_t offset)				\
     57 {									\
     58 	return (*(volatile __CONCAT3(uint,BITS,_t) *)			\
     59 	    (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))));	\
     60 }
     61 
     62 bus_space_read(1,8)
     63 bus_space_read(2,16)
     64 bus_space_read(4,32)
     65 bus_space_read(8,64)
     66 
     67 /*
     68  *	void bus_space_read_multi_N(bus_space_tag_t tag,
     69  *	    bus_space_handle_t bsh, bus_size_t offset,
     70  *	    uintN_t *addr, size_t count);
     71  *
     72  * Read `count' 1, 2, 4, or 8 byte quantities from bus space
     73  * described by tag/handle/offset and copy into buffer provided.
     74  */
     75 
     76 #define bus_space_read_multi(BYTES,BITS)				\
     77 void							\
     78 __CONCAT(bus_space_read_multi_,BYTES)(bus_space_tag_t bst,		\
     79     bus_space_handle_t bsh, bus_size_t offset,				\
     80     __CONCAT3(uint,BITS,_t) *datap, bus_size_t count)			\
     81 {									\
     82 	volatile __CONCAT3(uint,BITS,_t) *p =				\
     83 	    (volatile __CONCAT3(uint,BITS,_t) *)			\
     84 	    (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES)));	\
     85 									\
     86 	for (; count > 0; --count)					\
     87 		*datap++ = *p;						\
     88 }
     89 
     90 bus_space_read_multi(1,8)
     91 bus_space_read_multi(2,16)
     92 bus_space_read_multi(4,32)
     93 bus_space_read_multi(8,64)
     94 
     95 /*
     96  *	void bus_space_read_region_N(bus_space_tag_t tag,
     97  *	    bus_space_handle_t bsh, bus_size_t offset,
     98  *	    uintN_t *addr, size_t count);
     99  *
    100  * Read `count' 1, 2, 4, or 8 byte quantities from bus space
    101  * described by tag/handle and starting at `offset' and copy into
    102  * buffer provided.
    103  */
    104 
    105 #define bus_space_read_region(BYTES,BITS)				\
    106 void							\
    107 __CONCAT(bus_space_read_region_,BYTES)(bus_space_tag_t bst,		\
    108     bus_space_handle_t bsh, bus_size_t offset,				\
    109     __CONCAT3(uint,BITS,_t) *datap, bus_size_t count)			\
    110 {									\
    111 	int stride = 1 << __CONCAT(bst->bs_stride_,BYTES);		\
    112 	volatile __CONCAT3(uint,BITS,_t) *p =				\
    113 	    (volatile __CONCAT3(uint,BITS,_t) *)			\
    114 	    (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES)));	\
    115 									\
    116 	for (; count > 0; --count) {					\
    117 		*datap++ = *p;						\
    118 		p += stride;						\
    119 	}								\
    120 }
    121 
    122 bus_space_read_region(1,8)
    123 bus_space_read_region(2,16)
    124 bus_space_read_region(4,32)
    125 bus_space_read_region(8,64)
    126 
    127 /*
    128  *	void bus_space_write_N(bus_space_tag_t tag,
    129  *	    bus_space_handle_t bsh, bus_size_t offset,
    130  *	    uintN_t value);
    131  *
    132  * Write the 1, 2, 4, or 8 byte value `value' to bus space
    133  * described by tag/handle/offset.
    134  */
    135 
    136 #define bus_space_write(BYTES,BITS)					\
    137 void							\
    138 __CONCAT(bus_space_write_,BYTES)(bus_space_tag_t bst,			\
    139     bus_space_handle_t bsh,						\
    140     bus_size_t offset, __CONCAT3(uint,BITS,_t) data)			\
    141 {									\
    142 	*(volatile __CONCAT3(uint,BITS,_t) *)				\
    143 	    (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))) = data; \
    144 }
    145 
    146 bus_space_write(1,8)
    147 bus_space_write(2,16)
    148 bus_space_write(4,32)
    149 bus_space_write(8,64)
    150 
    151 /*
    152  *	void bus_space_write_multi_N(bus_space_tag_t tag,
    153  *	    bus_space_handle_t bsh, bus_size_t offset,
    154  *	    const uintN_t *addr, size_t count);
    155  *
    156  * Write `count' 1, 2, 4, or 8 byte quantities from the buffer
    157  * provided to bus space described by tag/handle/offset.
    158  */
    159 
    160 #define bus_space_write_multi(BYTES,BITS)				\
    161 void							\
    162 __CONCAT(bus_space_write_multi_,BYTES)(bus_space_tag_t bst,		\
    163     bus_space_handle_t bsh, bus_size_t offset,				\
    164     const __CONCAT3(uint,BITS,_t) *datap, bus_size_t count)		\
    165 {									\
    166 	volatile __CONCAT3(uint,BITS,_t) *p =				\
    167 	    (volatile __CONCAT3(uint,BITS,_t) *)			\
    168 	    (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES)));	\
    169 									\
    170 	for (; count > 0; --count)					\
    171 		*p = *datap++;						\
    172 }
    173 
    174 bus_space_write_multi(1,8)
    175 bus_space_write_multi(2,16)
    176 bus_space_write_multi(4,32)
    177 bus_space_write_multi(8,64)
    178 
    179 /*
    180  *	void bus_space_write_region_N(bus_space_tag_t tag,
    181  *	    bus_space_handle_t bsh, bus_size_t offset,
    182  *	    const uintN_t *addr, size_t count);
    183  *
    184  * Write `count' 1, 2, 4, or 8 byte quantities from the buffer provided
    185  * to bus space described by tag/handle starting at `offset'.
    186  */
    187 
    188 #define bus_space_write_region(BYTES,BITS)				\
    189 void							\
    190 __CONCAT(bus_space_write_region_,BYTES)(bus_space_tag_t bst,		\
    191     bus_space_handle_t bsh, bus_size_t offset,				\
    192     const __CONCAT3(uint,BITS,_t) *datap, bus_size_t count)		\
    193 {									\
    194 	int stride = 1 << __CONCAT(bst->bs_stride_,BYTES);		\
    195 	volatile __CONCAT3(uint,BITS,_t) *p =				\
    196 	    (volatile __CONCAT3(uint,BITS,_t) *)			\
    197 	    (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES)));	\
    198 									\
    199 	for (; count > 0; --count) {					\
    200 		*p = *datap++;						\
    201 		p += stride;						\
    202 	}								\
    203 }
    204 
    205 bus_space_write_region(1,8)
    206 bus_space_write_region(2,16)
    207 bus_space_write_region(4,32)
    208 bus_space_write_region(8,64)
    209 
    210 /*
    211  *	void bus_space_set_multi_N(bus_space_tag_t tag,
    212  *	    bus_space_handle_t bsh, bus_size_t offset, uintN_t val,
    213  *	    size_t count);
    214  *
    215  * Write the 1, 2, 4, or 8 byte value `val' to bus space described
    216  * by tag/handle/offset `count' times.
    217  */
    218 
    219 #define bus_space_set_multi(BYTES,BITS)					\
    220 void							\
    221 __CONCAT(bus_space_set_multi_,BYTES)(bus_space_tag_t bst,		\
    222     bus_space_handle_t bsh, bus_size_t offset,				\
    223     const __CONCAT3(uint,BITS,_t) data, bus_size_t count)		\
    224 {									\
    225 	volatile __CONCAT3(uint,BITS,_t) *p =				\
    226 	    (volatile __CONCAT3(uint,BITS,_t) *)			\
    227 	    (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES)));	\
    228 									\
    229 	for (; count > 0; --count)					\
    230 		*p = data;						\
    231 }
    232 
    233 bus_space_set_multi(1,8)
    234 bus_space_set_multi(2,16)
    235 bus_space_set_multi(4,32)
    236 bus_space_set_multi(8,64)
    237 
    238 /*
    239  *	void bus_space_set_region_N(bus_space_tag_t tag,
    240  *	    bus_space_handle_t bsh, bus_size_t offset, uintN_t val,
    241  *	    size_t count);
    242  *
    243  * Write `count' 1, 2, 4, or 8 byte value `val' to bus space described
    244  * by tag/handle starting at `offset'.
    245  */
    246 
    247 #define bus_space_set_region(BYTES,BITS)				\
    248 void							\
    249 __CONCAT(bus_space_set_region_,BYTES)(bus_space_tag_t bst,		\
    250     bus_space_handle_t bsh, bus_size_t offset,				\
    251     __CONCAT3(uint,BITS,_t) data, bus_size_t count)			\
    252 {									\
    253 	int stride = 1 << __CONCAT(bst->bs_stride_,BYTES);		\
    254 	volatile __CONCAT3(uint,BITS,_t) *p =				\
    255 	    (volatile __CONCAT3(uint,BITS,_t) *)			\
    256 	    (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES)));	\
    257 									\
    258 	for (; count > 0; --count) {					\
    259 		*p = data;						\
    260 		p += stride;						\
    261 	}								\
    262 }
    263 
    264 bus_space_set_region(1,8)
    265 bus_space_set_region(2,16)
    266 bus_space_set_region(4,32)
    267 bus_space_set_region(8,64)
    268 
    269 /*
    270  *	void bus_space_copy_region_N(bus_space_tag_t tag,
    271  *	    bus_space_handle_t bsh1, bus_size_t off1,
    272  *	    bus_space_handle_t bsh2, bus_size_t off2,
    273  *	    size_t count);
    274  *
    275  * Copy `count' 1, 2, 4, or 8 byte values from bus space starting
    276  * at tag/bsh1/off1 to bus space starting at tag/bsh2/off2.
    277  */
    278 
    279 #define bus_space_copy_region(BYTES,BITS)				\
    280 void							\
    281 __CONCAT(bus_space_copy_region_,BYTES)(bus_space_tag_t bst,		\
    282     bus_space_handle_t srcbsh, bus_size_t srcoffset,			\
    283     bus_space_handle_t dstbsh, bus_size_t dstoffset, bus_size_t count)	\
    284 {									\
    285 	int stride = 1 << __CONCAT(bst->bs_stride_,BYTES);		\
    286 	volatile __CONCAT3(uint,BITS,_t) *srcp =			\
    287 	    (volatile __CONCAT3(uint,BITS,_t) *)			\
    288 	    (srcbsh + (srcoffset << __CONCAT(bst->bs_stride_,BYTES)));	\
    289 	volatile __CONCAT3(uint,BITS,_t) *dstp =			\
    290 	    (volatile __CONCAT3(uint,BITS,_t) *)			\
    291 	    (dstbsh + (dstoffset << __CONCAT(bst->bs_stride_,BYTES)));	\
    292 	bus_size_t offset;						\
    293 									\
    294 	if (srcp >= dstp) {						\
    295 		/* src after dest: copy forward */			\
    296 		for (offset = 0; count > 0; --count, offset += stride)	\
    297 			dstp[offset] = srcp[offset];			\
    298 	} else {							\
    299 		/* dest after src: copy backward */			\
    300 		offset = (count << __CONCAT(bst->bs_stride_,BYTES))	\
    301 		    - stride;						\
    302 		for (; count > 0; --count, offset -= stride)		\
    303 			dstp[offset] = srcp[offset];			\
    304 	}								\
    305 }
    306 
    307 bus_space_copy_region(1,8)
    308 bus_space_copy_region(2,16)
    309 bus_space_copy_region(4,32)
    310 bus_space_copy_region(8,64)
    311 
    312 void
    313 arc_bus_space_init(bus_space_tag_t bst, const char *name, paddr_t paddr,
    314     vaddr_t vaddr, bus_addr_t start, bus_size_t size)
    315 {
    316 
    317 	bst->bs_name = name;
    318 	bst->bs_arena = NULL;
    319 	bst->bs_start = start;
    320 	bst->bs_size = size;
    321 	bst->bs_pbase = paddr;
    322 	bst->bs_vbase = vaddr;
    323 	bst->bs_compose_handle = arc_bus_space_compose_handle;
    324 	bst->bs_dispose_handle = arc_bus_space_dispose_handle;
    325 	bst->bs_paddr = arc_bus_space_paddr;
    326 	bst->bs_map = arc_bus_space_map;
    327 	bst->bs_unmap = arc_bus_space_unmap;
    328 	bst->bs_subregion = arc_bus_space_subregion;
    329 	bst->bs_mmap = arc_bus_space_mmap;
    330 	bst->bs_alloc = arc_bus_space_alloc;
    331 	bst->bs_free = arc_bus_space_free;
    332 	bst->bs_aux = NULL;
    333 	bst->bs_stride_1 = 0;
    334 	bst->bs_stride_2 = 0;
    335 	bst->bs_stride_4 = 0;
    336 	bst->bs_stride_8 = 0;
    337 }
    338 
    339 void
    340 arc_bus_space_init_arena(bus_space_tag_t bst, struct vmem *arena_store,
    341     struct vmem_btag *btag_store, unsigned int btag_count)
    342 {
    343 	int error __diagused;
    344 
    345 	bst->bs_arena = vmem_init(arena_store,
    346 				  bst->bs_name,		/* name */
    347 				  0,			/* addr */
    348 				  0,			/* size */
    349 				  1,			/* quantum */
    350 				  NULL,			/* importfn */
    351 				  NULL,			/* releasefn */
    352 				  NULL,			/* source */
    353 				  0,			/* qcache_max */
    354 				  VM_NOSLEEP | VM_PRIVTAGS,
    355 				  IPL_NONE);
    356 	KASSERT(bst->bs_arena != NULL);
    357 
    358 	vmem_add_bts(bst->bs_arena, btag_store, btag_count);
    359 	error = vmem_add(bst->bs_arena, bst->bs_start, bst->bs_size,
    360 	    VM_NOSLEEP);
    361 	KASSERT(error == 0);
    362 }
    363 
    364 void
    365 arc_bus_space_set_aligned_stride(bus_space_tag_t bst,
    366     unsigned int alignment_shift)
    367 {
    368 
    369 	bst->bs_stride_1 = alignment_shift;
    370 	if (alignment_shift > 0)
    371 		--alignment_shift;
    372 	bst->bs_stride_2 = alignment_shift;
    373 	if (alignment_shift > 0)
    374 		--alignment_shift;
    375 	bst->bs_stride_4 = alignment_shift;
    376 	if (alignment_shift > 0)
    377 		--alignment_shift;
    378 	bst->bs_stride_8 = alignment_shift;
    379 }
    380 
    381 int
    382 arc_bus_space_compose_handle(bus_space_tag_t bst, bus_addr_t addr,
    383     bus_size_t size, int flags, bus_space_handle_t *bshp)
    384 {
    385 	bus_space_handle_t bsh = bst->bs_vbase + (addr - bst->bs_start);
    386 
    387 	/*
    388 	 * Since all buses can be linearly mappable, we don't have to check
    389 	 * BUS_SPACE_MAP_LINEAR and BUS_SPACE_MAP_PREFETCHABLE.
    390 	 */
    391 	if ((flags & BUS_SPACE_MAP_CACHEABLE) == 0) {
    392 		*bshp = bsh;
    393 		return 0;
    394 	}
    395 	if (bsh < MIPS_KSEG1_START) /* KUSEG or KSEG0 */
    396 		panic("arc_bus_space_compose_handle: bad address 0x%x", bsh);
    397 	if (bsh < MIPS_KSEG2_START) { /* KSEG1 */
    398 		*bshp = MIPS_PHYS_TO_KSEG0(MIPS_KSEG1_TO_PHYS(bsh));
    399 		return 0;
    400 	}
    401 	/*
    402 	 * KSEG2:
    403 	 * Do not make the page cacheable in this case, since:
    404 	 * - the page which this bus_space belongs might include
    405 	 *   other bus_spaces.
    406 	 * or
    407 	 * - this bus might be mapped by wired TLB, in that case,
    408 	 *   we cannot manupulate cacheable attribute with page granularity.
    409 	 */
    410 #ifdef DIAGNOSTIC
    411 	printf("arc_bus_space_compose_handle: ignore cacheable 0x%x\n", bsh);
    412 #endif
    413 	*bshp = bsh;
    414 	return 0;
    415 }
    416 
    417 int
    418 arc_bus_space_dispose_handle(bus_space_tag_t bst, bus_space_handle_t bsh,
    419     bus_size_t size)
    420 {
    421 
    422 	return 0;
    423 }
    424 
    425 int
    426 arc_bus_space_paddr(bus_space_tag_t bst, bus_space_handle_t bsh, paddr_t *pap)
    427 {
    428 
    429 	if (bsh < MIPS_KSEG0_START) /* KUSEG */
    430 		panic("arc_bus_space_paddr(0x%qx): bad address",
    431 		    (unsigned long long) bsh);
    432 	else if (bsh < MIPS_KSEG1_START) /* KSEG0 */
    433 		*pap = MIPS_KSEG0_TO_PHYS(bsh);
    434 	else if (bsh < MIPS_KSEG2_START) /* KSEG1 */
    435 		*pap = MIPS_KSEG1_TO_PHYS(bsh);
    436 	else { /* KSEG2 */
    437 		/*
    438 		 * Since this region may be mapped by wired TLB,
    439 		 * kvtophys() is not always available.
    440 		 */
    441 		*pap = bst->bs_pbase + (bsh - bst->bs_vbase);
    442 	}
    443 	return 0;
    444 }
    445 
    446 int
    447 arc_bus_space_map(bus_space_tag_t bst, bus_addr_t addr, bus_size_t size,
    448     int flags, bus_space_handle_t *bshp)
    449 {
    450 	int err;
    451 
    452 	if (addr < bst->bs_start || addr + size > bst->bs_start + bst->bs_size)
    453 		return EINVAL;
    454 
    455 	if (bst->bs_arena != NULL) {
    456 		err = vmem_xalloc_addr(bst->bs_arena, addr, size, VM_NOSLEEP);
    457 		if (err)
    458 			return err;
    459 	}
    460 
    461 	return bus_space_compose_handle(bst, addr, size, flags, bshp);
    462 }
    463 
    464 void
    465 arc_bus_space_unmap(bus_space_tag_t bst, bus_space_handle_t bsh,
    466     bus_size_t size)
    467 {
    468 
    469 	if (bst->bs_arena != NULL) {
    470 		paddr_t pa;
    471 		bus_addr_t addr;
    472 		int err;
    473 
    474 		/* bus_space_paddr() becomes unavailable after unmapping */
    475 		err = bus_space_paddr(bst, bsh, &pa);
    476 		if (err)
    477 			panic("arc_bus_space_unmap: %s va 0x%qx: error %d",
    478 			    bst->bs_name, (unsigned long long) bsh, err);
    479 		addr = (bus_size_t)(pa - bst->bs_pbase) + bst->bs_start;
    480 		vmem_xfree(bst->bs_arena, addr, size);
    481 	}
    482 	bus_space_dispose_handle(bst, bsh, size);
    483 }
    484 
    485 int
    486 arc_bus_space_subregion(bus_space_tag_t bst, bus_space_handle_t bsh,
    487     bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
    488 {
    489 
    490 	*nbshp = bsh + offset;
    491 	return 0;
    492 }
    493 
    494 paddr_t
    495 arc_bus_space_mmap(bus_space_tag_t bst, bus_addr_t addr, off_t off, int prot,
    496     int flags)
    497 {
    498 
    499 	/*
    500 	 * XXX We do not disallow mmap'ing of EISA/PCI I/O space here,
    501 	 * XXX which we should be doing.
    502 	 */
    503 
    504 	if (addr < bst->bs_start ||
    505 	    (addr + off) >= (bst->bs_start + bst->bs_size))
    506 		return -1;
    507 
    508 	return mips_btop(bst->bs_pbase + (addr - bst->bs_start) + off);
    509 }
    510 
    511 int
    512 arc_bus_space_alloc(bus_space_tag_t bst, bus_addr_t start, bus_addr_t end,
    513     bus_size_t size, bus_size_t align, bus_size_t boundary, int flags,
    514     bus_addr_t *addrp, bus_space_handle_t *bshp)
    515 {
    516 	vmem_addr_t addr;
    517 	int err;
    518 
    519 	if (bst->bs_arena == NULL)
    520 		panic("arc_bus_space_alloc: vmem arena %s not available",
    521 		    bst->bs_name);
    522 
    523 	if (start < bst->bs_start ||
    524 	    start + size > bst->bs_start + bst->bs_size)
    525 		return EINVAL;
    526 
    527 	err = vmem_xalloc(bst->bs_arena, size,
    528 			  align,		/* align */
    529 			  0,			/* phase */
    530 			  boundary,		/* nocross */
    531 			  start,		/* minaddr */
    532 			  end,			/* maxaddr */
    533 			  VM_BESTFIT | VM_NOSLEEP,
    534 			  &addr);
    535 	if (err)
    536 		return err;
    537 
    538 	*addrp = addr;
    539 	return bus_space_compose_handle(bst, addr, size, flags, bshp);
    540 }
    541