@@ -721,6 +721,191 @@ static void imx_dsp_rproc_kick(struct rproc *rproc, int vqid)
721721 dev_err (dev , "%s: failed (%d, err:%d)\n" , __func__ , vqid , err );
722722}
723723
724+ /*
725+ * Custom memory copy implementation for i.MX DSP Cores
726+ *
727+ * The IRAM is part of the HiFi DSP.
728+ * According to hw specs only 32-bits writes are allowed.
729+ */
730+ static int imx_dsp_rproc_memcpy (void * dest , const void * src , size_t size )
731+ {
732+ const u8 * src_byte = src ;
733+ const u32 * source = src ;
734+ u32 affected_mask ;
735+ u32 * dst = dest ;
736+ int i , q , r ;
737+ u32 tmp ;
738+
739+ /* destination must be 32bit aligned */
740+ if (!IS_ALIGNED ((uintptr_t )dest , 4 ))
741+ return - EINVAL ;
742+
743+ q = size / 4 ;
744+ r = size % 4 ;
745+
746+ /* copy data in units of 32 bits at a time */
747+ for (i = 0 ; i < q ; i ++ )
748+ writel (source [i ], & dst [i ]);
749+
750+ if (r ) {
751+ affected_mask = GENMASK (8 * r , 0 );
752+
753+ /*
754+ * first read the 32bit data of dest, then change affected
755+ * bytes, and write back to dest.
756+ * For unaffected bytes, it should not be changed
757+ */
758+ tmp = readl (dest + q * 4 );
759+ tmp &= ~affected_mask ;
760+
761+ /* avoid reading after end of source */
762+ for (i = 0 ; i < r ; i ++ )
763+ tmp |= (src_byte [q * 4 + i ] << (8 * i ));
764+
765+ writel (tmp , dest + q * 4 );
766+ }
767+
768+ return 0 ;
769+ }
770+
771+ /*
772+ * Custom memset implementation for i.MX DSP Cores
773+ *
774+ * The IRAM is part of the HiFi DSP.
775+ * According to hw specs only 32-bits writes are allowed.
776+ */
777+ static int imx_dsp_rproc_memset (void * addr , u8 value , size_t size )
778+ {
779+ u32 tmp_val = value ;
780+ u32 * tmp_dst = addr ;
781+ u32 affected_mask ;
782+ int q , r ;
783+ u32 tmp ;
784+
785+ /* destination must be 32bit aligned */
786+ if (!IS_ALIGNED ((uintptr_t )addr , 4 ))
787+ return - EINVAL ;
788+
789+ tmp_val |= tmp_val << 8 ;
790+ tmp_val |= tmp_val << 16 ;
791+
792+ q = size / 4 ;
793+ r = size % 4 ;
794+
795+ while (q -- )
796+ writel (tmp_val , tmp_dst ++ );
797+
798+ if (r ) {
799+ affected_mask = GENMASK (8 * r , 0 );
800+
801+ /*
802+ * first read the 32bit data of addr, then change affected
803+ * bytes, and write back to addr.
804+ * For unaffected bytes, it should not be changed
805+ */
806+ tmp = readl (tmp_dst );
807+ tmp &= ~affected_mask ;
808+
809+ tmp |= (tmp_val & affected_mask );
810+ writel (tmp , tmp_dst );
811+ }
812+
813+ return 0 ;
814+ }
815+
816+ /*
817+ * imx_dsp_rproc_elf_load_segments() - load firmware segments to memory
818+ * @rproc: remote processor which will be booted using these fw segments
819+ * @fw: the ELF firmware image
820+ *
821+ * This function loads the firmware segments to memory, where the remote
822+ * processor expects them.
823+ *
824+ * Return: 0 on success and an appropriate error code otherwise
825+ */
826+ static int imx_dsp_rproc_elf_load_segments (struct rproc * rproc , const struct firmware * fw )
827+ {
828+ struct device * dev = & rproc -> dev ;
829+ const void * ehdr , * phdr ;
830+ int i , ret = 0 ;
831+ u16 phnum ;
832+ const u8 * elf_data = fw -> data ;
833+ u8 class = fw_elf_get_class (fw );
834+ u32 elf_phdr_get_size = elf_size_of_phdr (class );
835+
836+ ehdr = elf_data ;
837+ phnum = elf_hdr_get_e_phnum (class , ehdr );
838+ phdr = elf_data + elf_hdr_get_e_phoff (class , ehdr );
839+
840+ /* go through the available ELF segments */
841+ for (i = 0 ; i < phnum ; i ++ , phdr += elf_phdr_get_size ) {
842+ u64 da = elf_phdr_get_p_paddr (class , phdr );
843+ u64 memsz = elf_phdr_get_p_memsz (class , phdr );
844+ u64 filesz = elf_phdr_get_p_filesz (class , phdr );
845+ u64 offset = elf_phdr_get_p_offset (class , phdr );
846+ u32 type = elf_phdr_get_p_type (class , phdr );
847+ void * ptr ;
848+
849+ if (type != PT_LOAD || !memsz )
850+ continue ;
851+
852+ dev_dbg (dev , "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n" ,
853+ type , da , memsz , filesz );
854+
855+ if (filesz > memsz ) {
856+ dev_err (dev , "bad phdr filesz 0x%llx memsz 0x%llx\n" ,
857+ filesz , memsz );
858+ ret = - EINVAL ;
859+ break ;
860+ }
861+
862+ if (offset + filesz > fw -> size ) {
863+ dev_err (dev , "truncated fw: need 0x%llx avail 0x%zx\n" ,
864+ offset + filesz , fw -> size );
865+ ret = - EINVAL ;
866+ break ;
867+ }
868+
869+ if (!rproc_u64_fit_in_size_t (memsz )) {
870+ dev_err (dev , "size (%llx) does not fit in size_t type\n" ,
871+ memsz );
872+ ret = - EOVERFLOW ;
873+ break ;
874+ }
875+
876+ /* grab the kernel address for this device address */
877+ ptr = rproc_da_to_va (rproc , da , memsz , NULL );
878+ if (!ptr ) {
879+ dev_err (dev , "bad phdr da 0x%llx mem 0x%llx\n" , da ,
880+ memsz );
881+ ret = - EINVAL ;
882+ break ;
883+ }
884+
885+ /* put the segment where the remote processor expects it */
886+ if (filesz ) {
887+ ret = imx_dsp_rproc_memcpy (ptr , elf_data + offset , filesz );
888+ if (ret ) {
889+ dev_err (dev , "memory copy failed for da 0x%llx memsz 0x%llx\n" ,
890+ da , memsz );
891+ break ;
892+ }
893+ }
894+
895+ /* zero out remaining memory for this segment */
896+ if (memsz > filesz ) {
897+ ret = imx_dsp_rproc_memset (ptr + filesz , 0 , memsz - filesz );
898+ if (ret ) {
899+ dev_err (dev , "memset failed for da 0x%llx memsz 0x%llx\n" ,
900+ da , memsz );
901+ break ;
902+ }
903+ }
904+ }
905+
906+ return ret ;
907+ }
908+
724909static int imx_dsp_rproc_parse_fw (struct rproc * rproc , const struct firmware * fw )
725910{
726911 if (rproc_elf_load_rsc_table (rproc , fw ))
@@ -735,7 +920,7 @@ static const struct rproc_ops imx_dsp_rproc_ops = {
735920 .start = imx_dsp_rproc_start ,
736921 .stop = imx_dsp_rproc_stop ,
737922 .kick = imx_dsp_rproc_kick ,
738- .load = rproc_elf_load_segments ,
923+ .load = imx_dsp_rproc_elf_load_segments ,
739924 .parse_fw = imx_dsp_rproc_parse_fw ,
740925 .sanity_check = rproc_elf_sanity_check ,
741926 .get_boot_addr = rproc_elf_get_boot_addr ,
0 commit comments