Message ID | 20220613111759.1550578-2-conor.dooley@microchip.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add support for Microchip's pwm fpga core | expand |
Hi Conor,
I love your patch! Yet something to improve:
[auto build test ERROR on thierry-reding-pwm/for-next]
[also build test ERROR on linus/master v5.19-rc2 next-20220614]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/intel-lab-lkp/linux/commits/Conor-Dooley/pwm-add-microchip-soft-ip-corePWM-driver/20220613-211851
base: https://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git for-next
config: xtensa-allyesconfig (https://download.01.org/0day-ci/archive/20220614/202206141813.qgogbMAN-lkp@intel.com/config)
compiler: xtensa-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/4451a8420f9fd16ee9a0801fcf02f1ec04bb8ab0
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Conor-Dooley/pwm-add-microchip-soft-ip-corePWM-driver/20220613-211851
git checkout 4451a8420f9fd16ee9a0801fcf02f1ec04bb8ab0
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=xtensa SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
`.exit.text' referenced in section `__jump_table' of fs/cifs/cifsfs.o: defined in discarded section `.exit.text' of fs/cifs/cifsfs.o
`.exit.text' referenced in section `__jump_table' of fs/cifs/cifsfs.o: defined in discarded section `.exit.text' of fs/cifs/cifsfs.o
`.exit.text' referenced in section `__jump_table' of fs/fuse/inode.o: defined in discarded section `.exit.text' of fs/fuse/inode.o
`.exit.text' referenced in section `__jump_table' of fs/fuse/inode.o: defined in discarded section `.exit.text' of fs/fuse/inode.o
`.exit.text' referenced in section `__jump_table' of fs/ceph/super.o: defined in discarded section `.exit.text' of fs/ceph/super.o
`.exit.text' referenced in section `__jump_table' of fs/ceph/super.o: defined in discarded section `.exit.text' of fs/ceph/super.o
xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_enable.isra.0':
>> pwm-microchip-core.c:(.text+0x380): undefined reference to `__udivdi3'
xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_apply':
pwm-microchip-core.c:(.text+0x586): undefined reference to `__udivdi3'
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/rio_cm.o: defined in discarded section `.exit.text' of drivers/rapidio/rio_cm.o
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/rio_cm.o: defined in discarded section `.exit.text' of drivers/rapidio/rio_cm.o
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen2.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen2.o
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen2.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen2.o
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen2.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen2.o
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen2.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen2.o
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen3.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen3.o
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen3.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen3.o
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen3.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen3.o
`.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen3.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen3.o
`.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/vt8623fb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/vt8623fb.o
`.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/vt8623fb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/vt8623fb.o
`.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/s3fb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/s3fb.o
`.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/s3fb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/s3fb.o
`.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/arkfb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/arkfb.o
`.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/arkfb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/arkfb.o
`.exit.text' referenced in section `__jump_table' of drivers/misc/phantom.o: defined in discarded section `.exit.text' of drivers/misc/phantom.o
`.exit.text' referenced in section `__jump_table' of drivers/misc/phantom.o: defined in discarded section `.exit.text' of drivers/misc/phantom.o
`.exit.text' referenced in section `__jump_table' of drivers/misc/habanalabs/common/habanalabs_drv.o: defined in discarded section `.exit.text' of drivers/misc/habanalabs/common/habanalabs_drv.o
`.exit.text' referenced in section `__jump_table' of drivers/misc/habanalabs/common/habanalabs_drv.o: defined in discarded section `.exit.text' of drivers/misc/habanalabs/common/habanalabs_drv.o
`.exit.text' referenced in section `__jump_table' of drivers/scsi/fcoe/fcoe.o: defined in discarded section `.exit.text' of drivers/scsi/fcoe/fcoe.o
`.exit.text' referenced in section `__jump_table' of drivers/scsi/fcoe/fcoe.o: defined in discarded section `.exit.text' of drivers/scsi/fcoe/fcoe.o
`.exit.text' referenced in section `__jump_table' of drivers/scsi/cxgbi/libcxgbi.o: defined in discarded section `.exit.text' of drivers/scsi/cxgbi/libcxgbi.o
`.exit.text' referenced in section `__jump_table' of drivers/scsi/cxgbi/libcxgbi.o: defined in discarded section `.exit.text' of drivers/scsi/cxgbi/libcxgbi.o
`.exit.text' referenced in section `__jump_table' of drivers/target/target_core_configfs.o: defined in discarded section `.exit.text' of drivers/target/target_core_configfs.o
`.exit.text' referenced in section `__jump_table' of drivers/target/target_core_configfs.o: defined in discarded section `.exit.text' of drivers/target/target_core_configfs.o
`.exit.text' referenced in section `__jump_table' of drivers/mtd/maps/pcmciamtd.o: defined in discarded section `.exit.text' of drivers/mtd/maps/pcmciamtd.o
`.exit.text' referenced in section `__jump_table' of drivers/mtd/maps/pcmciamtd.o: defined in discarded section `.exit.text' of drivers/mtd/maps/pcmciamtd.o
`.exit.text' referenced in section `__jump_table' of drivers/net/wireless/zydas/zd1211rw/zd_usb.o: defined in discarded section `.exit.text' of drivers/net/wireless/zydas/zd1211rw/zd_usb.o
`.exit.text' referenced in section `__jump_table' of drivers/net/wireless/zydas/zd1211rw/zd_usb.o: defined in discarded section `.exit.text' of drivers/net/wireless/zydas/zd1211rw/zd_usb.o
`.exit.text' referenced in section `__jump_table' of drivers/net/wireless/ray_cs.o: defined in discarded section `.exit.text' of drivers/net/wireless/ray_cs.o
`.exit.text' referenced in section `__jump_table' of drivers/net/wireless/ray_cs.o: defined in discarded section `.exit.text' of drivers/net/wireless/ray_cs.o
`.exit.text' referenced in section `__jump_table' of drivers/net/wireless/mac80211_hwsim.o: defined in discarded section `.exit.text' of drivers/net/wireless/mac80211_hwsim.o
`.exit.text' referenced in section `__jump_table' of drivers/net/wireless/mac80211_hwsim.o: defined in discarded section `.exit.text' of drivers/net/wireless/mac80211_hwsim.o
`.exit.text' referenced in section `__jump_table' of drivers/usb/gadget/legacy/inode.o: defined in discarded section `.exit.text' of drivers/usb/gadget/legacy/inode.o
`.exit.text' referenced in section `__jump_table' of drivers/usb/gadget/legacy/inode.o: defined in discarded section `.exit.text' of drivers/usb/gadget/legacy/inode.o
`.exit.text' referenced in section `__jump_table' of drivers/usb/gadget/legacy/g_ffs.o: defined in discarded section `.exit.text' of drivers/usb/gadget/legacy/g_ffs.o
`.exit.text' referenced in section `__jump_table' of drivers/usb/gadget/legacy/g_ffs.o: defined in discarded section `.exit.text' of drivers/usb/gadget/legacy/g_ffs.o
`.exit.text' referenced in section `__jump_table' of drivers/media/common/siano/smscoreapi.o: defined in discarded section `.exit.text' of drivers/media/common/siano/smscoreapi.o
`.exit.text' referenced in section `__jump_table' of drivers/media/common/siano/smscoreapi.o: defined in discarded section `.exit.text' of drivers/media/common/siano/smscoreapi.o
`.exit.text' referenced in section `__jump_table' of drivers/vme/bridges/vme_fake.o: defined in discarded section `.exit.text' of drivers/vme/bridges/vme_fake.o
`.exit.text' referenced in section `__jump_table' of drivers/vme/bridges/vme_fake.o: defined in discarded section `.exit.text' of drivers/vme/bridges/vme_fake.o
`.exit.text' referenced in section `.data' of sound/soc/codecs/tlv320adc3xxx.o: defined in discarded section `.exit.text' of sound/soc/codecs/tlv320adc3xxx.o
`.exit.text' referenced in section `__jump_table' of net/netfilter/nf_conntrack_h323_main.o: defined in discarded section `.exit.text' of net/netfilter/nf_conntrack_h323_main.o
`.exit.text' referenced in section `__jump_table' of net/netfilter/nf_conntrack_h323_main.o: defined in discarded section `.exit.text' of net/netfilter/nf_conntrack_h323_main.o
`.exit.text' referenced in section `__jump_table' of net/netfilter/ipset/ip_set_core.o: defined in discarded section `.exit.text' of net/netfilter/ipset/ip_set_core.o
`.exit.text' referenced in section `__jump_table' of net/netfilter/ipset/ip_set_core.o: defined in discarded section `.exit.text' of net/netfilter/ipset/ip_set_core.o
`.exit.text' referenced in section `__jump_table' of net/ceph/ceph_common.o: defined in discarded section `.exit.text' of net/ceph/ceph_common.o
`.exit.text' referenced in section `__jump_table' of net/ceph/ceph_common.o: defined in discarded section `.exit.text' of net/ceph/ceph_common.o
> xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_enable.isra.0': >>> pwm-microchip-core.c:(.text+0x380): undefined reference to `__udivdi3' > xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_apply': > pwm-microchip-core.c:(.text+0x586): undefined reference to `__udivdi3' I assume this is me using functions that are only defined for 64 bit... On 14/06/2022 11:25, kernel test robot wrote: > Hi Conor, > > I love your patch! Yet something to improve: > > [auto build test ERROR on thierry-reding-pwm/for-next] > [also build test ERROR on linus/master v5.19-rc2 next-20220614] > [If your patch is applied to the wrong git tree, kindly drop us a note. > And when submitting patch, we suggest to use '--base' as documented in > https://git-scm.com/docs/git-format-patch] > > url: https://github.com/intel-lab-lkp/linux/commits/Conor-Dooley/pwm-add-microchip-soft-ip-corePWM-driver/20220613-211851 > base: https://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git for-next > config: xtensa-allyesconfig (https://download.01.org/0day-ci/archive/20220614/202206141813.qgogbMAN-lkp@intel.com/config) > compiler: xtensa-linux-gcc (GCC) 11.3.0 > reproduce (this is a W=1 build): > wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross > chmod +x ~/bin/make.cross > # https://github.com/intel-lab-lkp/linux/commit/4451a8420f9fd16ee9a0801fcf02f1ec04bb8ab0 > git remote add linux-review https://github.com/intel-lab-lkp/linux > git fetch --no-tags linux-review Conor-Dooley/pwm-add-microchip-soft-ip-corePWM-driver/20220613-211851 > git checkout 4451a8420f9fd16ee9a0801fcf02f1ec04bb8ab0 > # save the config file > mkdir build_dir && cp config build_dir/.config > COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=xtensa SHELL=/bin/bash > > If you fix the issue, kindly add following tag where applicable > Reported-by: kernel test robot <lkp@intel.com> > > All errors (new ones prefixed by >>): > > `.exit.text' referenced in section `__jump_table' of fs/cifs/cifsfs.o: defined in discarded section `.exit.text' of fs/cifs/cifsfs.o > `.exit.text' referenced in section `__jump_table' of fs/cifs/cifsfs.o: defined in discarded section `.exit.text' of fs/cifs/cifsfs.o > `.exit.text' referenced in section `__jump_table' of fs/fuse/inode.o: defined in discarded section `.exit.text' of fs/fuse/inode.o > `.exit.text' referenced in section `__jump_table' of fs/fuse/inode.o: defined in discarded section `.exit.text' of fs/fuse/inode.o > `.exit.text' referenced in section `__jump_table' of fs/ceph/super.o: defined in discarded section `.exit.text' of fs/ceph/super.o > `.exit.text' referenced in section `__jump_table' of fs/ceph/super.o: defined in discarded section `.exit.text' of fs/ceph/super.o > xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_enable.isra.0': >>> pwm-microchip-core.c:(.text+0x380): undefined reference to `__udivdi3' > xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_apply': > pwm-microchip-core.c:(.text+0x586): undefined reference to `__udivdi3' > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/rio_cm.o: defined in discarded section `.exit.text' of drivers/rapidio/rio_cm.o > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/rio_cm.o: defined in discarded section `.exit.text' of drivers/rapidio/rio_cm.o > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen2.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen2.o > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen2.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen2.o > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen2.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen2.o > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen2.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen2.o > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen3.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen3.o > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen3.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen3.o > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen3.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen3.o > `.exit.text' referenced in section `__jump_table' of drivers/rapidio/switches/idt_gen3.o: defined in discarded section `.exit.text' of drivers/rapidio/switches/idt_gen3.o > `.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/vt8623fb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/vt8623fb.o > `.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/vt8623fb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/vt8623fb.o > `.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/s3fb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/s3fb.o > `.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/s3fb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/s3fb.o > `.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/arkfb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/arkfb.o > `.exit.text' referenced in section `__jump_table' of drivers/video/fbdev/arkfb.o: defined in discarded section `.exit.text' of drivers/video/fbdev/arkfb.o > `.exit.text' referenced in section `__jump_table' of drivers/misc/phantom.o: defined in discarded section `.exit.text' of drivers/misc/phantom.o > `.exit.text' referenced in section `__jump_table' of drivers/misc/phantom.o: defined in discarded section `.exit.text' of drivers/misc/phantom.o > `.exit.text' referenced in section `__jump_table' of drivers/misc/habanalabs/common/habanalabs_drv.o: defined in discarded section `.exit.text' of drivers/misc/habanalabs/common/habanalabs_drv.o > `.exit.text' referenced in section `__jump_table' of drivers/misc/habanalabs/common/habanalabs_drv.o: defined in discarded section `.exit.text' of drivers/misc/habanalabs/common/habanalabs_drv.o > `.exit.text' referenced in section `__jump_table' of drivers/scsi/fcoe/fcoe.o: defined in discarded section `.exit.text' of drivers/scsi/fcoe/fcoe.o > `.exit.text' referenced in section `__jump_table' of drivers/scsi/fcoe/fcoe.o: defined in discarded section `.exit.text' of drivers/scsi/fcoe/fcoe.o > `.exit.text' referenced in section `__jump_table' of drivers/scsi/cxgbi/libcxgbi.o: defined in discarded section `.exit.text' of drivers/scsi/cxgbi/libcxgbi.o > `.exit.text' referenced in section `__jump_table' of drivers/scsi/cxgbi/libcxgbi.o: defined in discarded section `.exit.text' of drivers/scsi/cxgbi/libcxgbi.o > `.exit.text' referenced in section `__jump_table' of drivers/target/target_core_configfs.o: defined in discarded section `.exit.text' of drivers/target/target_core_configfs.o > `.exit.text' referenced in section `__jump_table' of drivers/target/target_core_configfs.o: defined in discarded section `.exit.text' of drivers/target/target_core_configfs.o > `.exit.text' referenced in section `__jump_table' of drivers/mtd/maps/pcmciamtd.o: defined in discarded section `.exit.text' of drivers/mtd/maps/pcmciamtd.o > `.exit.text' referenced in section `__jump_table' of drivers/mtd/maps/pcmciamtd.o: defined in discarded section `.exit.text' of drivers/mtd/maps/pcmciamtd.o > `.exit.text' referenced in section `__jump_table' of drivers/net/wireless/zydas/zd1211rw/zd_usb.o: defined in discarded section `.exit.text' of drivers/net/wireless/zydas/zd1211rw/zd_usb.o > `.exit.text' referenced in section `__jump_table' of drivers/net/wireless/zydas/zd1211rw/zd_usb.o: defined in discarded section `.exit.text' of drivers/net/wireless/zydas/zd1211rw/zd_usb.o > `.exit.text' referenced in section `__jump_table' of drivers/net/wireless/ray_cs.o: defined in discarded section `.exit.text' of drivers/net/wireless/ray_cs.o > `.exit.text' referenced in section `__jump_table' of drivers/net/wireless/ray_cs.o: defined in discarded section `.exit.text' of drivers/net/wireless/ray_cs.o > `.exit.text' referenced in section `__jump_table' of drivers/net/wireless/mac80211_hwsim.o: defined in discarded section `.exit.text' of drivers/net/wireless/mac80211_hwsim.o > `.exit.text' referenced in section `__jump_table' of drivers/net/wireless/mac80211_hwsim.o: defined in discarded section `.exit.text' of drivers/net/wireless/mac80211_hwsim.o > `.exit.text' referenced in section `__jump_table' of drivers/usb/gadget/legacy/inode.o: defined in discarded section `.exit.text' of drivers/usb/gadget/legacy/inode.o > `.exit.text' referenced in section `__jump_table' of drivers/usb/gadget/legacy/inode.o: defined in discarded section `.exit.text' of drivers/usb/gadget/legacy/inode.o > `.exit.text' referenced in section `__jump_table' of drivers/usb/gadget/legacy/g_ffs.o: defined in discarded section `.exit.text' of drivers/usb/gadget/legacy/g_ffs.o > `.exit.text' referenced in section `__jump_table' of drivers/usb/gadget/legacy/g_ffs.o: defined in discarded section `.exit.text' of drivers/usb/gadget/legacy/g_ffs.o > `.exit.text' referenced in section `__jump_table' of drivers/media/common/siano/smscoreapi.o: defined in discarded section `.exit.text' of drivers/media/common/siano/smscoreapi.o > `.exit.text' referenced in section `__jump_table' of drivers/media/common/siano/smscoreapi.o: defined in discarded section `.exit.text' of drivers/media/common/siano/smscoreapi.o > `.exit.text' referenced in section `__jump_table' of drivers/vme/bridges/vme_fake.o: defined in discarded section `.exit.text' of drivers/vme/bridges/vme_fake.o > `.exit.text' referenced in section `__jump_table' of drivers/vme/bridges/vme_fake.o: defined in discarded section `.exit.text' of drivers/vme/bridges/vme_fake.o > `.exit.text' referenced in section `.data' of sound/soc/codecs/tlv320adc3xxx.o: defined in discarded section `.exit.text' of sound/soc/codecs/tlv320adc3xxx.o > `.exit.text' referenced in section `__jump_table' of net/netfilter/nf_conntrack_h323_main.o: defined in discarded section `.exit.text' of net/netfilter/nf_conntrack_h323_main.o > `.exit.text' referenced in section `__jump_table' of net/netfilter/nf_conntrack_h323_main.o: defined in discarded section `.exit.text' of net/netfilter/nf_conntrack_h323_main.o > `.exit.text' referenced in section `__jump_table' of net/netfilter/ipset/ip_set_core.o: defined in discarded section `.exit.text' of net/netfilter/ipset/ip_set_core.o > `.exit.text' referenced in section `__jump_table' of net/netfilter/ipset/ip_set_core.o: defined in discarded section `.exit.text' of net/netfilter/ipset/ip_set_core.o > `.exit.text' referenced in section `__jump_table' of net/ceph/ceph_common.o: defined in discarded section `.exit.text' of net/ceph/ceph_common.o > `.exit.text' referenced in section `__jump_table' of net/ceph/ceph_common.o: defined in discarded section `.exit.text' of net/ceph/ceph_common.o >
On Tue, Jun 14, 2022 at 10:34:00AM +0000, Conor.Dooley@microchip.com wrote: > > xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_enable.isra.0': > >>> pwm-microchip-core.c:(.text+0x380): undefined reference to `__udivdi3' > > xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_apply': > > pwm-microchip-core.c:(.text+0x586): undefined reference to `__udivdi3' > > I assume this is me using functions that are only defined for 64 bit... This is usually a division with variables > int. Best regards Uwe
On 14/06/2022 13:13, Uwe Kleine-König wrote: > On Tue, Jun 14, 2022 at 10:34:00AM +0000, Conor.Dooley@microchip.com wrote: >>> xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_enable.isra.0': >>>>> pwm-microchip-core.c:(.text+0x380): undefined reference to `__udivdi3' >>> xtensa-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_apply': >>> pwm-microchip-core.c:(.text+0x586): undefined reference to `__udivdi3' >> >> I assume this is me using functions that are only defined for 64 bit... > > This is usually a division with variables > int. Aye, which I now have - hence the div64_u64(). Thanks, I'll go digging for a fix :) Conor.
Hello, On Mon, Jun 13, 2022 at 12:17:59PM +0100, Conor Dooley wrote: > Add a driver that supports the Microchip FPGA "soft" PWM IP core. > > Signed-off-by: Conor Dooley <conor.dooley@microchip.com> > --- > drivers/pwm/Kconfig | 10 + > drivers/pwm/Makefile | 1 + > drivers/pwm/pwm-microchip-core.c | 310 +++++++++++++++++++++++++++++++ > 3 files changed, 321 insertions(+) > create mode 100644 drivers/pwm/pwm-microchip-core.c > > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig > index 21e3b05a5153..a651848e444b 100644 > --- a/drivers/pwm/Kconfig > +++ b/drivers/pwm/Kconfig > @@ -383,6 +383,16 @@ config PWM_MEDIATEK > To compile this driver as a module, choose M here: the module > will be called pwm-mediatek. > > +config PWM_MICROCHIP_CORE > + tristate "Microchip corePWM PWM support" > + depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST > + depends on HAS_IOMEM && OF > + help > + PWM driver for Microchip FPGA soft IP core. > + > + To compile this driver as a module, choose M here: the module > + will be called pwm-microchip-core. > + > config PWM_MXS > tristate "Freescale MXS PWM support" > depends on ARCH_MXS || COMPILE_TEST > diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile > index 708840b7fba8..d29754c20f91 100644 > --- a/drivers/pwm/Makefile > +++ b/drivers/pwm/Makefile > @@ -33,6 +33,7 @@ obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o > obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o > obj-$(CONFIG_PWM_MESON) += pwm-meson.o > obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o > +obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o > obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o > obj-$(CONFIG_PWM_MXS) += pwm-mxs.o > obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o > diff --git a/drivers/pwm/pwm-microchip-core.c b/drivers/pwm/pwm-microchip-core.c > new file mode 100644 > index 000000000000..d2abc46deec4 > --- /dev/null > +++ b/drivers/pwm/pwm-microchip-core.c > @@ -0,0 +1,310 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * corePWM driver for Microchip "soft" FPGA IP cores. > + * > + * Copyright (c) 2021-2022 Microchip Corporation. All rights reserved. > + * Author: Conor Dooley <conor.dooley@microchip.com> > + * Documentation: > + * https://www.microsemi.com/document-portal/doc_download/1245275-corepwm-hb > + * > + * Limitations: > + * - If the IP block is configured without "shadow registers", all register > + * writes will take effect immediately, causing glitches on the output. > + * If shadow registers *are* enabled, a write to the "SYNC_UPDATE" register > + * notifies the core that it needs to update the registers defining the > + * waveform from the contents of the "shadow registers". > + * - The IP block has no concept of a duty cycle, only rising/falling edges of > + * the waveform. Unfortunately, if the rising & falling edges registers have > + * the same value written to them the IP block will do whichever of a rising > + * or a falling edge is possible. I.E. a 50% waveform at twice the requested > + * period. Therefore to get a 0% waveform, the output is set the max high/low > + * time depending on polarity. > + * - The PWM period is set for the whole IP block not per channel. The driver > + * will only change the period if no other PWM output is enabled. > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/platform_device.h> > +#include <linux/pwm.h> > +#include <linux/math.h> > + > +#define PREG_TO_VAL(PREG) ((PREG) + 1) > + > +#define COREPWM_PRESCALE_REG 0x00u > +#define COREPWM_PERIOD_REG 0x04u > +#define COREPWM_EN_LOW_REG 0x08u > +#define COREPWM_EN_HIGH_REG 0x0Cu > +#define COREPWM_SYNC_UPD_REG 0xE4u > +#define COREPWM_POSEDGE_OFFSET 0x10u > +#define COREPWM_NEGEDGE_OFFSET 0x14u > +#define COREPWM_CHANNEL_OFFSET 0x08u > + > +struct mchp_core_pwm_chip { > + struct pwm_chip chip; > + struct clk *clk; > + void __iomem *base; > +}; > + > +static inline struct mchp_core_pwm_chip *to_mchp_core_pwm(struct pwm_chip *chip) > +{ > + return container_of(chip, struct mchp_core_pwm_chip, chip); > +} > + > +static void mchp_core_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, bool enable) > +{ > + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); > + u8 channel_enable, reg_offset, shift; > + > + /* > + * There are two adjacent 8 bit control regs, the lower reg controls > + * 0-7 and the upper reg 8-15. Check if the pwm is in the upper reg > + * and if so, offset by the bus width. > + */ > + reg_offset = COREPWM_EN_LOW_REG + (pwm->hwpwm >> 3) * sizeof(u32); > + shift = pwm->hwpwm > 7 ? pwm->hwpwm - 8 : pwm->hwpwm; > + > + channel_enable = readb_relaxed(mchp_core_pwm->base + reg_offset); > + channel_enable &= ~(1 << shift); > + channel_enable |= (enable << shift); > + > + writel_relaxed(channel_enable, mchp_core_pwm->base + reg_offset); > +} > + > +static void mchp_core_pwm_apply_duty(struct pwm_chip *chip, struct pwm_device *pwm, > + const struct pwm_state *state) > +{ > + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); > + void __iomem *channel_base = mchp_core_pwm->base + pwm->hwpwm * COREPWM_CHANNEL_OFFSET; > + u64 duty_steps, period, tmp; > + u8 prescale, period_steps, posedge, negedge; > + > + prescale = PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_PRESCALE_REG)); > + period_steps = PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_PERIOD_REG)); > + period = period_steps * prescale * NSEC_PER_SEC; > + period = div64_u64(period, clk_get_rate(mchp_core_pwm->clk)); > + > + /* > + * Calculate the duty cycle in multiples of the prescaled period: > + * duty_steps = duty_in_ns / step_in_ns > + * step_in_ns = (prescale * NSEC_PER_SEC) / clk_rate > + * The code below is rearranged slightly to only divide once. > + * > + * Because the period is per channel, it is possible that the requested > + * duty cycle is longer than the period, in which case cap it to the > + * period. > + */ > + if (state->duty_cycle > period) { > + duty_steps = period_steps; > + } else { > + duty_steps = state->duty_cycle * clk_get_rate(mchp_core_pwm->clk); > + tmp = prescale * NSEC_PER_SEC; > + duty_steps = div64_u64(duty_steps, tmp); > + } > + > + /* > + * Turn the output on unless posedge == negedge, in which case the > + * duty is intended to be 0, but limitations of the IP block don't > + * allow a zero length duty cycle - so just set the max high/low time > + * respectively. > + */ > + if (state->polarity == PWM_POLARITY_INVERSED) { > + negedge = !duty_steps ? period_steps : 0u; > + posedge = duty_steps; > + } else { > + posedge = !duty_steps ? period_steps : 0u; > + negedge = duty_steps; > + } > + > + writel_relaxed(posedge, channel_base + COREPWM_POSEDGE_OFFSET); > + writel_relaxed(negedge, channel_base + COREPWM_NEGEDGE_OFFSET); > +} > + > +static void mchp_core_pwm_apply_period(struct pwm_chip *chip, const struct pwm_state *state) > +{ > + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); > + u64 tmp = state->period; > + u8 prescale, period_steps; > + > + /* > + * Calculate the period cycles and prescale values. > + * The registers are each 8 bits wide & multiplied to compute the period > + * so the maximum period that can be generated is 0xFFFF times the period > + * of the input clock. > + */ > + tmp *= clk_get_rate(mchp_core_pwm->clk); > + do_div(tmp, NSEC_PER_SEC); > + > + if (tmp > 0xFFFFu) { > + prescale = 0xFFu; > + period_steps = 0xFFu; > + } else { > + prescale = tmp >> 8; > + period_steps = tmp / PREG_TO_VAL(prescale) - 1; Here is the 64bit division. Best regards Uwe
On 14/06/2022 13:34, Uwe Kleine-König wrote: > Hello, > > On Mon, Jun 13, 2022 at 12:17:59PM +0100, Conor Dooley wrote: >> Add a driver that supports the Microchip FPGA "soft" PWM IP core. >> >> Signed-off-by: Conor Dooley <conor.dooley@microchip.com> >> --- >> drivers/pwm/Kconfig | 10 + >> drivers/pwm/Makefile | 1 + >> drivers/pwm/pwm-microchip-core.c | 310 +++++++++++++++++++++++++++++++ >> 3 files changed, 321 insertions(+) >> create mode 100644 drivers/pwm/pwm-microchip-core.c >> >> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig >> index 21e3b05a5153..a651848e444b 100644 >> --- a/drivers/pwm/Kconfig >> +++ b/drivers/pwm/Kconfig >> @@ -383,6 +383,16 @@ config PWM_MEDIATEK >> To compile this driver as a module, choose M here: the module >> will be called pwm-mediatek. >> >> +config PWM_MICROCHIP_CORE >> + tristate "Microchip corePWM PWM support" >> + depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST >> + depends on HAS_IOMEM && OF >> + help >> + PWM driver for Microchip FPGA soft IP core. >> + >> + To compile this driver as a module, choose M here: the module >> + will be called pwm-microchip-core. >> + >> config PWM_MXS >> tristate "Freescale MXS PWM support" >> depends on ARCH_MXS || COMPILE_TEST >> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile >> index 708840b7fba8..d29754c20f91 100644 >> --- a/drivers/pwm/Makefile >> +++ b/drivers/pwm/Makefile >> @@ -33,6 +33,7 @@ obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o >> obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o >> obj-$(CONFIG_PWM_MESON) += pwm-meson.o >> obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o >> +obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o >> obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o >> obj-$(CONFIG_PWM_MXS) += pwm-mxs.o >> obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o >> diff --git a/drivers/pwm/pwm-microchip-core.c b/drivers/pwm/pwm-microchip-core.c >> new file mode 100644 >> index 000000000000..d2abc46deec4 >> --- /dev/null >> +++ b/drivers/pwm/pwm-microchip-core.c >> @@ -0,0 +1,310 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * corePWM driver for Microchip "soft" FPGA IP cores. >> + * >> + * Copyright (c) 2021-2022 Microchip Corporation. All rights reserved. >> + * Author: Conor Dooley <conor.dooley@microchip.com> >> + * Documentation: >> + * https://www.microsemi.com/document-portal/doc_download/1245275-corepwm-hb >> + * >> + * Limitations: >> + * - If the IP block is configured without "shadow registers", all register >> + * writes will take effect immediately, causing glitches on the output. >> + * If shadow registers *are* enabled, a write to the "SYNC_UPDATE" register >> + * notifies the core that it needs to update the registers defining the >> + * waveform from the contents of the "shadow registers". >> + * - The IP block has no concept of a duty cycle, only rising/falling edges of >> + * the waveform. Unfortunately, if the rising & falling edges registers have >> + * the same value written to them the IP block will do whichever of a rising >> + * or a falling edge is possible. I.E. a 50% waveform at twice the requested >> + * period. Therefore to get a 0% waveform, the output is set the max high/low >> + * time depending on polarity. >> + * - The PWM period is set for the whole IP block not per channel. The driver >> + * will only change the period if no other PWM output is enabled. >> + */ >> + >> +#include <linux/clk.h> >> +#include <linux/delay.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/module.h> >> +#include <linux/of_device.h> >> +#include <linux/platform_device.h> >> +#include <linux/pwm.h> >> +#include <linux/math.h> >> + >> +#define PREG_TO_VAL(PREG) ((PREG) + 1) >> + >> +#define COREPWM_PRESCALE_REG 0x00u >> +#define COREPWM_PERIOD_REG 0x04u >> +#define COREPWM_EN_LOW_REG 0x08u >> +#define COREPWM_EN_HIGH_REG 0x0Cu >> +#define COREPWM_SYNC_UPD_REG 0xE4u >> +#define COREPWM_POSEDGE_OFFSET 0x10u >> +#define COREPWM_NEGEDGE_OFFSET 0x14u >> +#define COREPWM_CHANNEL_OFFSET 0x08u >> + >> +struct mchp_core_pwm_chip { >> + struct pwm_chip chip; >> + struct clk *clk; >> + void __iomem *base; >> +}; >> + >> +static inline struct mchp_core_pwm_chip *to_mchp_core_pwm(struct pwm_chip *chip) >> +{ >> + return container_of(chip, struct mchp_core_pwm_chip, chip); >> +} >> + >> +static void mchp_core_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, bool enable) >> +{ >> + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); >> + u8 channel_enable, reg_offset, shift; >> + >> + /* >> + * There are two adjacent 8 bit control regs, the lower reg controls >> + * 0-7 and the upper reg 8-15. Check if the pwm is in the upper reg >> + * and if so, offset by the bus width. >> + */ >> + reg_offset = COREPWM_EN_LOW_REG + (pwm->hwpwm >> 3) * sizeof(u32); >> + shift = pwm->hwpwm > 7 ? pwm->hwpwm - 8 : pwm->hwpwm; >> + >> + channel_enable = readb_relaxed(mchp_core_pwm->base + reg_offset); >> + channel_enable &= ~(1 << shift); >> + channel_enable |= (enable << shift); >> + >> + writel_relaxed(channel_enable, mchp_core_pwm->base + reg_offset); >> +} >> + >> +static void mchp_core_pwm_apply_duty(struct pwm_chip *chip, struct pwm_device *pwm, >> + const struct pwm_state *state) >> +{ >> + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); >> + void __iomem *channel_base = mchp_core_pwm->base + pwm->hwpwm * COREPWM_CHANNEL_OFFSET; >> + u64 duty_steps, period, tmp; >> + u8 prescale, period_steps, posedge, negedge; >> + >> + prescale = PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_PRESCALE_REG)); >> + period_steps = PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_PERIOD_REG)); >> + period = period_steps * prescale * NSEC_PER_SEC; >> + period = div64_u64(period, clk_get_rate(mchp_core_pwm->clk)); >> + >> + /* >> + * Calculate the duty cycle in multiples of the prescaled period: >> + * duty_steps = duty_in_ns / step_in_ns >> + * step_in_ns = (prescale * NSEC_PER_SEC) / clk_rate >> + * The code below is rearranged slightly to only divide once. >> + * >> + * Because the period is per channel, it is possible that the requested >> + * duty cycle is longer than the period, in which case cap it to the >> + * period. >> + */ >> + if (state->duty_cycle > period) { >> + duty_steps = period_steps; >> + } else { >> + duty_steps = state->duty_cycle * clk_get_rate(mchp_core_pwm->clk); >> + tmp = prescale * NSEC_PER_SEC; >> + duty_steps = div64_u64(duty_steps, tmp); >> + } >> + >> + /* >> + * Turn the output on unless posedge == negedge, in which case the >> + * duty is intended to be 0, but limitations of the IP block don't >> + * allow a zero length duty cycle - so just set the max high/low time >> + * respectively. >> + */ >> + if (state->polarity == PWM_POLARITY_INVERSED) { >> + negedge = !duty_steps ? period_steps : 0u; >> + posedge = duty_steps; >> + } else { >> + posedge = !duty_steps ? period_steps : 0u; >> + negedge = duty_steps; >> + } >> + >> + writel_relaxed(posedge, channel_base + COREPWM_POSEDGE_OFFSET); >> + writel_relaxed(negedge, channel_base + COREPWM_NEGEDGE_OFFSET); >> +} >> + >> +static void mchp_core_pwm_apply_period(struct pwm_chip *chip, const struct pwm_state *state) >> +{ >> + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); >> + u64 tmp = state->period; >> + u8 prescale, period_steps; >> + >> + /* >> + * Calculate the period cycles and prescale values. >> + * The registers are each 8 bits wide & multiplied to compute the period >> + * so the maximum period that can be generated is 0xFFFF times the period >> + * of the input clock. >> + */ >> + tmp *= clk_get_rate(mchp_core_pwm->clk); >> + do_div(tmp, NSEC_PER_SEC); >> + >> + if (tmp > 0xFFFFu) { >> + prescale = 0xFFu; >> + period_steps = 0xFFu; >> + } else { >> + prescale = tmp >> 8; >> + period_steps = tmp / PREG_TO_VAL(prescale) - 1; > > Here is the 64bit division. > Thanks :) I would prob have missed that too, since I was thinking div /by/ > int.
Hi Conor,
I love your patch! Yet something to improve:
[auto build test ERROR on thierry-reding-pwm/for-next]
[also build test ERROR on linus/master v5.19-rc2 next-20220615]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/intel-lab-lkp/linux/commits/Conor-Dooley/pwm-add-microchip-soft-ip-corePWM-driver/20220613-211851
base: https://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git for-next
config: powerpc-allyesconfig (https://download.01.org/0day-ci/archive/20220616/202206160239.6lkHbYaU-lkp@intel.com/config)
compiler: powerpc-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/4451a8420f9fd16ee9a0801fcf02f1ec04bb8ab0
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Conor-Dooley/pwm-add-microchip-soft-ip-corePWM-driver/20220613-211851
git checkout 4451a8420f9fd16ee9a0801fcf02f1ec04bb8ab0
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
powerpc-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_apply':
>> pwm-microchip-core.c:(.text.mchp_core_pwm_apply+0x290): undefined reference to `__udivdi3'
On 15/06/2022 20:14, kernel test robot wrote: > Hi Conor, > > I love your patch! Yet something to improve: > > [auto build test ERROR on thierry-reding-pwm/for-next] > [also build test ERROR on linus/master v5.19-rc2 next-20220615] > [If your patch is applied to the wrong git tree, kindly drop us a note. > And when submitting patch, we suggest to use '--base' as documented in > https://git-scm.com/docs/git-format-patch] > > url: https://github.com/intel-lab-lkp/linux/commits/Conor-Dooley/pwm-add-microchip-soft-ip-corePWM-driver/20220613-211851 > base: https://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git for-next > config: powerpc-allyesconfig (https://download.01.org/0day-ci/archive/20220616/202206160239.6lkHbYaU-lkp@intel.com/config) > compiler: powerpc-linux-gcc (GCC) 11.3.0 > reproduce (this is a W=1 build): > wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross > chmod +x ~/bin/make.cross > # https://github.com/intel-lab-lkp/linux/commit/4451a8420f9fd16ee9a0801fcf02f1ec04bb8ab0 > git remote add linux-review https://github.com/intel-lab-lkp/linux > git fetch --no-tags linux-review Conor-Dooley/pwm-add-microchip-soft-ip-corePWM-driver/20220613-211851 > git checkout 4451a8420f9fd16ee9a0801fcf02f1ec04bb8ab0 > # save the config file > mkdir build_dir && cp config build_dir/.config > COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash > > If you fix the issue, kindly add following tag where applicable > Reported-by: kernel test robot <lkp@intel.com> > > All errors (new ones prefixed by >>): > > powerpc-linux-ld: drivers/pwm/pwm-microchip-core.o: in function `mchp_core_pwm_apply': >>> pwm-microchip-core.c:(.text.mchp_core_pwm_apply+0x290): undefined reference to `__udivdi3' Not new :) (well on this arch it is I guess) FWIW Uwe, the fix was obv. not difficult but I ran into more problems with idempotency while checking before resending with the fix. I had fixed all the idempotency problems with my v2 but I think turning on the shadow registers in the core changed the behaviour I was relying on so I won't send v3 with this fix until that point. Thanks, Cnor.
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 21e3b05a5153..a651848e444b 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -383,6 +383,16 @@ config PWM_MEDIATEK To compile this driver as a module, choose M here: the module will be called pwm-mediatek. +config PWM_MICROCHIP_CORE + tristate "Microchip corePWM PWM support" + depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST + depends on HAS_IOMEM && OF + help + PWM driver for Microchip FPGA soft IP core. + + To compile this driver as a module, choose M here: the module + will be called pwm-microchip-core. + config PWM_MXS tristate "Freescale MXS PWM support" depends on ARCH_MXS || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 708840b7fba8..d29754c20f91 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o obj-$(CONFIG_PWM_MESON) += pwm-meson.o obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o +obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o diff --git a/drivers/pwm/pwm-microchip-core.c b/drivers/pwm/pwm-microchip-core.c new file mode 100644 index 000000000000..d2abc46deec4 --- /dev/null +++ b/drivers/pwm/pwm-microchip-core.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * corePWM driver for Microchip "soft" FPGA IP cores. + * + * Copyright (c) 2021-2022 Microchip Corporation. All rights reserved. + * Author: Conor Dooley <conor.dooley@microchip.com> + * Documentation: + * https://www.microsemi.com/document-portal/doc_download/1245275-corepwm-hb + * + * Limitations: + * - If the IP block is configured without "shadow registers", all register + * writes will take effect immediately, causing glitches on the output. + * If shadow registers *are* enabled, a write to the "SYNC_UPDATE" register + * notifies the core that it needs to update the registers defining the + * waveform from the contents of the "shadow registers". + * - The IP block has no concept of a duty cycle, only rising/falling edges of + * the waveform. Unfortunately, if the rising & falling edges registers have + * the same value written to them the IP block will do whichever of a rising + * or a falling edge is possible. I.E. a 50% waveform at twice the requested + * period. Therefore to get a 0% waveform, the output is set the max high/low + * time depending on polarity. + * - The PWM period is set for the whole IP block not per channel. The driver + * will only change the period if no other PWM output is enabled. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/math.h> + +#define PREG_TO_VAL(PREG) ((PREG) + 1) + +#define COREPWM_PRESCALE_REG 0x00u +#define COREPWM_PERIOD_REG 0x04u +#define COREPWM_EN_LOW_REG 0x08u +#define COREPWM_EN_HIGH_REG 0x0Cu +#define COREPWM_SYNC_UPD_REG 0xE4u +#define COREPWM_POSEDGE_OFFSET 0x10u +#define COREPWM_NEGEDGE_OFFSET 0x14u +#define COREPWM_CHANNEL_OFFSET 0x08u + +struct mchp_core_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + void __iomem *base; +}; + +static inline struct mchp_core_pwm_chip *to_mchp_core_pwm(struct pwm_chip *chip) +{ + return container_of(chip, struct mchp_core_pwm_chip, chip); +} + +static void mchp_core_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, bool enable) +{ + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); + u8 channel_enable, reg_offset, shift; + + /* + * There are two adjacent 8 bit control regs, the lower reg controls + * 0-7 and the upper reg 8-15. Check if the pwm is in the upper reg + * and if so, offset by the bus width. + */ + reg_offset = COREPWM_EN_LOW_REG + (pwm->hwpwm >> 3) * sizeof(u32); + shift = pwm->hwpwm > 7 ? pwm->hwpwm - 8 : pwm->hwpwm; + + channel_enable = readb_relaxed(mchp_core_pwm->base + reg_offset); + channel_enable &= ~(1 << shift); + channel_enable |= (enable << shift); + + writel_relaxed(channel_enable, mchp_core_pwm->base + reg_offset); +} + +static void mchp_core_pwm_apply_duty(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); + void __iomem *channel_base = mchp_core_pwm->base + pwm->hwpwm * COREPWM_CHANNEL_OFFSET; + u64 duty_steps, period, tmp; + u8 prescale, period_steps, posedge, negedge; + + prescale = PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_PRESCALE_REG)); + period_steps = PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_PERIOD_REG)); + period = period_steps * prescale * NSEC_PER_SEC; + period = div64_u64(period, clk_get_rate(mchp_core_pwm->clk)); + + /* + * Calculate the duty cycle in multiples of the prescaled period: + * duty_steps = duty_in_ns / step_in_ns + * step_in_ns = (prescale * NSEC_PER_SEC) / clk_rate + * The code below is rearranged slightly to only divide once. + * + * Because the period is per channel, it is possible that the requested + * duty cycle is longer than the period, in which case cap it to the + * period. + */ + if (state->duty_cycle > period) { + duty_steps = period_steps; + } else { + duty_steps = state->duty_cycle * clk_get_rate(mchp_core_pwm->clk); + tmp = prescale * NSEC_PER_SEC; + duty_steps = div64_u64(duty_steps, tmp); + } + + /* + * Turn the output on unless posedge == negedge, in which case the + * duty is intended to be 0, but limitations of the IP block don't + * allow a zero length duty cycle - so just set the max high/low time + * respectively. + */ + if (state->polarity == PWM_POLARITY_INVERSED) { + negedge = !duty_steps ? period_steps : 0u; + posedge = duty_steps; + } else { + posedge = !duty_steps ? period_steps : 0u; + negedge = duty_steps; + } + + writel_relaxed(posedge, channel_base + COREPWM_POSEDGE_OFFSET); + writel_relaxed(negedge, channel_base + COREPWM_NEGEDGE_OFFSET); +} + +static void mchp_core_pwm_apply_period(struct pwm_chip *chip, const struct pwm_state *state) +{ + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); + u64 tmp = state->period; + u8 prescale, period_steps; + + /* + * Calculate the period cycles and prescale values. + * The registers are each 8 bits wide & multiplied to compute the period + * so the maximum period that can be generated is 0xFFFF times the period + * of the input clock. + */ + tmp *= clk_get_rate(mchp_core_pwm->clk); + do_div(tmp, NSEC_PER_SEC); + + if (tmp > 0xFFFFu) { + prescale = 0xFFu; + period_steps = 0xFFu; + } else { + prescale = tmp >> 8; + period_steps = tmp / PREG_TO_VAL(prescale) - 1; + } + + writel_relaxed(prescale, mchp_core_pwm->base + COREPWM_PRESCALE_REG); + writel_relaxed(period_steps, mchp_core_pwm->base + COREPWM_PERIOD_REG); +} + +static int mchp_core_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); + struct pwm_state current_state = pwm->state; + bool period_locked; + u16 channel_enabled; + + if (!state->enabled) { + mchp_core_pwm_enable(chip, pwm, false); + return 0; + } + + /* + * If the only thing that has changed is the duty cycle or the polarity, + * we can shortcut the calculations and just compute/apply the new duty + * cycle pos & neg edges + * As all the channels share the same period, do not allow it to be + * changed if any other channels are enabled. + */ + channel_enabled = (((u16)readb_relaxed(mchp_core_pwm->base + COREPWM_EN_HIGH_REG) << 8) | + readb_relaxed(mchp_core_pwm->base + COREPWM_EN_LOW_REG)); + period_locked = channel_enabled & ~(1 << pwm->hwpwm); + + if ((!current_state.enabled || current_state.period != state->period) && !period_locked) { + mchp_core_pwm_apply_period(chip, state); + + /* + * A short delay is required before the newly written values can + * be read back on the bus + */ + usleep_range(50, 100); + } + + mchp_core_pwm_apply_duty(chip, pwm, state); + + /* + * Notify the block to update the waveform from the shadow registers. + * This is a NOP if shadow registers are not enabled. + */ + writel_relaxed(1U, mchp_core_pwm->base + COREPWM_SYNC_UPD_REG); + + mchp_core_pwm_enable(chip, pwm, true); + + return 0; +} + +static void mchp_core_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); + void __iomem *channel_base = mchp_core_pwm->base + pwm->hwpwm * COREPWM_CHANNEL_OFFSET; + u8 prescale, period_steps, duty_steps; + u8 posedge, negedge; + u16 channel_enabled; + + channel_enabled = (((u16)readb_relaxed(mchp_core_pwm->base + COREPWM_EN_HIGH_REG) << 8) | + readb_relaxed(mchp_core_pwm->base + COREPWM_EN_LOW_REG)); + + if (channel_enabled & 1 << pwm->hwpwm) + state->enabled = true; + else + state->enabled = false; + + prescale = PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_PRESCALE_REG)); + + posedge = readb_relaxed(channel_base + COREPWM_POSEDGE_OFFSET); + negedge = readb_relaxed(channel_base + COREPWM_NEGEDGE_OFFSET); + + duty_steps = abs((s16)posedge - (s16)negedge); + state->duty_cycle = duty_steps * prescale * NSEC_PER_SEC; + do_div(state->duty_cycle, clk_get_rate(mchp_core_pwm->clk)); + + state->polarity = negedge < posedge ? PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL; + + period_steps = PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + COREPWM_PERIOD_REG)); + state->period = period_steps * prescale * NSEC_PER_SEC; + do_div(state->period, clk_get_rate(mchp_core_pwm->clk)); +} + +static const struct pwm_ops mchp_core_pwm_ops = { + .apply = mchp_core_pwm_apply, + .get_state = mchp_core_pwm_get_state, + .owner = THIS_MODULE, +}; + +static const struct of_device_id mchp_core_of_match[] = { + { + .compatible = "microchip,corepwm-rtl-v4", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mchp_core_of_match); + +static int mchp_core_pwm_probe(struct platform_device *pdev) +{ + struct mchp_core_pwm_chip *mchp_pwm; + struct resource *regs; + int ret; + + mchp_pwm = devm_kzalloc(&pdev->dev, sizeof(*mchp_pwm), GFP_KERNEL); + if (!mchp_pwm) + return -ENOMEM; + + mchp_pwm->base = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); + if (IS_ERR(mchp_pwm->base)) + return PTR_ERR(mchp_pwm->base); + + mchp_pwm->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mchp_pwm->clk)) + return PTR_ERR(mchp_pwm->clk); + + ret = clk_prepare_enable(mchp_pwm->clk); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to prepare PWM clock\n"); + + mchp_pwm->chip.dev = &pdev->dev; + mchp_pwm->chip.ops = &mchp_core_pwm_ops; + mchp_pwm->chip.of_xlate = of_pwm_xlate_with_flags; + mchp_pwm->chip.of_pwm_n_cells = 3; + mchp_pwm->chip.npwm = 16; + + ret = pwmchip_add(&mchp_pwm->chip); + if (ret < 0) { + clk_disable_unprepare(mchp_pwm->clk); + return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n"); + } + + platform_set_drvdata(pdev, mchp_pwm); + dev_info(&pdev->dev, "Successfully registered Microchip corePWM\n"); + + return 0; +} + +static int mchp_core_pwm_remove(struct platform_device *pdev) +{ + struct mchp_core_pwm_chip *mchp_pwm = platform_get_drvdata(pdev); + + pwmchip_remove(&mchp_pwm->chip); + clk_disable_unprepare(mchp_pwm->clk); + + return 0; +} + +static struct platform_driver mchp_core_pwm_driver = { + .driver = { + .name = "mchp-core-pwm", + .of_match_table = mchp_core_of_match, + }, + .probe = mchp_core_pwm_probe, + .remove = mchp_core_pwm_remove, +}; +module_platform_driver(mchp_core_pwm_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); +MODULE_DESCRIPTION("corePWM driver for Microchip FPGAs");
Add a driver that supports the Microchip FPGA "soft" PWM IP core. Signed-off-by: Conor Dooley <conor.dooley@microchip.com> --- drivers/pwm/Kconfig | 10 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-microchip-core.c | 310 +++++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+) create mode 100644 drivers/pwm/pwm-microchip-core.c