* [PATCH] fix platform_no_drv_owner.cocci warnings [not found] <201501160912.S502Nlmz%fengguang.wu@intel.com> @ 2015-01-16 1:06 ` kbuild test robot 2015-03-11 11:05 ` Thierry Reding 0 siblings, 1 reply; 40+ messages in thread From: kbuild test robot @ 2015-01-16 1:06 UTC (permalink / raw) To: Linus Walleij; +Cc: kbuild-all, Thierry Reding, linux-pwm, linux-kernel drivers/pwm/pwm-stmpe.c:99:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- pwm-stmpe.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/pwm/pwm-stmpe.c +++ b/drivers/pwm/pwm-stmpe.c @@ -96,7 +96,6 @@ static int stmpe_pwm_remove(struct platf static struct platform_driver stmpe_pwm_driver = { .driver = { .name = "stmpe-pwm", - .owner = THIS_MODULE, }, .probe = stmpe_pwm_probe, .remove = stmpe_pwm_remove, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-01-16 1:06 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot @ 2015-03-11 11:05 ` Thierry Reding 2015-03-18 1:38 ` Linus Walleij 0 siblings, 1 reply; 40+ messages in thread From: Thierry Reding @ 2015-03-11 11:05 UTC (permalink / raw) To: kbuild test robot; +Cc: Linus Walleij, kbuild-all, linux-pwm, linux-kernel [-- Attachment #1: Type: text/plain, Size: 503 bytes --] On Fri, Jan 16, 2015 at 09:06:14AM +0800, kbuild test robot wrote: > drivers/pwm/pwm-stmpe.c:99:3-8: No need to set .owner here. The core will do it. > > Remove .owner field if calls are used which set it automatically > > Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci > > Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> > --- > > pwm-stmpe.c | 1 - > 1 file changed, 1 deletion(-) Erm... this driver doesn't exist in my tree. What's going on? Thierry [-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-03-11 11:05 ` Thierry Reding @ 2015-03-18 1:38 ` Linus Walleij 2015-03-24 10:43 ` Thierry Reding 0 siblings, 1 reply; 40+ messages in thread From: Linus Walleij @ 2015-03-18 1:38 UTC (permalink / raw) To: Thierry Reding; +Cc: kbuild test robot, kbuild-all, linux-pwm, linux-kernel On Wed, Mar 11, 2015 at 12:05 PM, Thierry Reding <thierry.reding@gmail.com> wrote: > On Fri, Jan 16, 2015 at 09:06:14AM +0800, kbuild test robot wrote: >> drivers/pwm/pwm-stmpe.c:99:3-8: No need to set .owner here. The core will do it. >> >> Remove .owner field if calls are used which set it automatically >> >> Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci >> >> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> >> --- >> >> pwm-stmpe.c | 1 - >> 1 file changed, 1 deletion(-) > > Erm... this driver doesn't exist in my tree. What's going on? What's going on is that the 0day build robot builds everything it finds, including experimental branches. I dunno why it's mailing you though, probably I already added your CC on the patch... stupid me. A bit much to think of. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-03-18 1:38 ` Linus Walleij @ 2015-03-24 10:43 ` Thierry Reding 2015-03-25 1:56 ` Fengguang Wu 0 siblings, 1 reply; 40+ messages in thread From: Thierry Reding @ 2015-03-24 10:43 UTC (permalink / raw) To: Linus Walleij; +Cc: kbuild test robot, kbuild-all, linux-pwm, linux-kernel [-- Attachment #1: Type: text/plain, Size: 1140 bytes --] On Wed, Mar 18, 2015 at 02:38:18AM +0100, Linus Walleij wrote: > On Wed, Mar 11, 2015 at 12:05 PM, Thierry Reding > <thierry.reding@gmail.com> wrote: > > On Fri, Jan 16, 2015 at 09:06:14AM +0800, kbuild test robot wrote: > >> drivers/pwm/pwm-stmpe.c:99:3-8: No need to set .owner here. The core will do it. > >> > >> Remove .owner field if calls are used which set it automatically > >> > >> Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci > >> > >> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> > >> --- > >> > >> pwm-stmpe.c | 1 - > >> 1 file changed, 1 deletion(-) > > > > Erm... this driver doesn't exist in my tree. What's going on? > > What's going on is that the 0day build robot builds everything it > finds, including experimental branches. > > I dunno why it's mailing you though, probably I already added your > CC on the patch... stupid me. A bit much to think of. I guess the robot generates this patch and then runs get_maintainers.pl on it, so it's doing the right thing. Anyway, I'll know to ignore these in the future if drivers aren't in my tree yet. Thierry [-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-03-24 10:43 ` Thierry Reding @ 2015-03-25 1:56 ` Fengguang Wu 0 siblings, 0 replies; 40+ messages in thread From: Fengguang Wu @ 2015-03-25 1:56 UTC (permalink / raw) To: Thierry Reding; +Cc: Linus Walleij, kbuild-all, linux-pwm, linux-kernel On Tue, Mar 24, 2015 at 11:43:41AM +0100, Thierry Reding wrote: > On Wed, Mar 18, 2015 at 02:38:18AM +0100, Linus Walleij wrote: > > On Wed, Mar 11, 2015 at 12:05 PM, Thierry Reding > > <thierry.reding@gmail.com> wrote: > > > On Fri, Jan 16, 2015 at 09:06:14AM +0800, kbuild test robot wrote: > > >> drivers/pwm/pwm-stmpe.c:99:3-8: No need to set .owner here. The core will do it. > > >> > > >> Remove .owner field if calls are used which set it automatically > > >> > > >> Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci > > >> > > >> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> > > >> --- > > >> > > >> pwm-stmpe.c | 1 - > > >> 1 file changed, 1 deletion(-) > > > > > > Erm... this driver doesn't exist in my tree. What's going on? > > > > What's going on is that the 0day build robot builds everything it > > finds, including experimental branches. > > > > I dunno why it's mailing you though, probably I already added your > > CC on the patch... stupid me. A bit much to think of. > > I guess the robot generates this patch and then runs get_maintainers.pl > on it, so it's doing the right thing. Anyway, I'll know to ignore these > in the future if drivers aren't in my tree yet. Sorry I've added some heuristics to avoid adding CC from get_maintainers.pl for the likely private branches. It should reduce such confusions and noises considerably in future. Thanks, Fengguang ^ permalink raw reply [flat|nested] 40+ messages in thread
[parent not found: <201502131224.TGzEj1Ve%fengguang.wu@intel.com>]
* [PATCH] fix platform_no_drv_owner.cocci warnings [not found] <201502131224.TGzEj1Ve%fengguang.wu@intel.com> @ 2015-02-13 4:49 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2015-02-13 4:49 UTC (permalink / raw) To: Rob Clark Cc: kbuild-all, Mike Turquette, Stephen Boyd, Srinivas Kandagatla, linux-kernel drivers/clk/qcom/clk-rpm.c:262:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- clk-rpm.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/clk/qcom/clk-rpm.c +++ b/drivers/clk/qcom/clk-rpm.c @@ -259,7 +259,6 @@ static struct platform_driver rpm_clk_dr .probe = rpm_clk_probe, .driver = { .name = "qcom-rpm-clk", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(clk_rpm_of_match), }, }; ^ permalink raw reply [flat|nested] 40+ messages in thread
[parent not found: <201503291531.GP28FwKy%fengguang.wu@intel.com>]
* [PATCH] fix platform_no_drv_owner.cocci warnings [not found] <201503291531.GP28FwKy%fengguang.wu@intel.com> @ 2015-03-29 7:33 ` kbuild test robot 2015-03-29 16:24 ` Mark Brown 0 siblings, 1 reply; 40+ messages in thread From: kbuild test robot @ 2015-03-29 7:33 UTC (permalink / raw) To: Paul Walmsley Cc: kbuild-all, Thierry Reding, Liam Girdwood, Mark Brown, linux-kernel drivers/regulator/max77620-regulator.c:800:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Paul Walmsley <pwalmsley@nvidia.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- max77620-regulator.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/regulator/max77620-regulator.c +++ b/drivers/regulator/max77620-regulator.c @@ -797,7 +797,6 @@ static struct platform_driver max77620_r .probe = max77620_regulator_probe, .driver = { .name = "max77620-pmic", - .owner = THIS_MODULE, }, }; ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-03-29 7:33 ` kbuild test robot @ 2015-03-29 16:24 ` Mark Brown 2015-03-30 12:52 ` Thierry Reding 0 siblings, 1 reply; 40+ messages in thread From: Mark Brown @ 2015-03-29 16:24 UTC (permalink / raw) To: kbuild test robot Cc: Paul Walmsley, kbuild-all, Thierry Reding, Liam Girdwood, linux-kernel [-- Attachment #1: Type: text/plain, Size: 234 bytes --] On Sun, Mar 29, 2015 at 03:33:33PM +0800, kbuild test robot wrote: > drivers/regulator/max77620-regulator.c:800:3-8: No need to set .owner here. The core will do it. This doesn't apply against current code, this file is not present. [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 473 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-03-29 16:24 ` Mark Brown @ 2015-03-30 12:52 ` Thierry Reding 0 siblings, 0 replies; 40+ messages in thread From: Thierry Reding @ 2015-03-30 12:52 UTC (permalink / raw) To: Mark Brown Cc: kbuild test robot, Paul Walmsley, kbuild-all, Liam Girdwood, linux-kernel [-- Attachment #1: Type: text/plain, Size: 580 bytes --] On Sun, Mar 29, 2015 at 09:24:13AM -0700, Mark Brown wrote: > * PGP Signed by an unknown key > > On Sun, Mar 29, 2015 at 03:33:33PM +0800, kbuild test robot wrote: > > drivers/regulator/max77620-regulator.c:800:3-8: No need to set .owner here. The core will do it. > > This doesn't apply against current code, this file is not present. Hi Mark, Sorry about this. The tree that this was generated against is heavily work in progress and should certainly not have patches for mainline generated against it. I'm working with Fengguang to resolve this. Thierry [-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
[parent not found: <201503291545.My1tADgz%fengguang.wu@intel.com>]
* [PATCH] fix platform_no_drv_owner.cocci warnings [not found] <201503291545.My1tADgz%fengguang.wu@intel.com> @ 2015-03-29 7:42 ` kbuild test robot 2015-03-29 16:27 ` Mark Brown 0 siblings, 1 reply; 40+ messages in thread From: kbuild test robot @ 2015-03-29 7:42 UTC (permalink / raw) To: Alexandre Courbot Cc: kbuild-all, Thierry Reding, Liam Girdwood, Mark Brown, linux-kernel drivers/regulator/regulator-pwm.c:333:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- regulator-pwm.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/regulator/regulator-pwm.c +++ b/drivers/regulator/regulator-pwm.c @@ -330,7 +330,6 @@ MODULE_DEVICE_TABLE(of, pwm_regulator_of static struct platform_driver pwm_regulator_driver = { .driver = { .name = "regulator-pwm", - .owner = THIS_MODULE, .pm = &pwm_regulator_pm_ops, .of_match_table = pwm_regulator_of_match, }, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-03-29 7:42 ` kbuild test robot @ 2015-03-29 16:27 ` Mark Brown 0 siblings, 0 replies; 40+ messages in thread From: Mark Brown @ 2015-03-29 16:27 UTC (permalink / raw) To: kbuild test robot Cc: Alexandre Courbot, kbuild-all, Thierry Reding, Liam Girdwood, linux-kernel [-- Attachment #1: Type: text/plain, Size: 310 bytes --] On Sun, Mar 29, 2015 at 03:42:46PM +0800, kbuild test robot wrote: > drivers/regulator/regulator-pwm.c:333:3-8: No need to set .owner here. The core will do it. > > Remove .owner field if calls are used which set it automatically This doesn't apply against current code, the above file isn't present. [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 473 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
[parent not found: <201503291519.K9Z6Qptb%fengguang.wu@intel.com>]
* [PATCH] fix platform_no_drv_owner.cocci warnings [not found] <201503291519.K9Z6Qptb%fengguang.wu@intel.com> @ 2015-03-29 7:49 ` kbuild test robot 2015-03-30 13:05 ` Thierry Reding 0 siblings, 1 reply; 40+ messages in thread From: kbuild test robot @ 2015-03-29 7:49 UTC (permalink / raw) To: Alexandre Courbot Cc: kbuild-all, Thierry Reding, Linus Walleij, linux-gpio, linux-kernel drivers/pinctrl/pinctrl-max77620.c:472:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- pinctrl-max77620.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/pinctrl/pinctrl-max77620.c +++ b/drivers/pinctrl/pinctrl-max77620.c @@ -469,7 +469,6 @@ static int max77620_pinctrl_remove(struc static struct platform_driver max77620_pinctrl_driver = { .driver = { .name = "max77620-pinctrl", - .owner = THIS_MODULE, }, .probe = max77620_pinctrl_probe, .remove = max77620_pinctrl_remove, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-03-29 7:49 ` kbuild test robot @ 2015-03-30 13:05 ` Thierry Reding 0 siblings, 0 replies; 40+ messages in thread From: Thierry Reding @ 2015-03-30 13:05 UTC (permalink / raw) To: kbuild test robot Cc: Alexandre Courbot, kbuild-all, Linus Walleij, linux-gpio, linux-kernel [-- Attachment #1: Type: text/plain, Size: 1160 bytes --] On Sun, Mar 29, 2015 at 03:49:20PM +0800, kbuild test robot wrote: > drivers/pinctrl/pinctrl-max77620.c:472:3-8: No need to set .owner here. The core will do it. > > Remove .owner field if calls are used which set it automatically > > Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci > > CC: Alexandre Courbot <acourbot@nvidia.com> > Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> > --- > > pinctrl-max77620.c | 1 - > 1 file changed, 1 deletion(-) Hi Linus, please ignore this. It's from a staging tree and against a driver that doesn't exist upstream yet. I have for now removed these branches from my github tree completely until we can figure out a way to keep the 0- day builder from generating these patches. Thierry > --- a/drivers/pinctrl/pinctrl-max77620.c > +++ b/drivers/pinctrl/pinctrl-max77620.c > @@ -469,7 +469,6 @@ static int max77620_pinctrl_remove(struc > static struct platform_driver max77620_pinctrl_driver = { > .driver = { > .name = "max77620-pinctrl", > - .owner = THIS_MODULE, > }, > .probe = max77620_pinctrl_probe, > .remove = max77620_pinctrl_remove, [-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings @ 2015-03-30 13:05 ` Thierry Reding 0 siblings, 0 replies; 40+ messages in thread From: Thierry Reding @ 2015-03-30 13:05 UTC (permalink / raw) To: kbuild test robot Cc: Alexandre Courbot, kbuild-all, Linus Walleij, linux-gpio, linux-kernel [-- Attachment #1: Type: text/plain, Size: 1160 bytes --] On Sun, Mar 29, 2015 at 03:49:20PM +0800, kbuild test robot wrote: > drivers/pinctrl/pinctrl-max77620.c:472:3-8: No need to set .owner here. The core will do it. > > Remove .owner field if calls are used which set it automatically > > Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci > > CC: Alexandre Courbot <acourbot@nvidia.com> > Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> > --- > > pinctrl-max77620.c | 1 - > 1 file changed, 1 deletion(-) Hi Linus, please ignore this. It's from a staging tree and against a driver that doesn't exist upstream yet. I have for now removed these branches from my github tree completely until we can figure out a way to keep the 0- day builder from generating these patches. Thierry > --- a/drivers/pinctrl/pinctrl-max77620.c > +++ b/drivers/pinctrl/pinctrl-max77620.c > @@ -469,7 +469,6 @@ static int max77620_pinctrl_remove(struc > static struct platform_driver max77620_pinctrl_driver = { > .driver = { > .name = "max77620-pinctrl", > - .owner = THIS_MODULE, > }, > .probe = max77620_pinctrl_probe, > .remove = max77620_pinctrl_remove, [-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-03-30 13:05 ` Thierry Reding (?) @ 2015-04-07 13:09 ` Linus Walleij 2015-04-08 1:24 ` Fengguang Wu -1 siblings, 1 reply; 40+ messages in thread From: Linus Walleij @ 2015-04-07 13:09 UTC (permalink / raw) To: Thierry Reding Cc: kbuild test robot, Alexandre Courbot, kbuild-all, linux-gpio, linux-kernel On Mon, Mar 30, 2015 at 3:05 PM, Thierry Reding <treding@nvidia.com> wrote: > On Sun, Mar 29, 2015 at 03:49:20PM +0800, kbuild test robot wrote: >> drivers/pinctrl/pinctrl-max77620.c:472:3-8: No need to set .owner here. The core will do it. >> >> Remove .owner field if calls are used which set it automatically >> >> Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci >> >> CC: Alexandre Courbot <acourbot@nvidia.com> >> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> >> --- >> >> pinctrl-max77620.c | 1 - >> 1 file changed, 1 deletion(-) > > Hi Linus, > > please ignore this. It's from a staging tree and against a driver that > doesn't exist upstream yet. I have for now removed these branches from > my github tree completely until we can figure out a way to keep the 0- > day builder from generating these patches. Bah no big deal, I think I managed to fire off a similar thing on a PWM driver down your path :P Fengguang said he'd fixed it though IIRC. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2015-04-07 13:09 ` Linus Walleij @ 2015-04-08 1:24 ` Fengguang Wu 0 siblings, 0 replies; 40+ messages in thread From: Fengguang Wu @ 2015-04-08 1:24 UTC (permalink / raw) To: Linus Walleij Cc: Thierry Reding, Alexandre Courbot, kbuild-all, linux-gpio, linux-kernel On Tue, Apr 07, 2015 at 03:09:46PM +0200, Linus Walleij wrote: > On Mon, Mar 30, 2015 at 3:05 PM, Thierry Reding <treding@nvidia.com> wrote: > > On Sun, Mar 29, 2015 at 03:49:20PM +0800, kbuild test robot wrote: > >> drivers/pinctrl/pinctrl-max77620.c:472:3-8: No need to set .owner here. The core will do it. > >> > >> Remove .owner field if calls are used which set it automatically > >> > >> Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci > >> > >> CC: Alexandre Courbot <acourbot@nvidia.com> > >> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> > >> --- > >> > >> pinctrl-max77620.c | 1 - > >> 1 file changed, 1 deletion(-) > > > > Hi Linus, > > > > please ignore this. It's from a staging tree and against a driver that > > doesn't exist upstream yet. I have for now removed these branches from > > my github tree completely until we can figure out a way to keep the 0- > > day builder from generating these patches. > > Bah no big deal, I think I managed to fire off a similar thing on > a PWM driver down your path :P > > Fengguang said he'd fixed it though IIRC. Yes sorry for the noise! I've listed Thierry's tree as private report tree (the black list way). Perhaps would be better to make the logic white list based -- then it will be completely noise free. Thanks, Fengguang ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] add new platform driver for PCI RC 2015-11-13 12:06 ` [PATCHv3] " Joao Pinto @ 2015-11-13 11:36 kbuild test robot 2015-11-13 12:06 ` [PATCHv3] " Joao Pinto 0 siblings, 1 reply; 40+ messages in thread From: kbuild test robot @ 2015-11-13 11:36 UTC (permalink / raw) To: Joao Pinto; +Cc: kbuild-all, bhelgaas, CARLOS.PALMINHA, linux-pci, Joao Pinto [-- Attachment #1: Type: text/plain, Size: 13645 bytes --] Hi Joao, [auto build test ERROR on: v4.3-rc7] [cannot apply to: pci/next next-20151113] url: https://github.com/0day-ci/linux/commits/Joao-Pinto/add-new-platform-driver-for-PCI-RC/20151113-190212 config: i386-allmodconfig (attached as .config) reproduce: # save the attached .config to linux build tree make ARCH=i386 All errors (new ones prefixed by >>): drivers/pci/host/pcie-designware.c:76:52: warning: 'struct pci_sys_data' declared inside parameter list static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) ^ drivers/pci/host/pcie-designware.c:76:52: warning: its scope is only this definition or declaration, which is probably not what you want In file included from include/uapi/linux/stddef.h:1:0, from include/linux/stddef.h:4, from include/uapi/linux/posix_types.h:4, from include/uapi/linux/types.h:13, from include/linux/types.h:5, from include/linux/smp.h:10, from include/linux/irq.h:12, from drivers/pci/host/pcie-designware.c:14: drivers/pci/host/pcie-designware.c: In function 'sys_to_pcie': >> drivers/pci/host/pcie-designware.c:78:13: error: dereferencing pointer to incomplete type 'struct pci_sys_data' BUG_ON(!sys->private_data); ^ include/linux/compiler.h:166:42: note: in definition of macro 'unlikely' # define unlikely(x) __builtin_expect(!!(x), 0) ^ drivers/pci/host/pcie-designware.c:78:2: note: in expansion of macro 'BUG_ON' BUG_ON(!sys->private_data); ^ drivers/pci/host/pcie-designware.c: In function 'dw_pcie_host_init': drivers/pci/host/pcie-designware.c:530:2: error: invalid use of undefined type 'struct hw_pci' dw_pci.nr_controllers = 1; ^ drivers/pci/host/pcie-designware.c:531:2: error: invalid use of undefined type 'struct hw_pci' dw_pci.private_data = (void **)&pp; ^ drivers/pci/host/pcie-designware.c:533:2: error: implicit declaration of function 'pci_common_init_dev' [-Werror=implicit-function-declaration] pci_common_init_dev(pp->dev, &dw_pci); ^ drivers/pci/host/pcie-designware.c: At top level: drivers/pci/host/pcie-designware.c:682:41: warning: 'struct pci_sys_data' declared inside parameter list static int dw_pcie_setup(int nr, struct pci_sys_data *sys) ^ drivers/pci/host/pcie-designware.c: In function 'dw_pcie_setup': drivers/pci/host/pcie-designware.c:686:19: warning: passing argument 1 of 'sys_to_pcie' from incompatible pointer type [-Wincompatible-pointer-types] pp = sys_to_pcie(sys); ^ drivers/pci/host/pcie-designware.c:76:33: note: expected 'struct pci_sys_data *' but argument is of type 'struct pci_sys_data *' static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) ^ >> drivers/pci/host/pcie-designware.c:688:25: error: 'SZ_1M' undeclared (first use in this function) if (global_io_offset < SZ_1M && pp->io_size > 0) { ^ drivers/pci/host/pcie-designware.c:688:25: note: each undeclared identifier is reported only once for each function it appears in drivers/pci/host/pcie-designware.c:689:6: error: dereferencing pointer to incomplete type 'struct pci_sys_data' sys->io_offset = global_io_offset - pp->io_bus_addr; ^ drivers/pci/host/pcie-designware.c:690:3: error: implicit declaration of function 'pci_ioremap_io' [-Werror=implicit-function-declaration] pci_ioremap_io(global_io_offset, pp->io_base); ^ >> drivers/pci/host/pcie-designware.c:691:23: error: 'SZ_64K' undeclared (first use in this function) global_io_offset += SZ_64K; ^ drivers/pci/host/pcie-designware.c: At top level: drivers/pci/host/pcie-designware.c:703:56: warning: 'struct pci_sys_data' declared inside parameter list static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) ^ drivers/pci/host/pcie-designware.c: In function 'dw_pcie_scan_bus': drivers/pci/host/pcie-designware.c:706:37: warning: passing argument 1 of 'sys_to_pcie' from incompatible pointer type [-Wincompatible-pointer-types] struct pcie_port *pp = sys_to_pcie(sys); ^ drivers/pci/host/pcie-designware.c:76:33: note: expected 'struct pci_sys_data *' but argument is of type 'struct pci_sys_data *' static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) ^ drivers/pci/host/pcie-designware.c:708:23: error: dereferencing pointer to incomplete type 'struct pci_sys_data' pp->root_bus_nr = sys->busnr; ^ drivers/pci/host/pcie-designware.c: At top level: drivers/pci/host/pcie-designware.c:739:15: error: variable 'dw_pci' has initializer but incomplete type static struct hw_pci dw_pci = { ^ drivers/pci/host/pcie-designware.c:740:2: error: unknown field 'setup' specified in initializer .setup = dw_pcie_setup, ^ drivers/pci/host/pcie-designware.c:740:12: warning: excess elements in struct initializer .setup = dw_pcie_setup, ^ drivers/pci/host/pcie-designware.c:740:12: note: (near initialization for 'dw_pci') drivers/pci/host/pcie-designware.c:741:2: error: unknown field 'scan' specified in initializer .scan = dw_pcie_scan_bus, ^ drivers/pci/host/pcie-designware.c:741:11: warning: excess elements in struct initializer .scan = dw_pcie_scan_bus, ^ drivers/pci/host/pcie-designware.c:741:11: note: (near initialization for 'dw_pci') drivers/pci/host/pcie-designware.c:742:2: error: unknown field 'map_irq' specified in initializer .map_irq = dw_pcie_map_irq, ^ drivers/pci/host/pcie-designware.c:742:13: warning: excess elements in struct initializer .map_irq = dw_pcie_map_irq, ^ drivers/pci/host/pcie-designware.c:742:13: note: (near initialization for 'dw_pci') drivers/pci/host/pcie-designware.c: In function 'sys_to_pcie': drivers/pci/host/pcie-designware.c:81:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ cc1: some warnings being treated as errors -- drivers/pci/host/pcie-snpsdev.c: In function 'snpsdev_pcie_host_init': >> drivers/pci/host/pcie-snpsdev.c:139:2: error: implicit declaration of function 'dw_pcie_link_retrain' [-Werror=implicit-function-declaration] dw_pcie_link_retrain(pp); ^ cc1: some warnings being treated as errors coccinelle warnings: (new ones prefixed by >>) >> drivers/pci/host/pcie-designware.c:158:23-47: Move constant to right. -- >> drivers/pci/host/pcie-snpsdev.c:323:3-8: No need to set .owner here. The core will do it. Please review and possibly fold the followup patch. vim +/dw_pcie_link_retrain +139 drivers/pci/host/pcie-snpsdev.c 133 snpsdev_pcie_deassert_core_reset(pp); 134 135 /*We expect the PCIE Link to be up by this time*/ 136 dw_pcie_setup_rc(pp); 137 138 /*Start LTSSM here*/ > 139 dw_pcie_link_retrain(pp); 140 141 /* Check for Link up indication */ 142 while (!dw_pcie_link_up(pp)) { 143 usleep_range(1000,1100); 144 count++; 145 if (count == 20) { 146 dev_err(pp->dev, "phy link never came up\n"); 147 dev_dbg(pp->dev, 148 "PL_DEBUG0: 0x%08x, DEBUG_R1: 0x%08x\n", 149 readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), 150 readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); 151 break; 152 } 153 } 154 155 if (IS_ENABLED(CONFIG_PCI_MSI)) 156 dw_pcie_msi_init(pp); 157 158 return; 159 } 160 /** 161 * 162 * Let all outof band signalling be handled by cfg_phy_control[31:0] 163 * which is selected through optional config attribute PHY_CONTROL_REG 164 * 165 * Monitor cxpl_debug_info as required to take necessary action 166 * This is available in the register PCIE_PHY_DEBUG_R0 & PCIE_PHY_DEBUG_R1 167 * 168 */ 169 static int snpsdev_pcie_link_up(struct pcie_port *pp) 170 { 171 u32 status; 172 173 /* Bit number 36: reports LTSSM PHY Link UP; Available in bit 3 of 174 * PCIE_PHY_DEBUG_R1 */ 175 status = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << 4); 176 if(status != 0) 177 return 1; 178 179 /* TODO: Now Link is in L0; Initiate GEN2/GEN3 migration if RC Supports */ 180 return 0; 181 } 182 183 184 /** 185 * This is RC operation structure 186 * snpsdev_pcie_link_up: the function which initiates the phy link up procedure 187 * snpsdev_pcie_host_init: the function whihc does the host/RC Root port initialization 188 */ 189 static struct pcie_host_ops snpsdev_pcie_host_ops = { 190 .link_up = snpsdev_pcie_link_up, 191 .host_init = snpsdev_pcie_host_init, 192 }; 193 194 /** 195 * snpsdev_add_pcie_port 196 * This function 197 * a. installs the interrupt handler 198 * b. registers host operations int he pcie_port structure 199 */ 200 static int snpsdev_add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) 201 { 202 int ret; 203 204 pp->irq = platform_get_irq(pdev, 1); 205 206 if (pp->irq < 0) { 207 if (pp->irq != -EPROBE_DEFER) 208 dev_err(&pdev->dev, "cannot get irq\n"); 209 return pp->irq; 210 } 211 212 ret = devm_request_irq(&pdev->dev, pp->irq, snpsdev_pcie_irq_handler, 213 IRQF_SHARED, "snpsdev-pcie", pp); 214 215 if (ret) { 216 dev_err(&pdev->dev, "failed to request irq\n"); 217 return ret; 218 } 219 220 if (IS_ENABLED(CONFIG_PCI_MSI)) { 221 pp->msi_irq = platform_get_irq(pdev, 0); 222 223 if (pp->msi_irq < 0) { 224 if (pp->msi_irq != -EPROBE_DEFER) 225 dev_err(&pdev->dev, "cannot get msi irq\n"); 226 return pp->msi_irq; 227 } 228 229 ret = devm_request_irq(&pdev->dev, pp->msi_irq, 230 snpsdev_pcie_msi_irq_handler, 231 IRQF_SHARED, "snpsdev-pcie-msi", pp); 232 if (ret) { 233 dev_err(&pdev->dev, "failed to request msi irq\n"); 234 return ret; 235 } 236 } 237 238 pp->root_bus_nr = -1; 239 pp->ops = &snpsdev_pcie_host_ops; 240 241 /* Below function: 242 * Checks for range property from DT 243 * Gets the IO and MEMORY and CONFIG-Space ranges from DT 244 * Does IOREMAPS on the physical addresses 245 * Gets the num-lanes from DT 246 * Gets MSI capability from DT 247 * Calls the platform specific host initialization 248 * Program the correct class, BAR0, Link width, in Config space 249 * Then it calls pci common init routine 250 * Then it calls funtion to assign "unassigend reources" 251 */ 252 ret = dw_pcie_host_init(pp); 253 if (ret) { 254 dev_err(&pdev->dev, "failed to initialize host\n"); 255 return ret; 256 } 257 258 return 0; 259 } 260 261 /** 262 * snpsdev_pcie_rc_probe() 263 * This function gets called as part of pcie registration. if the id matches 264 * the platform driver framework will call this function. 265 * 266 * @pdev: Pointer to the platform_device structure 267 * 268 * Returns zero on success; Negative errorno on failure 269 */ 270 static int __init snpsdev_pcie_rc_probe(struct platform_device *pdev) 271 { 272 struct snpsdev_pcie *snpsdev_pcie; 273 struct pcie_port *pp; 274 struct resource *dwc_pcie_rc_res; /* Resource from DT */ 275 int ret; 276 277 snpsdev_pcie = devm_kzalloc(&pdev->dev, sizeof(*snpsdev_pcie), GFP_KERNEL); 278 if (!snpsdev_pcie) { 279 dev_err(&pdev->dev, "no memory for snpsdev pcie\n"); 280 return -ENOMEM; 281 } 282 283 pp = &snpsdev_pcie->pp; 284 pp->dev = &pdev->dev; 285 286 dwc_pcie_rc_res= platform_get_resource(pdev, IORESOURCE_MEM, 0); 287 if (!dwc_pcie_rc_res) { 288 dev_err(&pdev->dev, "dwc_pcie_rc_res resource not found\n"); 289 return -ENODEV; 290 } 291 292 snpsdev_pcie->mem_base = devm_ioremap_resource(&pdev->dev, dwc_pcie_rc_res); 293 if (IS_ERR(snpsdev_pcie->mem_base)) { 294 ret = PTR_ERR(snpsdev_pcie->mem_base); 295 return ret; 296 } 297 pp->dbi_base = snpsdev_pcie->mem_base; 298 299 ret = snpsdev_add_pcie_port(pp, pdev); 300 if (ret < 0) 301 return ret; 302 303 platform_set_drvdata(pdev, snpsdev_pcie); 304 305 return 0; 306 } 307 308 static int __exit snpsdev_pcie_rc_remove(struct platform_device *pdev) 309 { 310 return 0; 311 } 312 313 static const struct of_device_id snpsdev_pcie_rc_of_match[] = { 314 { .compatible = "snps,pcie-snpsdev", }, 315 {}, 316 }; 317 MODULE_DEVICE_TABLE(of, snpsdev_pcie_rc_of_match); 318 319 static struct platform_driver snpsdev_pcie_rc_driver = { 320 .remove = __exit_p(snpsdev_pcie_rc_remove), 321 .driver = { 322 .name = "pcie-snpsdev", > 323 .owner = THIS_MODULE, 324 .of_match_table = snpsdev_pcie_rc_of_match, 325 }, 326 }; --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/octet-stream, Size: 51602 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCHv3] add new platform driver for PCI RC @ 2015-11-13 12:06 ` Joao Pinto 2015-11-13 11:36 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: Joao Pinto @ 2015-11-13 12:06 UTC (permalink / raw) To: bhelgaas; +Cc: CARLOS.PALMINHA, linux-pci, Joao Pinto This patch goal is to add a new platform driver called pcie-snpsdev for PCI RC using Synopsys' PCI RC DesignWare Core. This driver will be used extensively because it is going to be the official platform driver of the DesignWare PCI RC IP Prototyping Kit which provides a FPGA containing a DW PCI RC controller and a software development environment. Signed-off-by: Joao Pinto <jpinto@synopsys.com> --- Changes v1->v2 (kbuild test robot <lkp@intel.com>): - owner from the driver structure Changes v2->v3 (kbuild test robot <lkp@intel.com>): - removed dw_pcie_link_retrain() that does not exist in pcie-designware drivers/pci/host/Kconfig | 5 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pcie-snpsdev.c | 337 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+) create mode 100644 drivers/pci/host/pcie-snpsdev.c diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index d5e58ba..b9d59f6 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -145,4 +145,9 @@ config PCIE_IPROC_BCMA Say Y here if you want to use the Broadcom iProc PCIe controller through the BCMA bus interface +config PCIE_SNPSDEV + bool "Platform Driver for Synopsys Device" + select PCIEPORTBUS + select PCIE_DW + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 140d66f..c35c42a 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o +obj-$(CONFIG_PCIE_SNPSDEV) += pcie-snpsdev.o diff --git a/drivers/pci/host/pcie-snpsdev.c b/drivers/pci/host/pcie-snpsdev.c new file mode 100644 index 0000000..7389097 --- /dev/null +++ b/drivers/pci/host/pcie-snpsdev.c @@ -0,0 +1,333 @@ +/* + * PCIe RC driver for Synopsys Designware Core + * + * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com) + * + * Authors: Manjunath Bettegowda <manjumb@synopsys.com>, + * Jie Deng <jiedeng@synopsys.com> + * Joao Pinto <jpinto@synopsys.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/resource.h> +#include <linux/signal.h> +#include <linux/types.h> + +#include "pcie-designware.h" + +#define to_snpsdev_pcie(x) container_of(x, struct snpsdev_pcie, pp) + +struct snpsdev_pcie { + void __iomem *mem_base; /* Memory Base to access Core's [RC] Config Space Layout */ + struct pcie_port pp; /* RC Root Port specific structrue - DWC_PCIE_RC stuff */ +}; + +#define SIZE_1GB 0x40000000 +#define PCI_EQUAL_CONTROL_PHY 0x00000707 + +/* PCIe Port Logic registers (memory-mapped) */ +#define PLR_OFFSET 0x700 +#define PCIE_PHY_DEBUG_R0 (PLR_OFFSET + 0x28) /* 0x728 */ +#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c) /* 0x72c */ + +/* PCIE PHY CONTROL REGISTER: Useful for cfg_phy_control GPIO outputs */ +#define PCIE_PHY_CTRL (PLR_OFFSET + 0x114) /* 0x814 */ +/* PCIE PHY STATUS REGISTER: Useful for phy_cfg_status GPIO inputs */ +#define PCIE_PHY_STAT (PLR_OFFSET + 0x110) /* 0x810 */ + +static void snpsdev_pcie_fixup_bridge(struct pci_dev *dev) +{ + u32 slot_cap; + u16 caps_reg = pcie_caps_reg(dev) | PCI_EXP_FLAGS_SLOT; + pcie_capability_write_word(dev, PCI_EXP_FLAGS, caps_reg); + dev->pcie_flags_reg = caps_reg; + + pcie_capability_read_dword(dev, PCI_EXP_SLTCAP, &slot_cap); + slot_cap = slot_cap & (~PCI_EXP_SLTCAP_SPLV); + slot_cap = slot_cap | (0x30 << 7); + pcie_capability_write_dword(dev, PCI_EXP_SLTCAP, slot_cap); + + if (pcibios_enable_device(dev, ~0) < 0) { + pr_err("PCI: synopsys device enable failed\n"); + return; + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, snpsdev_pcie_fixup_bridge); + +static void snpsdev_pcie_fixup_res(struct pci_dev *dev) +{ + struct resource *res; + resource_size_t size; + int bar; + + for (bar = 0; bar < 6; bar++) { + res = dev->resource + bar; + size = resource_size(res); + + if (size == SIZE_1GB) + { + res->start = 0; + res->end = 0; + res->flags = 0; + } + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, snpsdev_pcie_fixup_res); + +/* This handler was created for future work */ +static irqreturn_t snpsdev_pcie_irq_handler(int irq, void *arg) +{ + return IRQ_NONE; +} + +static irqreturn_t snpsdev_pcie_msi_irq_handler(int irq, void *arg) +{ + struct pcie_port *pp = arg; + + dw_handle_msi_irq(pp); + + return IRQ_HANDLED; +} + +static void snpsdev_pcie_init_phy(struct pcie_port *pp) +{ + /* write Lane 0 Equalization Control fields register */ + writel(PCI_EQUAL_CONTROL_PHY,pp->dbi_base + 0x154); +} + +static int snpsdev_pcie_deassert_core_reset(struct pcie_port *pp) +{ + return 0; +} + +/* + * snpsdev_pcie_host_init() + * Platform specific host/RC initialization + * a. Assert the core reset + * b. Assert and deassert phy reset and initialize the phy + * c. De-Assert the core reset + * d. Initializet the Root Port (BARs/Memory Or IO/ Interrupt/ Commnad Reg) + * e. Initiate Link startup procedure + * + */ +static void snpsdev_pcie_host_init(struct pcie_port *pp) +{ + int count = 0; + + /* Initialize Phy (Reset/poweron/control-inputs ) */ + snpsdev_pcie_init_phy(pp); + + /* de-assert core reset */ + snpsdev_pcie_deassert_core_reset(pp); + + /*We expect the PCIE Link to be up by this time*/ + dw_pcie_setup_rc(pp); + + /* Check for Link up indication */ + while (!dw_pcie_link_up(pp)) { + usleep_range(1000,1100); + count++; + if (count == 20) { + dev_err(pp->dev, "phy link never came up\n"); + dev_dbg(pp->dev, + "PL_DEBUG0: 0x%08x, DEBUG_R1: 0x%08x\n", + readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), + readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); + break; + } + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + + return; +} +/** + * + * Let all outof band signalling be handled by cfg_phy_control[31:0] + * which is selected through optional config attribute PHY_CONTROL_REG + * + * Monitor cxpl_debug_info as required to take necessary action + * This is available in the register PCIE_PHY_DEBUG_R0 & PCIE_PHY_DEBUG_R1 + * + */ +static int snpsdev_pcie_link_up(struct pcie_port *pp) +{ + u32 status; + + /* Bit number 36: reports LTSSM PHY Link UP; Available in bit 3 of + * PCIE_PHY_DEBUG_R1 */ + status = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << 4); + if(status != 0) + return 1; + + /* TODO: Now Link is in L0; Initiate GEN2/GEN3 migration if RC Supports */ + return 0; +} + + +/** + * This is RC operation structure + * snpsdev_pcie_link_up: the function which initiates the phy link up procedure + * snpsdev_pcie_host_init: the function whihc does the host/RC Root port initialization + */ +static struct pcie_host_ops snpsdev_pcie_host_ops = { + .link_up = snpsdev_pcie_link_up, + .host_init = snpsdev_pcie_host_init, +}; + +/** + * snpsdev_add_pcie_port + * This function + * a. installs the interrupt handler + * b. registers host operations int he pcie_port structure + */ +static int snpsdev_add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) +{ + int ret; + + pp->irq = platform_get_irq(pdev, 1); + + if (pp->irq < 0) { + if (pp->irq != -EPROBE_DEFER) + dev_err(&pdev->dev, "cannot get irq\n"); + return pp->irq; + } + + ret = devm_request_irq(&pdev->dev, pp->irq, snpsdev_pcie_irq_handler, + IRQF_SHARED, "snpsdev-pcie", pp); + + if (ret) { + dev_err(&pdev->dev, "failed to request irq\n"); + return ret; + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + + if (pp->msi_irq < 0) { + if (pp->msi_irq != -EPROBE_DEFER) + dev_err(&pdev->dev, "cannot get msi irq\n"); + return pp->msi_irq; + } + + ret = devm_request_irq(&pdev->dev, pp->msi_irq, + snpsdev_pcie_msi_irq_handler, + IRQF_SHARED, "snpsdev-pcie-msi", pp); + if (ret) { + dev_err(&pdev->dev, "failed to request msi irq\n"); + return ret; + } + } + + pp->root_bus_nr = -1; + pp->ops = &snpsdev_pcie_host_ops; + + /* Below function: + * Checks for range property from DT + * Gets the IO and MEMORY and CONFIG-Space ranges from DT + * Does IOREMAPS on the physical addresses + * Gets the num-lanes from DT + * Gets MSI capability from DT + * Calls the platform specific host initialization + * Program the correct class, BAR0, Link width, in Config space + * Then it calls pci common init routine + * Then it calls funtion to assign "unassigend reources" + */ + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(&pdev->dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +/** + * snpsdev_pcie_rc_probe() + * This function gets called as part of pcie registration. if the id matches + * the platform driver framework will call this function. + * + * @pdev: Pointer to the platform_device structure + * + * Returns zero on success; Negative errorno on failure + */ +static int __init snpsdev_pcie_rc_probe(struct platform_device *pdev) +{ + struct snpsdev_pcie *snpsdev_pcie; + struct pcie_port *pp; + struct resource *dwc_pcie_rc_res; /* Resource from DT */ + int ret; + + snpsdev_pcie = devm_kzalloc(&pdev->dev, sizeof(*snpsdev_pcie), GFP_KERNEL); + if (!snpsdev_pcie) { + dev_err(&pdev->dev, "no memory for snpsdev pcie\n"); + return -ENOMEM; + } + + pp = &snpsdev_pcie->pp; + pp->dev = &pdev->dev; + + dwc_pcie_rc_res= platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!dwc_pcie_rc_res) { + dev_err(&pdev->dev, "dwc_pcie_rc_res resource not found\n"); + return -ENODEV; + } + + snpsdev_pcie->mem_base = devm_ioremap_resource(&pdev->dev, dwc_pcie_rc_res); + if (IS_ERR(snpsdev_pcie->mem_base)) { + ret = PTR_ERR(snpsdev_pcie->mem_base); + return ret; + } + pp->dbi_base = snpsdev_pcie->mem_base; + + ret = snpsdev_add_pcie_port(pp, pdev); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, snpsdev_pcie); + + return 0; +} + +static int __exit snpsdev_pcie_rc_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id snpsdev_pcie_rc_of_match[] = { + { .compatible = "snps,pcie-snpsdev", }, + {}, +}; +MODULE_DEVICE_TABLE(of, snpsdev_pcie_rc_of_match); + +static struct platform_driver snpsdev_pcie_rc_driver = { + .remove = __exit_p(snpsdev_pcie_rc_remove), + .driver = { + .name = "pcie-snpsdev", + .of_match_table = snpsdev_pcie_rc_of_match, + }, +}; + +static int __init snpsdev_pcie_init(void) +{ + return platform_driver_probe(&snpsdev_pcie_rc_driver, snpsdev_pcie_rc_probe); +} +subsys_initcall(snpsdev_pcie_init); + +MODULE_AUTHOR("Manjunath Bettegowda <manjumb@synopsys.com>"); +MODULE_DESCRIPTION("Platform Driver for Synopsys Device"); +MODULE_LICENSE("GPL v2"); + -- 1.8.1.5 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2015-11-13 12:06 ` [PATCHv3] " Joao Pinto @ 2015-11-13 11:36 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2015-11-13 11:36 UTC (permalink / raw) To: Joao Pinto; +Cc: kbuild-all, bhelgaas, CARLOS.PALMINHA, linux-pci, Joao Pinto drivers/pci/host/pcie-snpsdev.c:323:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Joao Pinto <Joao.Pinto@synopsys.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- pcie-snpsdev.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/pci/host/pcie-snpsdev.c +++ b/drivers/pci/host/pcie-snpsdev.c @@ -320,7 +320,6 @@ static struct platform_driver snpsdev_pc .remove = __exit_p(snpsdev_pcie_rc_remove), .driver = { .name = "pcie-snpsdev", - .owner = THIS_MODULE, .of_match_table = snpsdev_pcie_rc_of_match, }, }; ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 2/6] Add Advantech iManager GPIO driver
2016-01-08 22:29 ` richard.dorsch
@ 2016-01-09 0:50 kbuild test robot
2016-01-08 22:29 ` richard.dorsch
0 siblings, 1 reply; 40+ messages in thread
From: kbuild test robot @ 2016-01-09 0:50 UTC (permalink / raw)
Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog,
linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga,
Richard Vidal-Dorsch
Hi Richard,
[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.4-rc8 next-20160108]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/richard-dorsch-gmail-com/Add-Advantech-iManager-EC-driver-set/20160109-063329
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
coccinelle warnings: (new ones prefixed by >>)
>> drivers/gpio/imanager-gpio.c:170:3-8: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/6] Add Advantech iManager GPIO driver @ 2016-01-08 22:29 ` richard.dorsch 2016-01-09 0:50 ` kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: richard.dorsch @ 2016-01-08 22:29 UTC (permalink / raw) To: linux-kernel Cc: lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch From: Richard Vidal-Dorsch <richard.dorsch@gmail.com> --- drivers/gpio/Kconfig | 8 ++ drivers/gpio/Makefile | 2 + drivers/gpio/imanager-ec-gpio.c | 98 ++++++++++++++++++++ drivers/gpio/imanager-gpio.c | 182 ++++++++++++++++++++++++++++++++++++++ include/linux/mfd/imanager/gpio.h | 27 ++++++ 5 files changed, 317 insertions(+) create mode 100644 drivers/gpio/imanager-ec-gpio.c create mode 100644 drivers/gpio/imanager-gpio.c create mode 100644 include/linux/mfd/imanager/gpio.h diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b18bea0..0f80947 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -765,6 +765,14 @@ config GPIO_DLN2 This driver can also be built as a module. If so, the module will be called gpio-dln2. +config GPIO_IMANAGER + tristate "Advantech iManager GPIO support" + depends on MFD_IMANAGER + help + Say yes here to support Advantech iManager GPIO functionality + of some Advantech SOM, MIO, AIMB, and PCM modules/boards. + Requires mfd-core and imanager-core to function properly. + config GPIO_JANZ_TTL tristate "Janz VMOD-TTL Digital IO Module" depends on MFD_JANZ_CMODIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 986dbd8..0df55e4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -41,6 +41,8 @@ obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o +gpio-imanager-objs := imanager-gpio.o imanager-ec-gpio.o +obj-$(CONFIG_GPIO_IMANAGER) += gpio-imanager.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o obj-$(CONFIG_GPIO_IT87) += gpio-it87.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o diff --git a/drivers/gpio/imanager-ec-gpio.c b/drivers/gpio/imanager-ec-gpio.c new file mode 100644 index 0000000..cefc44d --- /dev/null +++ b/drivers/gpio/imanager-ec-gpio.c @@ -0,0 +1,98 @@ +/* + * Advantech iManager GPIO core + * + * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/bug.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/byteorder/generic.h> +#include <linux/mfd/imanager/ec.h> +#include <linux/mfd/imanager/gpio.h> + +#define EC_GPIOF_DIR_OUT (1 << 6) +#define EC_GPIOF_DIR_IN (1 << 7) +#define EC_GPIOF_LOW (0 << 0) +#define EC_GPIOF_HIGH (1 << 0) + +/* + * Power-on default: + * GPIO[7..4] := Input + * GPIO[3..0] := Output + */ + +static const struct imanager_gpio_device *gpio; + +int gpio_core_get_state(u32 num) +{ + int ret; + + if (WARN_ON(num >= gpio->num)) + return -EINVAL; + + ret = imanager_read_byte(EC_CMD_HWP_RD, gpio->attr[num].did); + if (ret < 0) + pr_err("Failed to get GPIO pin state (%x)\n", num); + + return ret; +} + +int gpio_core_set_state(u32 num, bool state) +{ + int ret; + + if (WARN_ON(num >= gpio->num)) + return -EINVAL; + + ret = imanager_write_byte(EC_CMD_HWP_WR, gpio->attr[num].did, + state ? EC_GPIOF_HIGH : EC_GPIOF_LOW); + if (ret) { + pr_err("Failed to set GPIO pin state (%x)\n", num); + return ret; + } + + return 0; +} + +int gpio_core_set_direction(u32 num, int dir) +{ + int ret; + + if (WARN_ON(num >= gpio->num)) + return -EINVAL; + + ret = imanager_write_byte(EC_CMD_GPIO_DIR_WR, gpio->attr[num].did, + dir ? EC_GPIOF_DIR_IN : EC_GPIOF_DIR_OUT); + if (ret) { + pr_err("Failed to set GPIO direction (%x, '%s')\n", num, + dir == GPIOF_DIR_OUT ? "OUT" : "IN"); + return ret; + } + + return 0; +} + +int gpio_core_get_max_count(void) +{ + return gpio->num; +} + +int gpio_core_init(void) +{ + gpio = imanager_get_gpio_device(); + if (!gpio) + return -ENODEV; + + return 0; +} + diff --git a/drivers/gpio/imanager-gpio.c b/drivers/gpio/imanager-gpio.c new file mode 100644 index 0000000..3939c0f --- /dev/null +++ b/drivers/gpio/imanager-gpio.c @@ -0,0 +1,182 @@ +/* + * Advantech iManager GPIO driver + * + * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/mfd/imanager/core.h> +#include <linux/mfd/imanager/gpio.h> + +struct imanager_gpio_data { + struct imanager_device_data *idev; + struct gpio_chip chip; +}; + +static inline struct imanager_gpio_data * +to_imanager_gpio_data(struct gpio_chip *chip) +{ + return container_of(chip, struct imanager_gpio_data, chip); +} + +static int imanager_direction_in(struct gpio_chip *chip, u32 gpio_num) +{ + struct imanager_gpio_data *data = to_imanager_gpio_data(chip); + int ret; + + mutex_lock(&data->idev->lock); + + ret = gpio_core_set_direction(gpio_num, GPIOF_DIR_IN); + if (ret) { + dev_err(chip->dev, "Failed to set direction to 'in' (%d)\n", + gpio_num); + ret = -EIO; + } + + mutex_unlock(&data->idev->lock); + + return ret; +} + +static int +imanager_direction_out(struct gpio_chip *chip, u32 gpio_num, int val) +{ + struct imanager_gpio_data *data = to_imanager_gpio_data(chip); + int ret; + + mutex_lock(&data->idev->lock); + + ret = gpio_core_set_direction(gpio_num, GPIOF_DIR_OUT); + if (ret) { + dev_err(chip->dev, "Failed to set direction to 'out' (%d)\n", + gpio_num); + ret = -EIO; + } + + mutex_unlock(&data->idev->lock); + + return ret; +} + +static int imanager_get(struct gpio_chip *chip, u32 gpio_num) +{ + struct imanager_gpio_data *data = to_imanager_gpio_data(chip); + int ret; + + mutex_lock(&data->idev->lock); + + ret = gpio_core_get_state(gpio_num); + if (ret < 0) { + dev_err(chip->dev, "Failed to get status (%d)\n", gpio_num); + ret = -EIO; + } + + mutex_unlock(&data->idev->lock); + + return ret; +} + +static void imanager_set(struct gpio_chip *chip, u32 gpio_num, + int val) +{ + struct imanager_gpio_data *data = to_imanager_gpio_data(chip); + int ret; + + mutex_lock(&data->idev->lock); + + ret = gpio_core_set_state(gpio_num, val); + if (ret < 0) + dev_err(chip->dev, "Failed to set status (%d)\n", gpio_num); + + mutex_unlock(&data->idev->lock); +} + +static int imanager_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imanager_device_data *idev = dev_get_drvdata(dev->parent); + struct imanager_gpio_data *data; + struct gpio_chip *chip; + int ret; + + if (!idev) { + dev_err(dev, "Invalid platform data\n"); + return -EINVAL; + } + + ret = gpio_core_init(); + if (ret) { + dev_err(dev, "Failed initializing GPIO core\n"); + return ret; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->idev = idev; + + platform_set_drvdata(pdev, data); + + chip = &data->chip; + + chip->owner = THIS_MODULE; + chip->dev = dev; + chip->label = "imanager_gpio"; + + chip->base = -1; + chip->ngpio = gpio_core_get_max_count(); + + chip->get = imanager_get; + chip->set = imanager_set; + + chip->can_sleep = 1; + + chip->direction_input = imanager_direction_in; + chip->direction_output = imanager_direction_out; + + ret = gpiochip_add(chip); + if (ret < 0) { + dev_err(dev, "Failed to register driver\n"); + return ret; + } + + return 0; +} + +static int imanager_remove(struct platform_device *pdev) +{ + struct imanager_gpio_data *data = platform_get_drvdata(pdev); + + gpiochip_remove(&data->chip); + + return 0; +} + +static struct platform_driver imanager_gpio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "imanager_gpio", + }, + .probe = imanager_gpio_probe, + .remove = imanager_remove, +}; + +module_platform_driver(imanager_gpio_driver); + +MODULE_DESCRIPTION("Advantech iManager GPIO Driver"); +MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imanager_gpio"); diff --git a/include/linux/mfd/imanager/gpio.h b/include/linux/mfd/imanager/gpio.h new file mode 100644 index 0000000..7b76727 --- /dev/null +++ b/include/linux/mfd/imanager/gpio.h @@ -0,0 +1,27 @@ +/* + * Advantech iManager GPIO core + * + * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __GPIO_H__ +#define __GPIO_H__ + +#include <linux/gpio.h> +#include <linux/types.h> + +int gpio_core_init(void); + +int gpio_core_get_max_count(void); + +int gpio_core_get_state(u32 num); +int gpio_core_set_state(u32 num, bool state); +int gpio_core_set_direction(u32 num, int dir); + +#endif -- 2.6.4 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2016-01-08 22:29 ` richard.dorsch @ 2016-01-09 0:50 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-09 0:50 UTC (permalink / raw) Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/gpio/imanager-gpio.c:170:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-gpio.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/gpio/imanager-gpio.c +++ b/drivers/gpio/imanager-gpio.c @@ -167,7 +167,6 @@ static int imanager_remove(struct platfo static struct platform_driver imanager_gpio_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_gpio", }, .probe = imanager_gpio_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-01-09 0:50 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-09 0:50 UTC (permalink / raw) To: richard.dorsch Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/gpio/imanager-gpio.c:170:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-gpio.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/gpio/imanager-gpio.c +++ b/drivers/gpio/imanager-gpio.c @@ -167,7 +167,6 @@ static int imanager_remove(struct platfo static struct platform_driver imanager_gpio_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_gpio", }, .probe = imanager_gpio_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 6/6] Add Advantech iManager Watchdog driver
@ 2016-01-09 2:02 kbuild test robot
[not found] ` <1452292166-20118-7-git-send-email-richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
0 siblings, 1 reply; 40+ messages in thread
From: kbuild test robot @ 2016-01-09 2:02 UTC (permalink / raw)
Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog,
linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga,
Richard Vidal-Dorsch
Hi Richard,
[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.4-rc8 next-20160108]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/richard-dorsch-gmail-com/Add-Advantech-iManager-EC-driver-set/20160109-063329
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
coccinelle warnings: (new ones prefixed by >>)
>> drivers/watchdog/imanager-wdt.c:322:3-8: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 40+ messages in thread
[parent not found: <1452292166-20118-7-git-send-email-richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-01-09 2:02 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-09 2:02 UTC (permalink / raw) Cc: kbuild-all-JC7UmRfGjtg, linux-kernel-u79uwXL29TY76Z2rM5mHXA, lm-sensors-GZX6beZjE8VD60Wz+7aTrA, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-watchdog-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, lee.jones-QSEj5FYQhm4dnm+yROfE0A, jdelvare-IBi9RG/b67k, linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ, jo.sunga-ELdSlb/RfAS1Z/+hSey0Gg, Richard Vidal-Dorsch drivers/watchdog/imanager-wdt.c:322:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> Signed-off-by: Fengguang Wu <fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> --- imanager-wdt.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/watchdog/imanager-wdt.c +++ b/drivers/watchdog/imanager-wdt.c @@ -319,7 +319,6 @@ static int imanager_wdt_remove(struct pl static struct platform_driver imanager_wdt_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_wdt", }, .probe = imanager_wdt_probe, -- To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-01-09 2:02 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-09 2:02 UTC (permalink / raw) To: richard.dorsch Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/watchdog/imanager-wdt.c:322:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-wdt.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/watchdog/imanager-wdt.c +++ b/drivers/watchdog/imanager-wdt.c @@ -319,7 +319,6 @@ static int imanager_wdt_remove(struct pl static struct platform_driver imanager_wdt_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_wdt", }, .probe = imanager_wdt_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v2 1/6] Add Advantech iManager MFD core driver
2016-01-10 9:10 ` richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w
@ 2016-01-10 10:11 kbuild test robot
2016-01-10 9:10 ` richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w
0 siblings, 1 reply; 40+ messages in thread
From: kbuild test robot @ 2016-01-10 10:11 UTC (permalink / raw)
Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog,
linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga,
Richard Vidal-Dorsch
Hi Richard,
[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.4-rc8 next-20160108]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/richard-dorsch-gmail-com/Add-Advantech-iManager-EC-driver-set/20160110-171635
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
coccinelle warnings: (new ones prefixed by >>)
>> drivers/mfd/imanager-core.c:248:3-8: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v2 1/6] Add Advantech iManager MFD core driver @ 2016-01-10 9:10 ` richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w 2016-01-10 10:11 ` kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w @ 2016-01-10 9:10 UTC (permalink / raw) To: linux-kernel-u79uwXL29TY76Z2rM5mHXA Cc: lm-sensors-GZX6beZjE8VD60Wz+7aTrA, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-watchdog-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, lee.jones-QSEj5FYQhm4dnm+yROfE0A, jdelvare-IBi9RG/b67k, linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ, jo.sunga-ELdSlb/RfAS1Z/+hSey0Gg, Richard Vidal-Dorsch From: Richard Vidal-Dorsch <richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> This patch adds Advantech iManager Embedded Controller MFD core driver. This mfd core dirver provides an interface to GPIO, I2C, HWmon, Watchdog, and Backlight/Brightness control. Signed-off-by: Richard Vidal-Dorsch <richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> --- Documentation/devicetree/bindings/mfd/imanager.txt | 33 + MAINTAINERS | 13 + drivers/mfd/Kconfig | 20 + drivers/mfd/Makefile | 2 + drivers/mfd/imanager-core.c | 288 +++++ drivers/mfd/imanager-ec.c | 1345 ++++++++++++++++++++ include/linux/mfd/imanager/core.h | 31 + include/linux/mfd/imanager/ec.h | 210 +++ 8 files changed, 1942 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/imanager.txt create mode 100644 drivers/mfd/imanager-core.c create mode 100644 drivers/mfd/imanager-ec.c create mode 100644 include/linux/mfd/imanager/core.h create mode 100644 include/linux/mfd/imanager/ec.h diff --git a/Documentation/devicetree/bindings/mfd/imanager.txt b/Documentation/devicetree/bindings/mfd/imanager.txt new file mode 100644 index 0000000..bf58a96 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/imanager.txt @@ -0,0 +1,33 @@ +Note +==== + +This is a set of platform drivers which provide support for multiple +embedded features such as GPIO, I2C/SMBus, Hardware Monitoring, Watchdog, +and Backlight/Brightness control. Those features are available on Advantech +Embedded boards such as SOM, MIO, AIMB, and PCM. +Datasheets of each product line can be downloaded from www.advantech.com + +Author: + Richard Vidal-Dorsch <richard.dorsch-ELdSlb/RfAS1Z/+hSey0Gg@public.gmane.org> + + +Kernel driver imanager +====================== + +This driver provides a communication layer to the Advantech iManager EC +firmware which is factory loaded onto ITE IT8518/28 chips. The type of +communication is message based. Clients (gpio, i2c, hwmon etc. drivers) +request data from Advantech iManager (polling, not interrupt driven). If +a response is been received within a time frame, the data is been extracted +from the message and then passed to the caller (clients). + + Supported chips: + * Advantech EC based on ITE IT8518 + Prefix: imanager + Addresses: 0x029e/0x029f + Datasheet: Available from ITE upon request + * Advantech EC based on ITE IT8528 + Prefix: imanager + Addresses: 0x0299/0x29a + Datasheet: Available from ITE upon request + diff --git a/MAINTAINERS b/MAINTAINERS index 233f834..8d25fdc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5391,6 +5391,19 @@ M: Stanislaw Gruszka <stf_xl-5tc4TXWwyLM@public.gmane.org> S: Maintained F: drivers/usb/atm/ueagle-atm.c +IMANAGER ADVANTECH EC DRIVER +M: Richard Vidal-Dorsch <richard.dorsch-ELdSlb/RfAS1Z/+hSey0Gg@public.gmane.org> +S: Maintained +F: Documentation/devicetree/bindings/mfd/imanager.txt +F: Documentation/hwmon/imanager +F: Documentation/i2c/busses/i2c-imanager +F: drivers/mfd/imanager-core.c +F: drivers/gpio/imanager-gpio.c +F: drivers/hwmon/imanager-hwmon.c +F: drivers/i2c/busses/imanager-i2c.c +F: drivers/video/backlight/imanager-bl.c +F: drivers/watchdog/imanager-wdt.c + INA209 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org> L: lm-sensors-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 4d92df6..4dc7c13 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -322,6 +322,26 @@ config MFD_INTEL_QUARK_I2C_GPIO their respective IO driver. The GPIO exports a total amount of 8 interrupt-capable GPIOs. +config MFD_IMANAGER + tristate "Advantech iManager Embedded Controller" + depends on PCI + depends on X86 + select MFD_CORE + help + This is the core driver for Advantech iManager EC as found on some + Advantech SOM, MIO, AIMB, and PCM modules/boards. The EC may provide + functions like GPIO, I2C interface, HW monitoring, Watchdog, and + backlight/brightness control. + + The following Advantech boards are supported: + * All SOM modules newer than SOM-5788 + * MIO-5250/5251/5270/5271 and newer + * PCM-9388 + * AIMB-274/231 + + This driver can also be built as a module. If so, the module + will be called imanager. + config LPC_ICH tristate "Intel ICH LPC" depends on PCI diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index a8b76b8..c9c63d9 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -142,6 +142,8 @@ obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o +imanager-objs := imanager-core.o imanager-ec.o +obj-$(CONFIG_MFD_IMANAGER) += imanager.o obj-$(CONFIG_MFD_KEMPLD) += kempld-core.o obj-$(CONFIG_MFD_INTEL_QUARK_I2C_GPIO) += intel_quark_i2c_gpio.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o diff --git a/drivers/mfd/imanager-core.c b/drivers/mfd/imanager-core.c new file mode 100644 index 0000000..9697c144 --- /dev/null +++ b/drivers/mfd/imanager-core.c @@ -0,0 +1,288 @@ +/* + * Advantech iManager MFD core driver + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch-ELdSlb/RfAS1Z/+hSey0Gg@public.gmane.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/mfd/core.h> +#include <linux/mfd/imanager/core.h> +#include <linux/mfd/imanager/ec.h> + +static struct platform_device *pdev; + +enum imanager_cells { + IMANAGER_BL, + IMANAGER_GPIO, + IMANAGER_HWMON, + IMANAGER_I2C, + IMANAGER_WDT, +}; + +static const char * const chip_names[] = { + "it8516", + "it8518", + "it8528", + NULL +}; + +static struct resource imanager_ioresource = { + .start = IT8516_DAT_PORT, + .end = IT8518_DAT_PORT, + .flags = IORESOURCE_IO, +}; + +/* + * Devices which are part of the iManager and are available via firmware. + */ +static struct mfd_cell imanager_devs[] = { + [IMANAGER_BL] = { + .name = "imanager_backlight", + }, + [IMANAGER_GPIO] = { + .name = "imanager_gpio", + }, + [IMANAGER_HWMON] = { + .name = "imanager_hwmon", + }, + [IMANAGER_I2C] = { + .name = "imanager_i2c", + }, + [IMANAGER_WDT] = { + .name = "imanager_wdt", + }, +}; + +const char *project_type_to_str(int type) +{ + const char *version_type; + + switch (type) { + case 'V': + version_type = "Release"; + break; + case 'X': + version_type = "Engineering Sample"; + break; + case 'A' ... 'U': + version_type = "Custom"; + break; + default: + version_type = "Unknown"; + break; + } + + return version_type; +} + +static ssize_t imanager_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct imanager_platform_data *pdata = dev_get_platdata(dev); + const struct ec_info *info = &pdata->dev->info; + + return scnprintf(buf, PAGE_SIZE, "%s\n", info->pcb_name); +} + +static ssize_t imanager_kversion_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct imanager_platform_data *pdata = dev_get_platdata(dev); + const struct ec_info *info = &pdata->dev->info; + + return scnprintf(buf, PAGE_SIZE, "%d.%d\n", + info->version.kernel_major, + info->version.kernel_minor); +} + +static ssize_t imanager_fwversion_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct imanager_platform_data *pdata = dev_get_platdata(dev); + const struct ec_info *info = &pdata->dev->info; + + return scnprintf(buf, PAGE_SIZE, "%d.%d\n", + info->version.firmware_major, + info->version.firmware_minor); +} + +static ssize_t imanager_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct imanager_platform_data *pdata = dev_get_platdata(dev); + const struct ec_info *info = &pdata->dev->info; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + project_type_to_str(info->version.type)); +} + +static ssize_t imanager_chip_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct imanager_platform_data *pdata = dev_get_platdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", pdata->chip_name); +} + +static DEVICE_ATTR(imanager_name, S_IRUGO, imanager_name_show, NULL); +static DEVICE_ATTR(imanager_kversion, S_IRUGO, imanager_kversion_show, NULL); +static DEVICE_ATTR(imanager_fwversion, S_IRUGO, imanager_fwversion_show, NULL); +static DEVICE_ATTR(imanager_type, S_IRUGO, imanager_type_show, NULL); +static DEVICE_ATTR(imanager_chip_name, S_IRUGO, imanager_chip_name_show, NULL); + +static struct attribute *imanager_core_attributes[] = { + &dev_attr_imanager_name.attr, + &dev_attr_imanager_kversion.attr, + &dev_attr_imanager_fwversion.attr, + &dev_attr_imanager_type.attr, + &dev_attr_imanager_chip_name.attr, + NULL +}; + +static const struct attribute_group imanager_core_attr_group = { + .attrs = imanager_core_attributes, +}; + +static int imanager_platform_create(void) +{ + struct device *dev; + struct imanager_platform_data platdata; + int err; + + pdev = platform_device_alloc("imanager-core", -1); + if (!pdev) + return -ENOMEM; + + dev = &pdev->dev; + + err = platform_device_add_data(pdev, &platdata, sizeof(platdata)); + if (err) + goto exit_device_put; + + err = platform_device_add_resources(pdev, &imanager_ioresource, 1); + if (err) + goto exit_device_put; + + err = platform_device_add(pdev); + if (err) + goto exit_device_put; + + err = mfd_add_devices(dev, pdev->id, imanager_devs, + ARRAY_SIZE(imanager_devs), NULL, -1, NULL); + if (err) + goto exit_device_unregister; + + return 0; + +exit_device_unregister: + platform_device_unregister(pdev); +exit_device_put: + platform_device_put(pdev); + + return err; +} + +static int imanager_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imanager_platform_data *pdata = dev_get_platdata(dev); + struct imanager_device_data *imanager; + int ret; + + if (!pdev) + return -EINVAL; + + imanager = devm_kzalloc(dev, sizeof(*imanager), GFP_KERNEL); + if (!imanager) + return -ENOMEM; + + imanager->dev = dev; + mutex_init(&imanager->lock); + + platform_set_drvdata(pdev, imanager); + + pdata->dev = imanager_get_ec_device(); + pdata->chip_name = chip_names[pdata->dev->id]; + + dev_info(dev, "Found Advantech iManager %s - %s %d.%d/%d.%d (%s)\n", + pdata->chip_name, + pdata->dev->info.pcb_name, + pdata->dev->info.version.kernel_major, + pdata->dev->info.version.kernel_minor, + pdata->dev->info.version.firmware_major, + pdata->dev->info.version.firmware_minor, + project_type_to_str(pdata->dev->info.version.type)); + + ret = sysfs_create_group(&dev->kobj, &imanager_core_attr_group); + if (ret) + return ret; + + return 0; +} + +static int imanager_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + sysfs_remove_group(&dev->kobj, &imanager_core_attr_group); + + mfd_remove_devices(dev); + + return 0; +} + +static struct platform_driver imanager_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "imanager-core", + }, + .probe = imanager_probe, + .remove = imanager_remove, +}; + +static int __init imanager_init(void) +{ + int ret; + + ret = imanager_ec_probe(); + if (ret < 0) + return ret; + + ret = imanager_platform_create(); + if (ret) + return ret; + + ret = platform_driver_register(&imanager_driver); + if (ret) + return ret; + + return 0; +} + +static void __exit imanager_exit(void) +{ + if (pdev) + platform_device_unregister(pdev); + + platform_driver_unregister(&imanager_driver); +} + +module_init(imanager_init); +module_exit(imanager_exit); + +MODULE_DESCRIPTION("Advantech iManager Core Driver"); +MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imanager-core"); diff --git a/drivers/mfd/imanager-ec.c b/drivers/mfd/imanager-ec.c new file mode 100644 index 0000000..be554ba --- /dev/null +++ b/drivers/mfd/imanager-ec.c @@ -0,0 +1,1345 @@ +/* + * Advantech iManager Core - Firmware Interface + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch-ELdSlb/RfAS1Z/+hSey0Gg@public.gmane.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/bug.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/byteorder/generic.h> +#include <linux/module.h> +#include <linux/swab.h> +#include <linux/mfd/imanager/ec.h> + +/** + * This is the delay time between two EC transactions. + * Values lower than 200us are not encouraged and may + * cause I/O errors + */ +#define EC_MICRO_DELAY 200 +#define EC_MAX_RETRY 1000 + +#define EC_MSG_OFFSET_CMD 0UL +#define EC_MSG_OFFSET_STATUS 1UL +#define EC_MSG_OFFSET_PARAM 2UL +#define EC_MSG_OFFSET_DATA(N) (3UL + N) +#define EC_MSG_OFFSET_PAYLOAD(N) (7UL + N) + +/* The Device ID registers - 16 bit */ +#define DEVID_REG_MSB 0x20 +#define DEVID_REG_LSB 0x21 + +/* + * IT8528 based firmware require a read/write command offset. + */ +#define EC_CMD_OFFSET_READ 0xA0UL +#define EC_CMD_OFFSET_WRITE 0x50UL + +#define EC_STATUS_SUCCESS BIT(0) +#define EC_STATUS_CMD_COMPLETE BIT(7) + +#define PCB_NAME_MAX_SIZE 8UL +#define EC_I2C_BLOCK_SIZE 32 +#define EC_MAX_DID 32UL + +#define DID_LABEL_SIZE 24UL +#define DID_DESC_SIZE 32UL + +#define EC_KERNEL_MINOR(x) (LOBYTE16(x)) +#define EC_KERNEL_MAJOR(x) ({ \ + typeof(x) __x = (HIBYTE16(x)); \ + ((__x >> 4) * 10 + (__x & 0x000f)); }) +#define EC_FIRMWARE_MINOR(x) (LOBYTE16(x)) +#define EC_FIRMWARE_MAJOR(x) EC_KERNEL_MAJOR(x) +#define EC_PROJECT_CODE(x) ((char)(LOBYTE16(x))) + +enum ec_device_type { + ADC = 1, + DAC, + GPIO, + IRQ, + PWM, + SMB, + TACH +}; + +enum ec_device_id { + /* GPIO */ + ALTGPIO0 = 0x10, + ALTGPIO1, + ALTGPIO2, + ALTGPIO3, + ALTGPIO4, + ALTGPIO5, + ALTGPIO6, + ALTGPIO7, + /* Button (GPIO) */ + BUTTON0, + BUTTON1, + BUTTON2, + BUTTON3, + BUTTON4, + BUTTON5, + BUTTON6, + BUTTON7, + /* FAN */ + CPUFAN_2P, + CPUFAN_4P, + SYSFAN1_2P, + SYSFAN1_4P, + SYSFAN2_2P, + SYSFAN2_4P, + /* Brightness Control */ + BRIGHTNESS, + /* System Speaker */ + PCBEEP, + /* SMBus */ + SMBOEM0, + SMBOEM1, + SMBOEM2, + SMBEEPROM, + SMBTHERMAL0, + SMBTHERMAL1, + SMBSECEEP, + I2COEM, + /* Speaker */ + SPEAKER = 0x30, + /* SMBus */ + SMBEEP2K = 0x38, + OEMEEP, + OEMEEP2K, + PECI, + SMBOEM3, + SMLINK, + SMBSLV, + /* LED */ + POWERLED = 0x40, + BATLEDG, + OEMLED0, + OEMLED1, + OEMLED2, + BATLEDR, + /* Smart Battery */ + SMARTBAT1 = 0x48, + SMARTBAT2, + /* ADC */ + CMOSBAT = 0x50, + CMOSBATx2, + CMOSBATx10, + LIBAT, + LIBATx2, + LIBATx10, + ADC5VS0, + ADC5VS0x2, + ADC5VS0x10, + ADC5VS5, + ADC5VS5x2, + ADC5VS5x10, + ADC33VS0, + ADC33VS0x2, + ADC33VS0x10, + ADC33VS5, + ADC33VS5x2, /* 0x60 */ + ADC33VS5x10, + ADC12VS0, + ADC12VS0x2, + ADC12VS0x10, + VCOREA, + VCOREAx2, + VCOREAx10, + VCOREB, + VCOREBx2, + VCOREBx10, + ADCDC, + ADCDCx2, + ADCDCx10, + VSTBY, + VSTBYx2, + VSTBYx10, /* 0x70 */ + VAUX, + VAUXx2, + VAUXx10, + CURRENT, + /* Watchdog */ + WDIRQ = 0x78, + WDNMI, + /* FAN Tacho */ + TACHO0 = 0x80, + TACHO1, + TACHO2, + /* Brightness/Backlight Control */ + BRIGHTNESS2 = 0x88, + BACKLIGHT1, + BACKLIGHT2 +}; + +enum ec_dynamic_table_type { + EC_DYN_DID, + EC_DYN_HWP, + EC_DYN_POL +}; + +struct ec_devtbl { + int id; + int type; + int scale; + const char label[DID_LABEL_SIZE]; + const char description[DID_DESC_SIZE]; +}; + +struct ec_dyn_devtbl { + int did; /* Device ID */ + int hwp; /* Hardware Pin */ + int pol; /* Polarity */ + const struct ec_devtbl *devtbl; /* Device table Entry */ +}; + +struct imanager_data { + int (*read)(int cmd); + int (*write)(int cmd, int value); + + struct ec_dyn_devtbl dyn[EC_MAX_DID]; + + struct imanager_ec_device dev; + struct imanager_hwmon_device sensors; + struct imanager_gpio_device gpio; + struct imanager_i2c_device i2c; + struct imanager_watchdog_device wdt; + struct imanager_backlight_device blc; +}; + +struct ec_version_raw { + u16 kernel, + chipid, + project_code, + firmware; +}; + +enum ec_ram_type { + EC_RAM_ACPI = 1, + EC_RAM_HW, + EC_RAM_EXT +}; + +static struct imanager_data ec; + +static const struct ec_devtbl devtbl[] = { + { ALTGPIO0, GPIO, -1, "gpio0" }, + { ALTGPIO1, GPIO, -1, "gpio1" }, + { ALTGPIO2, GPIO, -1, "gpio2" }, + { ALTGPIO3, GPIO, -1, "gpio3" }, + { ALTGPIO4, GPIO, -1, "gpio4" }, + { ALTGPIO5, GPIO, -1, "gpio5" }, + { ALTGPIO6, GPIO, -1, "gpio6" }, + { ALTGPIO7, GPIO, -1, "gpio7" }, + { BUTTON0, GPIO, -1, "button0" }, + { BUTTON1, GPIO, -1, "button1" }, + { BUTTON2, GPIO, -1, "button2" }, + { BUTTON3, GPIO, -1, "button3" }, + { BUTTON4, GPIO, -1, "button4" }, + { BUTTON5, GPIO, -1, "button4" }, + { BUTTON6, GPIO, -1, "button4" }, + { BUTTON7, GPIO, -1, "button4" }, + { CPUFAN_2P, PWM, 2, "FAN CPU" }, + { CPUFAN_4P, PWM, 4, "FAN CPU" }, + { SYSFAN1_2P, PWM, 2, "FAN SYS1" }, + { SYSFAN1_4P, PWM, 4, "FAN SYS1" }, + { SYSFAN2_2P, PWM, 2, "FAN SYS2" }, + { SYSFAN2_4P, PWM, 4, "FAN SYS2" }, + { BRIGHTNESS, PWM, -1, "Brightness1" }, + { PCBEEP, PWM, -1, "Beep" }, + { SMBOEM0, SMB, -1, "SMB1" }, + { SMBOEM1, SMB, -1, "SMB2" }, + { SMBOEM2, SMB, -1, "SMB3" }, + { SMBEEPROM, SMB, -1, "SMBEEP" }, + { SMBTHERMAL0, SMB, -1, "SMBTHERM0" }, + { SMBTHERMAL1, SMB, -1, "SMBTHERM1" }, + { SMBSECEEP, SMB, -1, "SMBSECEEP" }, + { I2COEM, SMB, -1, "I2COEM" }, + { SPEAKER, DAC, -1, "Speaker" }, + { SMBEEP2K, SMB, -1, "SMBEEP2K" }, + { OEMEEP, SMB, -1, "OEMEEP" }, + { OEMEEP2K, SMB, -1, "OEMEEP2K" }, + { PECI, SMB, -1, "SMB_PECI" }, + { SMBOEM3, SMB, -1, "SMBOEM3" }, + { SMLINK, SMB, -1, "SMLINK" }, + { SMBSLV, SMB, -1, "SMBSLV" }, + { POWERLED, GPIO, -1, "Power LED" }, + { BATLEDG, GPIO, -1, "BATLEDG" }, + { OEMLED0, GPIO, -1, "OEMLED0" }, + { OEMLED1, GPIO, -1, "OEMLED1" }, + { OEMLED2, GPIO, -1, "OEMLED2" }, + { BATLEDR, GPIO, -1, "OEMLEDR" }, + { SMARTBAT1, SMB, -1, "SmartBat1" }, + { SMARTBAT2, SMB, -1, "SmartBat2" }, + { CMOSBAT, ADC, 1, "VBat" }, + { CMOSBATx2, ADC, 2, "VBat" }, + { CMOSBATx10, ADC, 10, "VBat" }, + { LIBAT, ADC, 1, "VBat2" }, + { LIBATx2, ADC, 2, "VBat2" }, + { LIBATx10, ADC, 10, "VBat2" }, + { ADC5VS0, ADC, 1, "+5V" }, + { ADC5VS0x2, ADC, 2, "+5V" }, + { ADC5VS0x10, ADC, 10, "+5V" }, + { ADC5VS5, ADC, 1, "+5V" }, + { ADC5VS5x2, ADC, 2, "+5V" }, + { ADC5VS5x10, ADC, 10, "+5V" }, + { ADC33VS0, ADC, 1, "+3.3V" }, + { ADC33VS0x2, ADC, 2, "+3.3V" }, + { ADC33VS0x10, ADC, 10, "+3.3V" }, + { ADC33VS5, ADC, 1, "+3.3V" }, + { ADC33VS5x2, ADC, 2, "+3.3V" }, + { ADC33VS5x10, ADC, 10, "+3.3V" }, + { ADC12VS0, ADC, 1, "+12V" }, + { ADC12VS0x2, ADC, 2, "+12V" }, + { ADC12VS0x10, ADC, 10, "+12V" }, + { VCOREA, ADC, 1, "VCore" }, + { VCOREAx2, ADC, 2, "VCore" }, + { VCOREAx10, ADC, 10, "VCore" }, + { VCOREB, ADC, 1, "VCore2" }, + { VCOREBx2, ADC, 2, "VCore2" }, + { VCOREBx10, ADC, 10, "VCore2" }, + { ADCDC, ADC, 1, "ADCDC" }, + { ADCDCx2, ADC, 2, "ADCDCx2" }, + { ADCDCx10, ADC, 10, "ADCDCx10" }, + { VSTBY, ADC, 1, "Vsb" }, + { VSTBYx2, ADC, 2, "Vsb" }, + { VSTBYx10, ADC, 10, "Vsb" }, + { VAUX, ADC, 1, "VAUX" }, + { VAUXx2, ADC, 2, "VAUX" }, + { VAUXx10, ADC, 10, "VAUX" }, + { CURRENT, ADC, 1, "Imon" }, + { WDIRQ, IRQ, -1, "WDIRQ" }, + { WDNMI, GPIO, -1, "WDNMI" }, + { TACHO0, TACH, -1, "Tacho1" }, + { TACHO1, TACH, -1, "Tacho2" }, + { TACHO2, TACH, -1, "Tacho3" }, + { BRIGHTNESS2, PWM, -1, "Brightness2" }, + { BACKLIGHT1, GPIO, -1, "Backlight1" }, + { BACKLIGHT2, GPIO, -1, "Backlight2" }, + { 0, 0, 0, "" } +}; + +/** + * EC I/O + */ + +static inline void imanager_delay(void) +{ + udelay(EC_MICRO_DELAY); +} + +static int wait_ibf_cleared(void) +{ + int i = 0; + + do { + if (!(inb(IT8516_CMD_PORT) & BIT(1))) + return 0; + imanager_delay(); + } while (i++ < EC_MAX_RETRY); + + return -ETIME; +} + +static int wait_obf_set(void) +{ + int i = 0; + + do { + if (inb(IT8516_CMD_PORT) & BIT(0)) + return 0; + imanager_delay(); + } while (i++ < EC_MAX_RETRY); + + return -ETIME; +} + +static inline int ec_inb(int addr, int reg) +{ + outb(reg, addr); + return inb(addr + 1); +} + +static inline void ec_outb(int addr, int reg, int val) +{ + outb(reg, addr); + outb(val, addr + 1); +} + +static inline int ec_io28_inb(int addr, int reg) +{ + int ret; + + ret = wait_ibf_cleared(); + if (ret) + return ret; + + /* clear data to prevent lock */ + inb(addr - 1); + + outb(reg, addr); + + ret = wait_obf_set(); + if (ret) + return ret; + + return inb(addr - 1); +} + +static inline int ec_io28_outb(int addr, int reg, int val) +{ + int ret; + + ret = wait_ibf_cleared(); + if (ret) + return ret; + + outb(reg, addr); + + ret = wait_ibf_cleared(); + if (ret) + return ret; + + outb(val, addr - 1); + + return 0; +} + +static int ec_io18_read(int cmd) +{ + return ec_inb(IT8518_CMD_PORT, cmd); +} + +static int ec_io18_write(int cmd, int value) +{ + ec_outb(IT8518_CMD_PORT, cmd, value); + + return 0; +} + +static int ec_io28_read(int cmd) +{ + return ec_io28_inb(IT8516_CMD_PORT, cmd + EC_CMD_OFFSET_READ); +} + +static int ec_io28_write(int cmd, int value) +{ + return ec_io28_outb(IT8516_CMD_PORT, cmd + EC_CMD_OFFSET_WRITE, value); +} + +/* Prevent FW lock */ +static void ec_clear_ports(void) +{ + inb(IT8516_DAT_PORT); + inb(IT8518_DAT_PORT); +} + +static inline u16 ec_read_chipid(u16 addr) +{ + return (ec_inb(addr, DEVID_REG_MSB) << 8 | + ec_inb(addr, DEVID_REG_LSB)); +} + +static int ec_wait_cmd_clear(void) +{ + int i = 0; + + do { + if (!ec.read(0)) + return 0; + imanager_delay(); + } while (i++ < EC_MAX_RETRY); + + pr_err("No respons from EC (timeout)\n"); + + return -ETIME; +} + +static int ec_read_ram(u8 bank, u8 offset, u8 len, u8 *buf, u8 bufsz) +{ + int i; + int ret; + + if (WARN_ON(!buf)) + return -EINVAL; + + ret = ec_wait_cmd_clear(); + if (ret) + return ret; + + ec.write(EC_MSG_OFFSET_PARAM, bank); + ec.write(EC_MSG_OFFSET_DATA(0), offset); + ec.write(EC_MSG_OFFSET_DATA(0x2C), len); + ec.write(EC_MSG_OFFSET_CMD, EC_CMD_RAM_RD); + + ret = ec_wait_cmd_clear(); + if (ret) + return ret; + + ret = ec.read(EC_MSG_OFFSET_STATUS); + if (ret != EC_STATUS_SUCCESS) + return -EIO; + + for (i = 0; (i < len) && (len < EC_MSG_SIZE) && (len <= bufsz); i++) + buf[i] = ec.read(EC_MSG_OFFSET_DATA(i + 1)); + + return 0; +} + +static int ec_write_ram(u8 bank, u8 offset, u8 len, u8 *buf) +{ + int i; + int ret; + + if (WARN_ON(!buf)) + return -EINVAL; + + ret = ec_wait_cmd_clear(); + if (ret) + return ret; + + ec.write(EC_MSG_OFFSET_PARAM, bank); + ec.write(EC_MSG_OFFSET_DATA(0), offset); + ec.write(EC_MSG_OFFSET_DATA(0x2C), len); + + for (i = 0; (i < len) && (len < EC_MSG_SIZE); i++) + ec.write(EC_MSG_OFFSET_DATA(i + 1), buf[i]); + + ec.write(EC_MSG_OFFSET_CMD, EC_CMD_RAM_WR); + + ret = ec_wait_cmd_clear(); + if (ret) + return ret; + + ret = ec.read(EC_MSG_OFFSET_STATUS); + if (ret != EC_STATUS_SUCCESS) + return -EIO; + + return 0; +} + +static int ec_read_dynamic_devtbl(struct imanager_data *ec) +{ + u32 i, j; + int ret; + struct ec_message did = { + .rlen = EC_MAX_DID, + .wlen = 0, + }; + struct ec_message hwp = { + .rlen = EC_MAX_DID, + .wlen = 0, + }; + struct ec_message pol = { + .rlen = EC_MAX_DID, + .wlen = 0, + }; + struct ec_dyn_devtbl *dyn; + + memset(ec->dyn, 0, sizeof(ec->dyn)); + + ret = imanager_msg_read(EC_CMD_DYN_TBL_RD, EC_DYN_DID, &did); + if (ret) + return -EIO; + + ret = imanager_msg_read(EC_CMD_DYN_TBL_RD, EC_DYN_HWP, &hwp); + if (ret) + return -EIO; + + ret = imanager_msg_read(EC_CMD_DYN_TBL_RD, EC_DYN_POL, &pol); + if (ret) + return -EIO; + + for (i = 0; (i < EC_MAX_DID) && did.u.data[i]; i++) { + dyn = &ec->dyn[i]; + for (j = 0; j < ARRAY_SIZE(devtbl); j++) { + if (devtbl[j].id == did.u.data[i]) { + dyn->did = did.u.data[i]; + dyn->hwp = hwp.u.data[i]; + dyn->pol = pol.u.data[i]; + dyn->devtbl = &devtbl[j]; + break; + } + } + } + + return 0; +} + +static int ec_read_buffer(u8 *data, int rlen) +{ + int ret, i, j; + int pages = rlen % EC_I2C_BLOCK_SIZE; + int rem = rlen / EC_I2C_BLOCK_SIZE; + + /* pre-condition: rlen <= 256 */ + + ret = ec_wait_cmd_clear(); + if (ret) + return ret; + + for (i = 0; i < pages; i++) { + ec.write(EC_MSG_OFFSET_PARAM, i); + ec.write(EC_MSG_OFFSET_CMD, EC_CMD_BUF_RD); + + ret = ec_wait_cmd_clear(); + if (ret) + return ret; + + ret = ec.read(EC_MSG_OFFSET_STATUS); + if (ret != EC_STATUS_SUCCESS) + return -EIO; + + for (j = 0; j < EC_I2C_BLOCK_SIZE; j++) + data[i * EC_I2C_BLOCK_SIZE + j] = + ec.read(EC_MSG_OFFSET_DATA(j)); + } + + if (rem) { + ec.write(EC_MSG_OFFSET_PARAM, pages); + ec.write(EC_MSG_OFFSET_CMD, EC_CMD_BUF_RD); + + ret = ec_wait_cmd_clear(); + if (ret) + return ret; + + ret = ec.read(EC_MSG_OFFSET_STATUS); + if (ret != EC_STATUS_SUCCESS) + return -EIO; + + for (j = 0; j < rem; j++) + data[pages * EC_I2C_BLOCK_SIZE + j] = + ec.read(EC_MSG_OFFSET_DATA(j)); + } + + return 0; +} + +static int +imanager_msg_trans(u8 cmd, u8 param, struct ec_message *msg, bool payload) +{ + int ret, i, len; + u32 offset; + + ret = ec_wait_cmd_clear(); + if (ret) + return ret; + + ec.write(EC_MSG_OFFSET_PARAM, param); + + if (msg && msg->wlen) { + if (!msg->data) { + for (i = 0; i < msg->wlen; i++) + ec.write(EC_MSG_OFFSET_DATA(i), + msg->u.data[i]); + } else { + for (i = 0; i < msg->wlen; i++) + ec.write(EC_MSG_OFFSET_DATA(i), msg->data[i]); + ec.write(EC_MSG_OFFSET_DATA(0x2c), msg->wlen); + } + } + + ec.write(EC_MSG_OFFSET_CMD, cmd); + ret = ec_wait_cmd_clear(); + if (ret) + return ret; + + /* GPIO and I2C have different success return values */ + ret = ec.read(EC_MSG_OFFSET_STATUS); + if ((ret != EC_STATUS_SUCCESS) && !(ret & EC_STATUS_CMD_COMPLETE)) + return -EIO; + /* + * EC I2C may return an error code which we need to hand-off + * to the caller + */ + else if (ret & 0x07e) + return ret; + + if (msg && msg->data) { + ret = ec_read_buffer(msg->data, msg->rlen); + if (ret < 0) + return ret; + } else if (msg && msg->rlen) { + if (msg->rlen == 0xff) + /* Use alternate message body for hwmon */ + len = ec.read(EC_MSG_OFFSET_DATA(0x2C)); + else + len = (msg->rlen > EC_MSG_SIZE ? EC_MSG_SIZE : + msg->rlen); + offset = payload ? EC_MSG_OFFSET_PAYLOAD(0) : + EC_MSG_OFFSET_DATA(0); + for (i = 0; i < len; i++) + msg->u.data[i] = ec.read(offset + i); + } + + return 0; +} + +int imanager_msg_read(u8 cmd, u8 param, struct ec_message *msg) +{ + return imanager_msg_trans(cmd, param, msg, false); +} +EXPORT_SYMBOL_GPL(imanager_msg_read); + +int imanager_msg_write(u8 cmd, u8 param, struct ec_message *msg) +{ + return imanager_msg_trans(cmd, param, msg, true); +} +EXPORT_SYMBOL_GPL(imanager_msg_write); + +int imanager_read_byte(u8 cmd, u8 param) +{ + int ret; + struct ec_message msg = { + .rlen = 1, + .wlen = 0, + }; + + ret = imanager_msg_read(cmd, param, &msg); + if (ret) + return ret; + + return msg.u.data[0]; +} +EXPORT_SYMBOL_GPL(imanager_read_byte); + +int imanager_read_word(u8 cmd, u8 param) +{ + int ret; + struct ec_message msg = { + .rlen = 2, + .wlen = 0, + }; + + ret = imanager_msg_read(cmd, param, &msg); + if (ret) + return ret; + + return (msg.u.data[0] << 8 | msg.u.data[1]); +} +EXPORT_SYMBOL_GPL(imanager_read_word); + +int imanager_write_byte(u8 cmd, u8 param, u8 byte) +{ + struct ec_message msg = { + .rlen = 0, + .wlen = 1, + .u = { + .data = { byte, 0 }, + }, + }; + + return imanager_msg_write(cmd, param, &msg); +} +EXPORT_SYMBOL_GPL(imanager_write_byte); + +int imanager_write_word(u8 cmd, u8 param, u16 word) +{ + struct ec_message msg = { + .rlen = 0, + .wlen = 2, + .u = { + .data = { HIBYTE16(word), LOBYTE16(word), 0 }, + }, + }; + + return imanager_msg_write(cmd, param, &msg); +} +EXPORT_SYMBOL_GPL(imanager_write_word); + +static int ec_hwram_read_byte(u8 offset) +{ + int ret; + u8 val; + + ret = ec_read_ram(EC_RAM_HW, offset, sizeof(val), &val, sizeof(val)); + if (ret < 0) { + pr_err("Failed to read from HWRAM @ 0x%02X\n", offset); + return ret; + } + + return val; +} + +int imanager_acpiram_read_byte(u8 offset) +{ + int ret; + u8 value; + + ret = ec_read_ram(EC_RAM_ACPI, offset, sizeof(value), (u8 *)&value, + sizeof(value)); + if (ret < 0) { + pr_err("Failed to read from ACPI RAM @ 0x%02X\n", offset); + return ret; + } + + return value; +} +EXPORT_SYMBOL_GPL(imanager_acpiram_read_byte); + +int imanager_acpiram_write_byte(u8 offset, u8 value) +{ + int ret; + + ret = ec_write_ram(EC_RAM_ACPI, offset, sizeof(value), &value); + if (ret) { + pr_err("Failed to write to ACPI RAM @ 0x%02X\n", offset); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(imanager_acpiram_write_byte); + +int imanager_acpiram_read_block(u8 offset, u8 *buf, u8 len) +{ + int ret; + + ret = ec_read_ram(EC_RAM_ACPI, offset, len, buf, len); + if (ret < 0) { + pr_err("Failed to read from ACPI RAM @ 0x%02X\n", offset); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(imanager_acpiram_read_block); + +int imanager_acpiram_write_block(u8 offset, u8 *buf, u8 len) +{ + int ret; + + ret = ec_write_ram(EC_RAM_ACPI, offset, len, buf); + if (ret) { + pr_err("Failed to write to ACPI RAM @ 0x%02X\n", offset); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(imanager_acpiram_write_block); + +int imanager_wait_proc_complete(u8 offset, int cond) +{ + int ret, i; + + for (i = 0; i < EC_MAX_RETRY; i++) { + ret = ec_hwram_read_byte(offset); + if (ret < 0) + return ret; + + if (ret == cond) + return 0; + + imanager_delay(); + } + + return -EIO; +} +EXPORT_SYMBOL_GPL(imanager_wait_proc_complete); + +static inline void ec_get_dev_attr(struct ec_dev_attr *attr, + const struct ec_dyn_devtbl *tbl) +{ + attr->did = tbl->did; + attr->hwp = tbl->hwp; + attr->pol = tbl->pol; + attr->scale = tbl->devtbl->scale; + attr->label = tbl->devtbl->label; +} + +static int ec_get_dev_gpio(struct imanager_data *ec) +{ + size_t i; + struct ec_dyn_devtbl *dyn; + struct imanager_gpio_device *gpio = &ec->gpio; + + for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) { + dyn = &ec->dyn[i]; + if (dyn->did && (dyn->devtbl->type == GPIO)) { + switch (dyn->did) { + case ALTGPIO0: + ec_get_dev_attr(&gpio->attr[0], dyn); + gpio->num++; + break; + case ALTGPIO1: + ec_get_dev_attr(&gpio->attr[1], dyn); + gpio->num++; + break; + case ALTGPIO2: + ec_get_dev_attr(&gpio->attr[2], dyn); + gpio->num++; + break; + case ALTGPIO3: + ec_get_dev_attr(&gpio->attr[3], dyn); + gpio->num++; + break; + case ALTGPIO4: + ec_get_dev_attr(&gpio->attr[4], dyn); + gpio->num++; + break; + case ALTGPIO5: + ec_get_dev_attr(&gpio->attr[5], dyn); + gpio->num++; + break; + case ALTGPIO6: + ec_get_dev_attr(&gpio->attr[6], dyn); + gpio->num++; + break; + case ALTGPIO7: + ec_get_dev_attr(&gpio->attr[7], dyn); + gpio->num++; + break; + case BUTTON0: + case BUTTON1: + case BUTTON2: + case BUTTON3: + case BUTTON4: + case BUTTON5: + case BUTTON6: + case BUTTON7: + case POWERLED: + case BATLEDG: + case OEMLED0: + case OEMLED1: + case OEMLED2: + case BATLEDR: + case WDNMI: + case BACKLIGHT1: + case BACKLIGHT2: + break; + default: + pr_err("DID 0x%02X not handled\n", dyn->did); + return -EINVAL; + } + } + } + + gpio->info = &ec->dev.info; + + return 0; +} + +static int ec_get_dev_adc(struct imanager_data *ec) +{ + size_t i; + struct ec_dyn_devtbl *dyn; + struct dev_adc *adc = &ec->sensors.adc; + + for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) { + dyn = &ec->dyn[i]; + if (dyn->did && (dyn->devtbl->type == ADC)) { + switch (dyn->did) { + case ADC12VS0: + case ADC12VS0x2: + case ADC12VS0x10: + ec_get_dev_attr(&adc->attr[0], dyn); + adc->num++; + break; + case ADC5VS5: + case ADC5VS5x2: + case ADC5VS5x10: + ec_get_dev_attr(&adc->attr[1], dyn); + adc->num++; + break; + case CMOSBAT: + case CMOSBATx2: + case CMOSBATx10: + ec_get_dev_attr(&adc->attr[2], dyn); + adc->num++; + break; + case VCOREA: + case ADC5VS0: + case ADC5VS0x2: + case ADC5VS0x10: + ec_get_dev_attr(&adc->attr[3], dyn); + adc->num++; + break; + case CURRENT: + case ADC33VS0: + case ADC33VS0x2: + case ADC33VS0x10: + ec_get_dev_attr(&adc->attr[4], dyn); + adc->num++; + break; + default: + pr_err("DID 0x%02X not handled\n", dyn->did); + return -EINVAL; + } + } + } + + return 0; +} + +static int ec_get_dev_fan(struct imanager_data *ec) +{ + size_t i; + struct ec_dyn_devtbl *dyn; + struct dev_fan *fan = &ec->sensors.fan; + + for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) { + dyn = &ec->dyn[i]; + if (dyn->did && ((dyn->devtbl->type == TACH) || + (dyn->devtbl->type == PWM))) { + switch (dyn->did) { + case CPUFAN_2P: + case CPUFAN_4P: + ec_get_dev_attr(&fan->attr[0], dyn); + fan->num++; + fan->active |= 1 << 0; + break; + case SYSFAN1_2P: + case SYSFAN1_4P: + ec_get_dev_attr(&fan->attr[1], dyn); + fan->num++; + fan->active |= 1 << 1; + break; + case SYSFAN2_2P: + case SYSFAN2_4P: + ec_get_dev_attr(&fan->attr[2], dyn); + fan->num++; + fan->active |= 1 << 2; + break; + case TACHO0: + case TACHO1: + case TACHO2: + case BRIGHTNESS: + case BRIGHTNESS2: + case PCBEEP: + break; + default: + pr_err("DID 0x%02X not handled\n", dyn->did); + return -EINVAL; + } + } + } + + return 0; +} + +static int ec_get_dev_hwmon(struct imanager_data *ec) +{ + int ret; + + ret = ec_get_dev_adc(ec); + if (ret < 0) + return ret; + + ret = ec_get_dev_fan(ec); + if (ret < 0) + return ret; + + ec->sensors.ecdev = &ec->dev; + + return 0; +} + +static int ec_get_dev_i2c(struct imanager_data *ec) +{ + size_t i; + struct ec_dyn_devtbl *dyn; + struct imanager_i2c_device *i2c = &ec->i2c; + + for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) { + dyn = &ec->dyn[i]; + if (dyn->did && (dyn->devtbl->type == SMB)) { + switch (dyn->did) { + case SMBEEPROM: + ec_get_dev_attr(&i2c->attr[0], dyn); + i2c->eeprom = &i2c->attr[0]; + i2c->num++; + break; + case I2COEM: + ec_get_dev_attr(&i2c->attr[1], dyn); + i2c->i2coem = &i2c->attr[1]; + i2c->num++; + break; + case SMBOEM0: + case SMBOEM1: + case SMBOEM2: + case SMBTHERMAL0: + case SMBTHERMAL1: + case SMBSECEEP: + case SMBEEP2K: + case OEMEEP: + case OEMEEP2K: + case PECI: + case SMBOEM3: + case SMLINK: + case SMBSLV: + case SMARTBAT1: + case SMARTBAT2: + break; + default: + pr_err("DID 0x%02X not handled\n", dyn->did); + return -EINVAL; + } + } + } + + i2c->ecdev = &ec->dev; + + return 0; +} + +static int ec_get_dev_blc(struct imanager_data *ec) +{ + size_t i; + struct ec_dyn_devtbl *dyn; + struct imanager_backlight_device *blc = &ec->blc; + + for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) { + dyn = &ec->dyn[i]; + if (dyn->did && (dyn->devtbl->type == PWM)) { + switch (dyn->did) { + case BRIGHTNESS: + ec_get_dev_attr(&blc->attr[0], dyn); + blc->brightness[0] = EC_ACPIRAM_BRIGHTNESS1; + blc->num++; + break; + case BRIGHTNESS2: + ec_get_dev_attr(&blc->attr[1], dyn); + blc->brightness[1] = EC_ACPIRAM_BRIGHTNESS2; + blc->num++; + break; + case CPUFAN_2P: + case CPUFAN_4P: + case SYSFAN1_2P: + case SYSFAN1_4P: + case SYSFAN2_2P: + case SYSFAN2_4P: + case PCBEEP: + case TACHO0: + case TACHO1: + case TACHO2: + break; + default: + pr_err("DID 0x%02X not handled\n", dyn->did); + return -EINVAL; + } + } + } + + blc->info = &ec->dev.info; + + return 0; +} + +static int ec_get_dev_wdt(struct imanager_data *ec) +{ + size_t i; + struct ec_dyn_devtbl *dyn; + struct imanager_watchdog_device *wdt = &ec->wdt; + + for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) { + dyn = &ec->dyn[i]; + if (dyn->did && (dyn->devtbl->type == IRQ)) { + switch (dyn->did) { + case WDIRQ: + ec_get_dev_attr(&wdt->attr[0], dyn); + wdt->irq = &wdt->attr[0]; + wdt->num++; + break; + case WDNMI: + ec_get_dev_attr(&wdt->attr[1], dyn); + wdt->nmi = &wdt->attr[1]; + wdt->num++; + break; + default: + pr_err("DID 0x%02X not handled\n", dyn->did); + return -EINVAL; + } + } + } + + wdt->info = &ec->dev.info; + + return 0; +} + +const struct imanager_ec_device *imanager_get_ec_device(void) +{ + return &ec.dev; +}; +EXPORT_SYMBOL_GPL(imanager_get_ec_device); + +const struct imanager_hwmon_device *imanager_get_hwmon_device(void) +{ + return &ec.sensors; +} +EXPORT_SYMBOL_GPL(imanager_get_hwmon_device); + +const struct imanager_gpio_device *imanager_get_gpio_device(void) +{ + return &ec.gpio; +} +EXPORT_SYMBOL_GPL(imanager_get_gpio_device); + +const struct imanager_i2c_device *imanager_get_i2c_device(void) +{ + return &ec.i2c; +} +EXPORT_SYMBOL_GPL(imanager_get_i2c_device); + +const struct imanager_backlight_device *imanager_get_backlight_device(void) +{ + return &ec.blc; +} +EXPORT_SYMBOL_GPL(imanager_get_backlight_device); + +const struct imanager_watchdog_device *imanager_get_watchdog_device(void) +{ + return &ec.wdt; +} +EXPORT_SYMBOL_GPL(imanager_get_watchdog_device); + +static int ec_get_version(struct ec_version *version) +{ + int ret; + u16 raw; + struct ec_version_raw ver; + + if (WARN_ON(!version)) + return -EINVAL; + + ret = ec_read_ram(EC_RAM_ACPI, EC_ACPIRAM_FW_RELEASE_RD, sizeof(ver), + (u8 *)&ver, sizeof(ver)); + if (ret < 0) + return ret; + + raw = swab16(ver.kernel); + version->kernel_major = EC_KERNEL_MAJOR(raw); + version->kernel_minor = EC_KERNEL_MINOR(raw); + + raw = swab16(ver.firmware); + version->firmware_major = EC_FIRMWARE_MAJOR(raw); + version->firmware_minor = EC_FIRMWARE_MINOR(raw); + + raw = swab16(ver.project_code); + version->type = EC_PROJECT_CODE(raw); + + return 0; +} + +static int ec_get_pcb_name(struct ec_info *info) +{ + int ret; + struct ec_message msg = { + .rlen = ARRAY_SIZE(info->pcb_name), + .wlen = 0, + }; + + if (WARN_ON(!info)) + return -EINVAL; + + ret = imanager_msg_read(EC_CMD_FW_INFO_RD, 0, &msg); + if (ret) + return ret; + + /* + * Sadly, the string is not Null-terminated so we will need to read a + * fixed amount of chars. There is, apparently, no exact definition + * of board name (SOM6867 vs. MIO-5271). + */ + memset(info->pcb_name, 0, ARRAY_SIZE(info->pcb_name)); + strncpy(info->pcb_name, (const char *)msg.u.data, PCB_NAME_MAX_SIZE); + + if (strchr(info->pcb_name, '-') == NULL) + info->pcb_name[PCB_NAME_MAX_SIZE - 1] = '\0'; + + return 0; +} + +static int ec_get_fw_info(struct ec_info *info) +{ + int ret; + + if (WARN_ON(!info)) + return -EINVAL; + + ret = ec_get_version(&info->version); + if (ret) + return ret; + + return ec_get_pcb_name(info); +} + +static int ec_init(void) +{ + int ret; + + ec_clear_ports(); + + ret = ec_read_dynamic_devtbl(&ec); + if (ret) + return ret; + + ret = ec_get_fw_info(&ec.dev.info); + if (ret < 0) + return ret; + + ret = ec_get_dev_gpio(&ec); + if (ret < 0) + return ret; + + ret = ec_get_dev_hwmon(&ec); + if (ret < 0) + return ret; + + ret = ec_get_dev_i2c(&ec); + if (ret < 0) + return ret; + + ret = ec_get_dev_blc(&ec); + if (ret < 0) + return ret; + + ret = ec_get_dev_wdt(&ec); + if (ret < 0) + return ret; + + return 0; +} + +int imanager_ec_probe(void) +{ + int chipid = ec_read_chipid(EC_BASE_ADDR); + + memset((void *)&ec, 0, sizeof(ec)); + + switch (chipid) { + case EC_DEVID_IT8516: + pr_err("EC IT8516 not supported\n"); + ec.dev.id = IT8516; + return -ENODEV; + case EC_DEVID_IT8518: + ec.read = ec_io18_read; + ec.write = ec_io18_write; + ec.dev.id = IT8518; + break; + case EC_DEVID_IT8528: + ec.read = ec_io28_read; + ec.write = ec_io28_write; + ec.dev.id = IT8528; + break; + default: + return -ENODEV; + } + + ec.dev.addr = EC_BASE_ADDR; + + return ec_init(); +} + diff --git a/include/linux/mfd/imanager/core.h b/include/linux/mfd/imanager/core.h new file mode 100644 index 0000000..05c77fa --- /dev/null +++ b/include/linux/mfd/imanager/core.h @@ -0,0 +1,31 @@ +/* + * Advantech iManager MFD core + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch-ELdSlb/RfAS1Z/+hSey0Gg@public.gmane.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __CORE_H__ +#define __CORE_H__ + +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/types.h> +#include <linux/mfd/imanager/ec.h> + +struct imanager_platform_data { + const struct imanager_ec_device *dev; + const char *chip_name; +}; + +struct imanager_device_data { + struct device *dev; + struct mutex lock; +}; + +#endif diff --git a/include/linux/mfd/imanager/ec.h b/include/linux/mfd/imanager/ec.h new file mode 100644 index 0000000..783f268 --- /dev/null +++ b/include/linux/mfd/imanager/ec.h @@ -0,0 +1,210 @@ +/* + * Advantech iManager core - firmware interface + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch-ELdSlb/RfAS1Z/+hSey0Gg@public.gmane.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __EC_H__ +#define __EC_H__ + +#include <linux/types.h> + +#define EC_DEVID_IT8516 0x8516 +#define EC_DEVID_IT8518 0x8518 +#define EC_DEVID_IT8528 0x8528 + +#define EC_BASE_ADDR 0x029C + +#define IT8516_CMD_PORT 0x029A /* it8528 */ +#define IT8516_DAT_PORT 0x0299 + +#define IT8518_CMD_PORT 0x029E /* it8518 */ +#define IT8518_DAT_PORT 0x029F + +#define EC_GPIO_MAX_NUM 8 +#define EC_HWM_MAX_ADC 5 +#define EC_HWM_MAX_FAN 3 +#define EC_BLC_MAX_NUM 2 +#define EC_SMB_MAX_NUM 4 +#define EC_WDT_MAX_NUM 2 + +#define PCB_NAME_SIZE 32 +#define EC_PAYLOAD_SIZE 40 +#define EC_MSG_SIZE sizeof(struct ec_smb_message) + +#define LOBYTE16(x) (x & 0x00FF) +#define HIBYTE16(x) (LOBYTE16(x >> 8)) +#define LOADDR16(x) LOBYTE16(x) +#define HIADDR16(x) (x >= 0xF000 ? LOBYTE16(x >> 8) : 0) + +/* + * iManager commands + */ +#define EC_CMD_HWP_RD 0x11UL +#define EC_CMD_HWP_WR 0x12UL +#define EC_CMD_GPIO_DIR_RD 0x30UL +#define EC_CMD_GPIO_DIR_WR 0x31UL +#define EC_CMD_PWM_FREQ_RD 0x36UL +#define EC_CMD_PWM_FREQ_WR 0x32UL +#define EC_CMD_PWM_POL_RD 0x37UL +#define EC_CMD_PWM_POL_WR 0x33UL +#define EC_CMD_SMB_FREQ_RD 0x34UL +#define EC_CMD_SMB_FREQ_WR 0x35UL +#define EC_CMD_FAN_CTL_RD 0x40UL +#define EC_CMD_FAN_CTL_WR 0x41UL +#define EC_CMD_THZ_RD 0x42UL +#define EC_CMD_DYN_TBL_RD 0x20UL +#define EC_CMD_FW_INFO_RD 0xF0UL +#define EC_CMD_BUF_CLR 0xC0UL +#define EC_CMD_BUF_RD 0xC1UL +#define EC_CMD_BUF_WR 0xC2UL +#define EC_CMD_RAM_RD 0x1EUL +#define EC_CMD_RAM_WR 0x1FUL +#define EC_CMD_I2C_RW 0x0EUL +#define EC_CMD_I2C_WR 0x0FUL +#define EC_CMD_WDT_CTRL 0x28UL + +/* + * ACPI and HW RAM offsets + */ +#define EC_ACPIRAM_FAN_ALERT 0x6FUL +#define EC_ACPIRAM_FAN_SPEED_LIMIT 0x76UL +#define EC_ACPIRAM_BRIGHTNESS1 0x50UL +#define EC_ACPIRAM_BRIGHTNESS2 0x52UL +#define EC_ACPIRAM_BLC_CTRL 0x99UL +#define EC_ACPIRAM_FW_RELEASE_RD 0xF8UL + +enum chips { IT8516, IT8518, IT8528 }; + +struct ec_message_header { + u8 addr_low; /* SMB low-byte address or data low-byte */ + /* of byte-/word-transaction */ + u8 addr_high; /* SMB high-byte address or data high-byte */ + /* of word-transaction */ + u8 rlen; /* SMB read length */ + u8 wlen; /* SMB write length */ + u8 cmd; /* SMB command */ +}; + +struct ec_smb_message { + struct ec_message_header hdr; + u8 data[EC_PAYLOAD_SIZE]; +}; + +struct ec_message { + u8 rlen; /* EC message read length */ + u8 wlen; /* EC message write length */ + union { + struct ec_smb_message smb; + u8 data[EC_MSG_SIZE]; + } u; + + u8 *data; +}; + +struct ec_version { + u32 kernel_major; + u32 kernel_minor; + u32 firmware_major; + u32 firmware_minor; + u32 type; +}; + +struct ec_info { + struct ec_version version; + char pcb_name[PCB_NAME_SIZE]; +}; + +struct imanager_ec_device { + int id; /* enum chip */ + u16 addr; + struct ec_info info; +}; + +struct ec_dev_attr { + int did; /* Device ID */ + int hwp; /* Hardware Pin number */ + int pol; /* Polarity */ + int scale; /* Scaling factor */ + const char *label; +}; + +struct imanager_gpio_device { + u32 num; + struct ec_dev_attr attr[EC_GPIO_MAX_NUM]; + struct ec_info *info; +}; + +struct dev_adc { + u32 num; + struct ec_dev_attr attr[EC_HWM_MAX_ADC]; +}; + +struct dev_fan { + u32 num; + u32 active; + struct ec_dev_attr attr[EC_HWM_MAX_FAN]; +}; + +struct imanager_hwmon_device { + struct dev_adc adc; + struct dev_fan fan; + struct imanager_ec_device *ecdev; +}; + +struct imanager_i2c_device { + u32 num; + struct ec_dev_attr attr[EC_SMB_MAX_NUM]; + struct ec_dev_attr *eeprom; + struct ec_dev_attr *i2coem; + struct imanager_ec_device *ecdev; +}; + +struct imanager_backlight_device { + u32 num; + struct ec_dev_attr attr[EC_BLC_MAX_NUM]; + u8 brightness[EC_BLC_MAX_NUM]; + struct ec_info *info; +}; + +struct imanager_watchdog_device { + u32 num; + struct ec_dev_attr attr[EC_WDT_MAX_NUM]; + struct ec_dev_attr *irq; + struct ec_dev_attr *nmi; + struct ec_info *info; +}; + +/* Must be called first, obviously */ +int imanager_ec_probe(void); + +int imanager_msg_write(u8 cmd, u8 param, struct ec_message *msg); +int imanager_msg_read(u8 cmd, u8 param, struct ec_message *msg); + +int imanager_read_byte(u8 cmd, u8 param); +int imanager_read_word(u8 cmd, u8 param); + +int imanager_write_byte(u8 cmd, u8 param, u8 byte); +int imanager_write_word(u8 cmd, u8 param, u16 word); + +int imanager_acpiram_read_byte(u8 offset); +int imanager_acpiram_write_byte(u8 offset, u8 value); +int imanager_acpiram_read_block(u8 offset, u8 *buf, u8 len); +int imanager_acpiram_write_block(u8 offset, u8 *buf, u8 len); + +int imanager_wait_proc_complete(u8 offset, int cond); + +const struct imanager_ec_device *imanager_get_ec_device(void); +const struct imanager_hwmon_device *imanager_get_hwmon_device(void); +const struct imanager_gpio_device *imanager_get_gpio_device(void); +const struct imanager_i2c_device *imanager_get_i2c_device(void); +const struct imanager_watchdog_device *imanager_get_watchdog_device(void); +const struct imanager_backlight_device *imanager_get_backlight_device(void); + +#endif -- 2.6.4 -- To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2016-01-10 9:10 ` richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w @ 2016-01-10 10:11 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-10 10:11 UTC (permalink / raw) Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/mfd/imanager-core.c:248:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-core.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/mfd/imanager-core.c +++ b/drivers/mfd/imanager-core.c @@ -245,7 +245,6 @@ static int imanager_remove(struct platfo static struct platform_driver imanager_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager-core", }, .probe = imanager_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-01-10 10:11 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-10 10:11 UTC (permalink / raw) To: richard.dorsch Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/mfd/imanager-core.c:248:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-core.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/mfd/imanager-core.c +++ b/drivers/mfd/imanager-core.c @@ -245,7 +245,6 @@ static int imanager_remove(struct platfo static struct platform_driver imanager_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager-core", }, .probe = imanager_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v2 3/6] Add Advantech iManager HWmon driver
2016-01-10 9:11 ` richard.dorsch
@ 2016-01-10 10:25 kbuild test robot
2016-01-10 9:11 ` richard.dorsch
0 siblings, 1 reply; 40+ messages in thread
From: kbuild test robot @ 2016-01-10 10:25 UTC (permalink / raw)
Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog,
linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga,
Richard Vidal-Dorsch
Hi Richard,
[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.4-rc8 next-20160108]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/richard-dorsch-gmail-com/Add-Advantech-iManager-EC-driver-set/20160110-171635
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
coccinelle warnings: (new ones prefixed by >>)
>> drivers/hwmon/imanager-hwmon.c:1047:3-8: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v2 3/6] Add Advantech iManager HWmon driver @ 2016-01-10 9:11 ` richard.dorsch 2016-01-10 10:25 ` kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: richard.dorsch @ 2016-01-10 9:11 UTC (permalink / raw) To: linux-kernel Cc: lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch From: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Richard Vidal-Dorsch <richard.dorsch@gmail.com> --- Documentation/hwmon/imanager | 59 ++ drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 2 + drivers/hwmon/imanager-ec-hwmon.c | 606 +++++++++++++++++++++ drivers/hwmon/imanager-hwmon.c | 1058 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/imanager/hwmon.h | 120 ++++ 6 files changed, 1857 insertions(+) create mode 100644 Documentation/hwmon/imanager create mode 100644 drivers/hwmon/imanager-ec-hwmon.c create mode 100644 drivers/hwmon/imanager-hwmon.c create mode 100644 include/linux/mfd/imanager/hwmon.h diff --git a/Documentation/hwmon/imanager b/Documentation/hwmon/imanager new file mode 100644 index 0000000..0705cf9 --- /dev/null +++ b/Documentation/hwmon/imanager @@ -0,0 +1,59 @@ +Kernel driver imanager_hwmon +============================ + +This platform driver provides support for iManager Hardware Monitoring +and FAN control. + +This driver depends on imanager (mfd). + +Description +----------- + +This driver provides support for the Advantech iManager Hardware Monitoring EC. + +The Advantech iManager supports up to 3 fan rotation speed sensors, +3 temperature monitoring sources and up to 5 voltage sensors, VID, alarms and +a automatic fan regulation strategy (as well as manual fan control mode). + +Temperatures are measured in degrees Celsius and measurement resolution is +1 degC. An Alarm is triggered when the temperature gets higher than the high +limit; it stays on until the temperature falls below the high limit. + +Fan rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. No fan +speed divider support available. + +Voltage sensors (also known as IN sensors) report their values in millivolts. +An alarm is triggered if the voltage has crossed a programmable minimum +or maximum limit. + +The driver supports automatic fan control mode known as Thermal Cruise. +In this mode, the firmware attempts to keep the measured temperature in a +predefined temperature range. If the temperature goes out of range, fan +is driven slower/faster to reach the predefined range again. + +The mode works for fan1-fan3. + +sysfs attributes +---------------- + +pwm[1-3] - this file stores PWM duty cycle or DC value (fan speed) in range: + 0 (lowest speed) to 255 (full) + +pwm[1-3]_enable - this file controls mode of fan/temperature control: + * 0 Fan control disabled (fans set to maximum speed) + * 1 Manual mode, write to pwm[1-3] any value 0-255 + * 2 "Fan Speed Cruise" mode + +pwm[1-3]_mode - controls if output is PWM or DC level + * 0 DC output + * 1 PWM output + +Speed Cruise mode (2) +--------------------- + +This mode tries to keep the fan speed constant within min/max speed. + +fan[1-3]_min - Minimum fan speed +fan[1-3]_max - Maximum fan speed + diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 80a73bf..776bb8a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -613,6 +613,18 @@ config SENSORS_CORETEMP sensor inside your CPU. Most of the family 6 CPUs are supported. Check Documentation/hwmon/coretemp for details. +config SENSORS_IMANAGER + tristate "Advantech iManager Hardware Monitoring" + depends on MFD_IMANAGER + select HWMON_VID + help + This enables support for Advantech iManager hardware monitoring + of some Advantech SOM, MIO, AIMB, and PCM modules/boards. + Requires mfd-core and imanager-core to function properly. + + This driver can also be built as a module. If so, the module + will be called imanager_hwmon. + config SENSORS_IT87 tristate "ITE IT87xx and compatibles" depends on !PPC diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 12a3239..53752bc 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -76,6 +76,8 @@ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o +imanager_hwmon-objs := imanager-hwmon.o imanager-ec-hwmon.o +obj-$(CONFIG_SENSORS_IMANAGER) += imanager_hwmon.o obj-$(CONFIG_SENSORS_INA209) += ina209.o obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o obj-$(CONFIG_SENSORS_IT87) += it87.o diff --git a/drivers/hwmon/imanager-ec-hwmon.c b/drivers/hwmon/imanager-ec-hwmon.c new file mode 100644 index 0000000..1920835 --- /dev/null +++ b/drivers/hwmon/imanager-ec-hwmon.c @@ -0,0 +1,606 @@ +/* + * Advantech iManager Hardware Monitoring core + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/bug.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/byteorder/generic.h> +#include <linux/swab.h> +#include <linux/mfd/imanager/ec.h> +#include <linux/mfd/imanager/hwmon.h> + +#define HWM_STATUS_UNDEFINED_ITEM 2UL +#define HWM_STATUS_UNDEFINED_DID 3UL +#define HWM_STATUS_UNDEFINED_HWPIN 4UL + +/* + * FAN defs + */ + +struct fan_dev_config { + u8 did, + hwpin, + tachoid, + status, + control, + temp_max, + temp_min, + temp_stop, + pwm_max, + pwm_min; + u16 rpm_max; + u16 rpm_min; + u8 debounce; /* debounce time, not used */ + u8 temp; /* Current Thermal Zone Temperature */ + u16 rpm_target; /* RPM Target Speed, not used */ +}; + +struct fan_status { + u32 sysctl : 1, /* System Control flag */ + tacho : 1, /* FAN tacho source defined */ + pulse : 1, /* FAN pulse type defined */ + thermal : 1, /* Thermal zone init */ + i2clink : 1, /* I2C protocol fail flag (thermal sensor) */ + dnc : 1, /* don't care */ + mode : 2; /* FAN Control mode */ +}; + +struct fan_alert_limit { + u16 fan0_min, + fan0_max, + fan1_min, + fan1_max, + fan2_min, + fan2_max; +}; + +struct fan_alert_flag { + u32 fan0_min_alarm : 1, + fan0_max_alarm : 1, + fan1_min_alarm : 1, + fan1_max_alarm : 1, + fan2_min_alarm : 1, + fan2_max_alarm : 1, + dnc : 2; /* don't care */ +}; + +/*----------------------------------------------------* + * FAN Control bit field * + * enable: 0:Disabled, 1:Enabled * + * type: 0:PWM, 1:RPM * + * pulse: 0:Undefined 1:2 Pulse 2:4 Pulse * + * tacho: 1:CPU FAN, 2:SYS1 FAN, 3:SYS2 FAN * + * mode: 0:Off, 1:Full, 2:Manual, 3:Auto * + *- 7 6 ---- 5 4 --- 3 2 ----- 1 -------- 0 -------* + * MODE | TACHO | PULSE | TYPE | ENABLE * + *----------------------------------------------------*/ +struct fan_ctrl { + u32 enable : 1, /* SmartFAN control on/off */ + type : 1, /* FAN control type [0, 1] PWM/RPM */ + pulse : 2, /* FAN pulse [0..2] */ + tacho : 2, /* FAN Tacho Input [1..3] */ + mode : 2; /* off/full/manual/auto */ +}; + +enum fan_dev_ctrl { + CTRL_STATE = 3, + OPMODE, + IDSENSOR, + ACTIVE, + CTRL_MODE, +}; + +enum fan_limit { + LIMIT_PWM, + LIMIT_RPM, + LIMIT_TEMP, +}; + +static const char * const fan_temp_label[] = { + "Temp CPU", + "Temp SYS1", + "Temp SYS2", + NULL, +}; + +static const struct imanager_hwmon_device *hwmon; + +static inline int hwm_get_adc_value(u8 did) +{ + return imanager_read_word(EC_CMD_HWP_RD, did); +} + +static inline int hwm_get_rpm_value(u8 did) +{ + return imanager_read_word(EC_CMD_HWP_RD, did); +} + +static inline int hwm_get_pwm_value(u8 did) +{ + return imanager_read_byte(EC_CMD_HWP_RD, did); +} + +static inline int hwm_set_pwm_value(u8 did, u8 val) +{ + return imanager_write_byte(EC_CMD_HWP_WR, did, val); +} + +static int hwm_read_fan_config(int num, struct fan_dev_config *cfg) +{ + int ret; + struct ec_message msg = { + .rlen = 0xff, /* use alternative message body */ + .wlen = 0, + }; + struct fan_dev_config *_cfg = (struct fan_dev_config *)&msg.u.data; + + if (WARN_ON(!cfg)) + return -EINVAL; + + memset(cfg, 0, sizeof(struct fan_dev_config)); + + ret = imanager_msg_read(EC_CMD_FAN_CTL_RD, num, &msg); + if (ret) + return ret; + + if (!_cfg->did) { + pr_err("Invalid FAN%d device ID - possible firmware bug\n", + num); + return -ENODEV; + } + + memcpy(cfg, &msg.u.data, sizeof(struct fan_dev_config)); + + return 0; +} + +static int hwm_write_fan_config(int fnum, struct fan_dev_config *cfg) +{ + int ret; + struct ec_message msg = { + .rlen = 0, + .wlen = sizeof(struct fan_dev_config), + }; + + if (!cfg->did) + return -ENODEV; + + msg.data = (u8 *)cfg; + + ret = imanager_msg_write(EC_CMD_FAN_CTL_WR, fnum, &msg); + if (ret < 0) + return ret; + + switch (ret) { + case 0: + break; + case HWM_STATUS_UNDEFINED_ITEM: + case HWM_STATUS_UNDEFINED_DID: + case HWM_STATUS_UNDEFINED_HWPIN: + return -EFAULT; + default: + pr_err("Unknown error status of fan%d (%d)\n", fnum, ret); + return -EIO; + } + + return 0; +} + +static inline void hwm_set_temp_limit(struct fan_dev_config *cfg, + const struct hwm_fan_temp_limit *temp) +{ + cfg->temp_stop = temp->stop; + cfg->temp_min = temp->min; + cfg->temp_max = temp->max; +} + +static inline void hwm_set_pwm_limit(struct fan_dev_config *cfg, + const struct hwm_fan_limit *pwm) +{ + cfg->pwm_min = pwm->min; + cfg->pwm_max = pwm->max; +} + +static inline void hwm_set_rpm_limit(struct fan_dev_config *cfg, + const struct hwm_fan_limit *rpm) +{ + cfg->rpm_min = swab16(rpm->min); + cfg->rpm_max = swab16(rpm->max); +} + +static inline void hwm_set_limit(struct fan_dev_config *cfg, + const struct hwm_sensors_limit *limit) +{ + hwm_set_temp_limit(cfg, &limit->temp); + hwm_set_pwm_limit(cfg, &limit->pwm); + hwm_set_rpm_limit(cfg, &limit->rpm); +} + +static int hwm_core_get_fan_alert_flag(struct fan_alert_flag *flag) +{ + int ret; + u8 *value = (u8 *)flag; + + ret = imanager_acpiram_read_byte(EC_ACPIRAM_FAN_ALERT); + if (ret < 0) + return ret; + + *value = ret; + + return 0; +} + +static int hwm_core_get_fan_alert_limit(int fnum, + struct hwm_smartfan *fan) +{ + int ret; + struct fan_alert_limit limit; + struct fan_alert_flag flag; + + ret = imanager_acpiram_read_block(EC_ACPIRAM_FAN_SPEED_LIMIT, + (u8 *)&limit, sizeof(limit)); + if (ret < 0) + return ret; + + ret = hwm_core_get_fan_alert_flag(&flag); + if (ret < 0) + return ret; + + switch (fnum) { + case FAN_CPU: + fan->alert.min = swab16(limit.fan0_min); + fan->alert.max = swab16(limit.fan0_max); + fan->alert.min_alarm = flag.fan0_min_alarm; + fan->alert.max_alarm = flag.fan0_max_alarm; + break; + case FAN_SYS1: + fan->alert.min = swab16(limit.fan1_min); + fan->alert.max = swab16(limit.fan1_max); + fan->alert.min_alarm = flag.fan1_min_alarm; + fan->alert.max_alarm = flag.fan1_max_alarm; + break; + case FAN_SYS2: + fan->alert.min = swab16(limit.fan2_min); + fan->alert.max = swab16(limit.fan2_max); + fan->alert.min_alarm = flag.fan2_min_alarm; + fan->alert.max_alarm = flag.fan2_max_alarm; + break; + default: + pr_err("Unknown FAN ID %d\n", fnum); + return -EINVAL; + } + + return 0; +} + +static int hwm_core_fan_set_alert_limit(int fnum, + struct hwm_fan_alert *alert) +{ + int ret; + struct fan_alert_limit limit; + + ret = imanager_acpiram_read_block(EC_ACPIRAM_FAN_SPEED_LIMIT, + (u8 *)&limit, sizeof(limit)); + if (ret < 0) + return ret; + + switch (fnum) { + case FAN_CPU: + limit.fan0_min = swab16(alert->min); + limit.fan0_max = swab16(alert->max); + break; + case FAN_SYS1: + limit.fan1_min = swab16(alert->min); + limit.fan1_max = swab16(alert->max); + break; + case FAN_SYS2: + limit.fan2_min = swab16(alert->min); + limit.fan2_max = swab16(alert->max); + break; + default: + pr_err("Unknown FAN ID %d\n", fnum); + return -EINVAL; + } + + return imanager_acpiram_write_block(EC_ACPIRAM_FAN_SPEED_LIMIT, + (u8 *)&limit, sizeof(limit)); +} + +/* HWM CORE API */ + +const char *hwm_core_adc_get_label(int num) +{ + if (WARN_ON(num >= hwmon->adc.num)) + return NULL; + + return hwmon->adc.attr[num].label; +} + +const char *hwm_core_fan_get_label(int num) +{ + if (WARN_ON(num >= hwmon->fan.num)) + return NULL; + + return hwmon->fan.attr[num].label; +} + +const char *hwm_core_fan_get_temp_label(int num) +{ + if (WARN_ON(num >= hwmon->fan.num)) + return NULL; + + return fan_temp_label[num]; +} + +int hwm_core_adc_is_available(int num) +{ + if (num >= EC_HWM_MAX_ADC) + return -EINVAL; + + return hwmon->adc.attr[num].did ? 0 : -ENODEV; +} + +int hwm_core_adc_get_value(int num, struct hwm_voltage *volt) +{ + int ret; + + volt->valid = false; + + ret = hwm_core_adc_is_available(num); + if (ret < 0) + return ret; + + ret = hwm_get_adc_value(hwmon->adc.attr[num].did); + if (ret < 0) + return ret; + + volt->value = ret * hwmon->adc.attr[num].scale; + volt->valid = true; + + return 0; +} + +int hwm_core_fan_get_ctrl(int num, struct hwm_smartfan *fan) +{ + int ret; + struct fan_dev_config cfg; + struct fan_ctrl *ctrl = (struct fan_ctrl *)&cfg.control; + + if (WARN_ON((num >= HWM_MAX_FAN) || !fan)) + return -EINVAL; + + memset(fan, 0, sizeof(struct hwm_smartfan)); + + ret = hwm_read_fan_config(num, &cfg); + if (ret < 0) + return ret; + + fan->pulse = ctrl->pulse; + fan->type = ctrl->type; + + /* + * It seems that fan->mode does not always report the correct + * FAN mode so the only way of reporting the current FAN mode + * is to read back ctrl->mode. + */ + fan->mode = ctrl->mode; + + ret = hwm_get_rpm_value(cfg.tachoid); + if (ret < 0) { + pr_err("Failed to read FAN speed\n"); + return ret; + } + + fan->speed = ret; + + ret = hwm_get_pwm_value(hwmon->fan.attr[num].did); + if (ret < 0) { + pr_err("Failed to read FAN%d PWM\n", num); + return ret; + } + + fan->pwm = ret; + + fan->alarm = (fan->pwm && !fan->speed) ? 1 : 0; + + fan->limit.temp.min = cfg.temp_min; + fan->limit.temp.max = cfg.temp_max; + fan->limit.temp.stop = cfg.temp_stop; + fan->limit.pwm.min = cfg.pwm_min; + fan->limit.pwm.max = cfg.pwm_max; + fan->limit.rpm.min = swab16(cfg.rpm_min); + fan->limit.rpm.max = swab16(cfg.rpm_max); + + ret = hwm_core_get_fan_alert_limit(num, fan); + if (ret) + return ret; + + fan->valid = true; + + return 0; +} + +int hwm_core_fan_set_ctrl(int num, int fmode, int ftype, int pwm, int pulse, + struct hwm_sensors_limit *limit, + struct hwm_fan_alert *alert) +{ + int ret; + struct fan_dev_config cfg; + struct fan_ctrl *ctrl = (struct fan_ctrl *)&cfg.control; + struct hwm_sensors_limit _limit = { {0, 0, 0}, {0, 0}, {0, 0} }; + + if (WARN_ON(num >= HWM_MAX_FAN)) + return -EINVAL; + + ret = hwm_read_fan_config(num, &cfg); + if (ret < 0) { + pr_err("Failed while reading FAN %s config\n", + hwmon->fan.attr[num].label); + return ret; + } + + if (!limit) + limit = &_limit; + + switch (fmode) { + case MODE_OFF: + ctrl->type = CTRL_PWM; + ctrl->mode = MODE_OFF; + break; + case MODE_FULL: + ctrl->type = CTRL_PWM; + ctrl->mode = MODE_FULL; + break; + case MODE_MANUAL: + ctrl->type = CTRL_PWM; + ctrl->mode = MODE_MANUAL; + ret = hwm_set_pwm_value(hwmon->fan.attr[num].did, pwm); + if (ret < 0) + return ret; + break; + case MODE_AUTO: + switch (ftype) { + case CTRL_PWM: + limit->rpm.min = 0; + limit->rpm.max = 0; + ctrl->type = CTRL_PWM; + break; + case CTRL_RPM: + limit->pwm.min = 0; + limit->pwm.max = 0; + ctrl->type = CTRL_RPM; + break; + default: + return -EINVAL; + } + ctrl->mode = MODE_AUTO; + break; + default: + return -EINVAL; + } + + hwm_set_limit(&cfg, limit); + + ctrl->pulse = (pulse && (pulse < 3)) ? pulse : 0; + ctrl->enable = 1; + + ret = hwm_write_fan_config(num, &cfg); + if (ret < 0) + return ret; + + if (alert) + return hwm_core_fan_set_alert_limit(num, alert); + + return 0; +} + +int hwm_core_fan_is_available(int num) +{ + if (WARN_ON(num >= HWM_MAX_FAN)) + return -EINVAL; + + return hwmon->fan.active & (1 << num) && + hwmon->fan.attr[num].did ? 0 : -ENODEV; +} + +static int hwm_core_fan_set_limit(int num, int fan_limit, + struct hwm_sensors_limit *limit) +{ + struct fan_dev_config cfg; + int ret; + + if (WARN_ON(num >= HWM_MAX_FAN)) + return -EINVAL; + + ret = hwm_read_fan_config(num, &cfg); + if (ret < 0) { + pr_err("Failed while reading FAN %s config\n", + hwmon->fan.attr[num].label); + return ret; + } + + switch (fan_limit) { + case LIMIT_PWM: + hwm_set_pwm_limit(&cfg, &limit->pwm); + break; + case LIMIT_RPM: + hwm_set_rpm_limit(&cfg, &limit->rpm); + break; + case LIMIT_TEMP: + hwm_set_temp_limit(&cfg, &limit->temp); + break; + default: + return -EINVAL; + } + + return hwm_write_fan_config(num, &cfg); +} + +int hwm_core_fan_set_rpm_limit(int num, int min, int max) +{ + struct hwm_sensors_limit limit = { + .rpm = { + .min = min, + .max = max, + }, + }; + + return hwm_core_fan_set_limit(num, LIMIT_RPM, &limit); +} + +int hwm_core_fan_set_pwm_limit(int num, int min, int max) +{ + struct hwm_sensors_limit limit = { + .pwm = { + .min = min, + .max = max, + }, + }; + + return hwm_core_fan_set_limit(num, LIMIT_PWM, &limit); +} + +int hwm_core_fan_set_temp_limit(int num, int stop, int min, int max) +{ + struct hwm_sensors_limit limit = { + .temp = { + .stop = stop, + .min = min, + .max = max, + }, + }; + + return hwm_core_fan_set_limit(num, LIMIT_TEMP, &limit); +} + +int hwm_core_adc_get_max_count(void) +{ + return hwmon->adc.num; +} + +int hwm_core_fan_get_max_count(void) +{ + return hwmon->fan.num; +} + +int hwm_core_init(void) +{ + hwmon = imanager_get_hwmon_device(); + if (!hwmon) + return -ENODEV; + + return 0; +} + diff --git a/drivers/hwmon/imanager-hwmon.c b/drivers/hwmon/imanager-hwmon.c new file mode 100644 index 0000000..b8ad562 --- /dev/null +++ b/drivers/hwmon/imanager-hwmon.c @@ -0,0 +1,1058 @@ +/* + * Advantech iManager Hardware Monitoring driver + * Derived from nct6775 driver + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/hwmon-vid.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/mfd/imanager/core.h> +#include <linux/mfd/imanager/hwmon.h> + +struct imanager_hwmon_data { + struct imanager_device_data *idev; + bool valid; /* if set, below values are valid */ + struct hwm_data hwm; + int adc_num; + int fan_num; + unsigned long samples; + unsigned long last_updated; + const struct attribute_group *groups[3]; +}; + +static inline u32 in_from_reg(u16 val) +{ + return clamp_val(DIV_ROUND_CLOSEST(val * SCALE_IN, 1000), 0, 65535); +} + +static inline u16 in_to_reg(u32 val) +{ + return clamp_val(DIV_ROUND_CLOSEST(val * 1000, SCALE_IN), 0, 65535); +} + +static struct imanager_hwmon_data * +imanager_hwmon_update_device(struct device *dev) +{ + struct imanager_hwmon_data *data = dev_get_drvdata(dev); + int i; + + mutex_lock(&data->idev->lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + /* Measured voltages */ + for (i = 0; i < data->adc_num; i++) + hwm_core_adc_get_value(i, &data->hwm.volt[i]); + + /* Measured fan speeds */ + for (i = 0; i < data->fan_num; i++) + hwm_core_fan_get_ctrl(i, &data->hwm.fan[i]); + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->idev->lock); + + return data; +} + +static ssize_t +show_in(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + + return sprintf(buf, "%u\n", in_from_reg(adc->value)); +} + +static ssize_t +show_in_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + + return sprintf(buf, "%u\n", in_from_reg(adc->min)); +} + +static ssize_t +show_in_max(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + + return sprintf(buf, "%u\n", in_from_reg(adc->max)); +} + +static ssize_t +store_in_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->idev->lock); + + adc->min = in_to_reg(val); + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +store_in_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->idev->lock); + + adc->max = in_to_reg(val); + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +show_in_alarm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + int val = 0; + + if (adc->valid) + val = (adc->value < adc->min) || (adc->value > adc->max); + + return sprintf(buf, "%u\n", val); +} + +static ssize_t +show_in_average(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + + if (adc->average) + adc->average = + DIV_ROUND_CLOSEST(adc->average * data->samples + + adc->value, ++data->samples); + else { + adc->average = adc->value; + data->samples = 1; + } + + return sprintf(buf, "%u\n", in_from_reg(adc->average)); +} + +static ssize_t +show_in_lowest(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + + if (!adc->lowest) + adc->lowest = adc->highest = adc->value; + else if (adc->value < adc->lowest) + adc->lowest = adc->value; + + return sprintf(buf, "%u\n", in_from_reg(adc->lowest)); +} + +static ssize_t +show_in_highest(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + + if (!adc->highest) + adc->highest = adc->value; + else if (adc->value > adc->highest) + adc->highest = adc->value; + + return sprintf(buf, "%u\n", in_from_reg(adc->highest)); +} + +static ssize_t +store_in_reset_history(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_voltage *adc = &data->hwm.volt[index]; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->idev->lock); + + if (val == 1) { + adc->lowest = 0; + adc->highest = 0; + } else { + count = -EINVAL; + } + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_smartfan *fan = &data->hwm.fan[index - 1]; + + return sprintf(buf, "%u\n", fan->temp * 1000); +} + +static ssize_t +show_fan_in(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_smartfan *fan = &data->hwm.fan[index - 1]; + + return sprintf(buf, "%u\n", fan->valid ? fan->speed : 0); +} + +static inline int is_alarm(const struct hwm_smartfan *fan) +{ + /* + * Do not set ALARM flag if FAN is in speed cruise mode (3) + * as this mode automatically turns on the FAN + * Set ALARM flag when pwm is set but speed is 0 as this + * could be a defective FAN or no FAN is present + */ + return (!fan->valid || + ((fan->mode == MODE_AUTO) && fan->alarm) || + (fan->speed > fan->limit.rpm.max)); +} + +static ssize_t +show_fan_alarm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_smartfan *fan = &data->hwm.fan[index - 1]; + + return sprintf(buf, "%u\n", fan->valid ? is_alarm(fan) : 0); +} + +static ssize_t +show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_fan_limit *rpm = &data->hwm.fan[index - 1].limit.rpm; + + return sprintf(buf, "%u\n", rpm->min); +} + +static ssize_t +show_fan_max(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_fan_limit *rpm = &data->hwm.fan[index - 1].limit.rpm; + + return sprintf(buf, "%u\n", rpm->max); +} + +static ssize_t +store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_smartfan *fan = &data->hwm.fan[index - 1]; + struct hwm_fan_limit *rpm = &fan->limit.rpm; + unsigned long val = 0; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + /* do not apply value if not in 'fan cruise mode' */ + if (fan->mode != MODE_AUTO) + return -EINVAL; + + mutex_lock(&data->idev->lock); + + hwm_core_fan_set_rpm_limit(index - 1, val, rpm->max); + hwm_core_fan_get_ctrl(index - 1, fan); /* update */ + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +store_fan_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_smartfan *fan = &data->hwm.fan[index - 1]; + struct hwm_fan_limit *rpm = &fan->limit.rpm; + unsigned long val = 0; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + /* do not apply value if not in 'fan cruise mode' */ + if (fan->mode != MODE_AUTO) + return -EINVAL; + + mutex_lock(&data->idev->lock); + + hwm_core_fan_set_rpm_limit(index - 1, rpm->min, val); + hwm_core_fan_get_ctrl(index - 1, fan); + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +show_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + u32 val = DIV_ROUND_CLOSEST(data->hwm.fan[index - 1].pwm * 255, 100); + + return sprintf(buf, "%u\n", val); +} + +static ssize_t +store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_smartfan *fan = &data->hwm.fan[index - 1]; + unsigned long val = 0; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = DIV_ROUND_CLOSEST(val * 100, 255); + + mutex_lock(&data->idev->lock); + + switch (fan->mode) { + case MODE_MANUAL: + hwm_core_fan_set_ctrl(index - 1, MODE_MANUAL, CTRL_PWM, + val, 0, NULL, NULL); + break; + case MODE_AUTO: + if (fan->type == CTRL_RPM) + break; + hwm_core_fan_set_ctrl(index - 1, MODE_AUTO, CTRL_PWM, + val, 0, &fan->limit, &fan->alert); + break; + } + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +show_pwm_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%u\n", data->hwm.fan[index - 1].limit.pwm.min); +} + +static ssize_t +show_pwm_max(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%u\n", data->hwm.fan[index - 1].limit.pwm.max); +} + +static ssize_t +show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index - 1; + struct hwm_smartfan *fan = &data->hwm.fan[nr]; + + if (fan->mode == MODE_OFF) + return -EINVAL; + + return sprintf(buf, "%u\n", fan->mode - 1); +} + +static ssize_t +store_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index - 1; + struct hwm_smartfan *fan = &data->hwm.fan[nr]; + unsigned long mode = 0; + int err; + + err = kstrtoul(buf, 10, &mode); + if (err < 0) + return err; + + if (mode > MODE_AUTO) + return -EINVAL; + + mutex_lock(&data->idev->lock); + + switch (mode) { + case 0: + if (mode != 0) + hwm_core_fan_set_ctrl(nr, MODE_FULL, CTRL_PWM, 100, + fan->pulse, NULL, NULL); + break; + case 1: + if (mode != 1) + hwm_core_fan_set_ctrl(nr, MODE_MANUAL, CTRL_PWM, 0, + fan->pulse, NULL, NULL); + break; + case 2: + if (mode != 2) + hwm_core_fan_set_ctrl(nr, MODE_AUTO, fan->type, 0, + fan->pulse, &fan->limit, NULL); + break; + } + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_smartfan *fan = &data->hwm.fan[index - 1]; + + if (fan->mode == MODE_OFF) + return -EINVAL; + + return sprintf(buf, "%u\n", fan->type == CTRL_PWM ? 1 : 0); +} + +static ssize_t +store_pwm_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index - 1; + struct hwm_smartfan *fan = &data->hwm.fan[nr]; + unsigned long val = 0; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (fan->mode != MODE_AUTO) + return -EINVAL; + + mutex_lock(&data->idev->lock); + + hwm_core_fan_set_ctrl(nr, fan->mode, val ? CTRL_RPM : CTRL_PWM, + fan->pwm, fan->pulse, &fan->limit, &fan->alert); + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +show_temp_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index - 1; + int val = data->hwm.fan[nr].limit.temp.min; + + return sprintf(buf, "%d\n", val * 1000); +} + +static ssize_t +show_temp_max(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index - 1; + int val = data->hwm.fan[nr].limit.temp.max; + + return sprintf(buf, "%u\n", val * 1000); +} + +static ssize_t +show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index - 1; + struct hwm_smartfan *fan = &data->hwm.fan[nr]; + struct hwm_fan_temp_limit *temp = &fan->limit.temp; + + return sprintf(buf, "%u\n", (fan->temp && (fan->temp >= temp->max))); +} + +static ssize_t +store_temp_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index - 1; + struct hwm_smartfan *fan = &data->hwm.fan[nr]; + struct hwm_fan_temp_limit *temp = &fan->limit.temp; + long val = 0; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = DIV_ROUND_CLOSEST(val, 1000); + + if (val > 100) + return -EINVAL; + + /* do not apply value if not in 'fan cruise mode' */ + if (fan->mode != MODE_AUTO) + return -EINVAL; + + /* The EC imanager provides three different temperature limit values + * (stop, min, and max) where stop indicates a minimum temp value + * (threshold) from which the FAN will turn off. We are setting + * temp_stop to the same value as temp_min. + */ + + mutex_lock(&data->idev->lock); + + hwm_core_fan_set_temp_limit(nr, val, val, temp->max); + hwm_core_fan_get_ctrl(nr, fan); + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +store_temp_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index - 1; + struct hwm_smartfan *fan = &data->hwm.fan[nr]; + struct hwm_fan_temp_limit *temp = &fan->limit.temp; + long val = 0; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = DIV_ROUND_CLOSEST(val, 1000); + + if (val > 100) + return -EINVAL; + + /* do not apply value if not in 'fan cruise mode' */ + if (fan->mode != MODE_AUTO) + return -EINVAL; + + mutex_lock(&data->idev->lock); + + hwm_core_fan_set_temp_limit(nr, temp->stop, temp->min, val); + hwm_core_fan_get_ctrl(nr, fan); + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +store_pwm_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_fan_limit *pwm = &data->hwm.fan[index - 1].limit.pwm; + long val = 0; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = DIV_ROUND_CLOSEST(val * 100, 255); + + if (val > 100) + return -EINVAL; + + mutex_lock(&data->idev->lock); + + hwm_core_fan_set_pwm_limit(index - 1, val, pwm->max); + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +store_pwm_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + struct hwm_fan_limit *pwm = &data->hwm.fan[index - 1].limit.pwm; + long val = 0; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = DIV_ROUND_CLOSEST(val * 100, 255); + + if (val > 100) + return -EINVAL; + + mutex_lock(&data->idev->lock); + + hwm_core_fan_set_pwm_limit(index - 1, pwm->min, val); + + mutex_unlock(&data->idev->lock); + + return count; +} + +static ssize_t +show_in_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%s\n", hwm_core_adc_get_label(index)); +} + +static ssize_t +show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%s\n", hwm_core_fan_get_temp_label(index - 1)); +} + +static ssize_t +show_fan_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%s\n", hwm_core_fan_get_label(index - 1)); +} + +/* + * Sysfs callback functions + */ + +static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_in_label, NULL, 0); +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0); +static SENSOR_DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, + store_in_min, 0); +static SENSOR_DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, + store_in_max, 0); +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_in_alarm, NULL, 0); + +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_in_label, NULL, 1); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); +static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, + store_in_min, 1); +static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, + store_in_max, 1); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_in_alarm, NULL, 1); + +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_in_label, NULL, 2); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2); +static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, + store_in_min, 2); +static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, + store_in_max, 2); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_in_alarm, NULL, 2); + +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, show_temp_min, + store_temp_min, 1); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 1); + +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, show_temp_min, + store_temp_min, 2); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 2); +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 2); + +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 3); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR, show_temp_min, + store_temp_min, 3); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 3); +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 3); + +static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, show_fan_label, NULL, 1); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_in, NULL, 1); +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 1); +static SENSOR_DEVICE_ATTR(fan1_max, S_IWUSR | S_IRUGO, show_fan_max, + store_fan_max, 1); +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 1); + +static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, show_fan_label, NULL, 2); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_in, NULL, 2); +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 2); +static SENSOR_DEVICE_ATTR(fan2_max, S_IWUSR | S_IRUGO, show_fan_max, + store_fan_max, 2); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 2); + +static SENSOR_DEVICE_ATTR(fan3_label, S_IRUGO, show_fan_label, NULL, 3); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan_in, NULL, 3); +static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 3); +static SENSOR_DEVICE_ATTR(fan3_max, S_IWUSR | S_IRUGO, show_fan_max, + store_fan_max, 3); +static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 3); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm1_min, S_IWUSR | S_IRUGO, show_pwm_min, + store_pwm_min, 1); +static SENSOR_DEVICE_ATTR(pwm1_max, S_IWUSR | S_IRUGO, show_pwm_max, + store_pwm_max, 1); +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 1); +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 1); + +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2); +static SENSOR_DEVICE_ATTR(pwm2_min, S_IWUSR | S_IRUGO, show_pwm_min, + store_pwm_min, 2); +static SENSOR_DEVICE_ATTR(pwm2_max, S_IWUSR | S_IRUGO, show_pwm_max, + store_pwm_max, 2); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 2); +static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 2); + +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3); +static SENSOR_DEVICE_ATTR(pwm3_min, S_IWUSR | S_IRUGO, show_pwm_min, + store_pwm_min, 3); +static SENSOR_DEVICE_ATTR(pwm3_max, S_IWUSR | S_IRUGO, show_pwm_max, + store_pwm_max, 3); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 3); +static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 3); + +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, show_in, NULL, 4); +static SENSOR_DEVICE_ATTR(curr1_min, S_IWUSR | S_IRUGO, show_in_min, + store_in_min, 4); +static SENSOR_DEVICE_ATTR(curr1_max, S_IWUSR | S_IRUGO, show_in_max, + store_in_max, 4); +static SENSOR_DEVICE_ATTR(curr1_alarm, S_IRUGO, show_in_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(curr1_average, S_IRUGO, show_in_average, NULL, 4); +static SENSOR_DEVICE_ATTR(curr1_lowest, S_IRUGO, show_in_lowest, NULL, 4); +static SENSOR_DEVICE_ATTR(curr1_highest, S_IRUGO, show_in_highest, NULL, 4); +static SENSOR_DEVICE_ATTR(curr1_reset_history, S_IWUSR, NULL, + store_in_reset_history, 4); + +static SENSOR_DEVICE_ATTR(cpu0_vid, S_IRUGO, show_in, NULL, 3); + +static struct attribute *imanager_in_attributes[] = { + &sensor_dev_attr_in0_label.dev_attr.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + + NULL +}; + +static umode_t +imanager_in_is_visible(struct kobject *kobj, struct attribute *attr, int index) +{ + int max_count = hwm_core_adc_get_max_count(); + + if (max_count >= 3) + return attr->mode; + + return 0; +} + +static const struct attribute_group imanager_group_in = { + .attrs = imanager_in_attributes, + .is_visible = imanager_in_is_visible, +}; + +static struct attribute *imanager_other_attributes[] = { + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_min.dev_attr.attr, + &sensor_dev_attr_curr1_max.dev_attr.attr, + &sensor_dev_attr_curr1_alarm.dev_attr.attr, + &sensor_dev_attr_curr1_average.dev_attr.attr, + &sensor_dev_attr_curr1_lowest.dev_attr.attr, + &sensor_dev_attr_curr1_highest.dev_attr.attr, + &sensor_dev_attr_curr1_reset_history.dev_attr.attr, + + &sensor_dev_attr_cpu0_vid.dev_attr.attr, + + NULL +}; + +static umode_t +imanager_other_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + int max_count = hwm_core_adc_get_max_count(); + + /* + * There are either 3 or 5 VINs + * vin3 is current monitoring + * vin4 is CPU VID + */ + if (max_count > 3) + return attr->mode; + + return 0; +} + +static const struct attribute_group imanager_group_other = { + .attrs = imanager_other_attributes, + .is_visible = imanager_other_is_visible, +}; + +static struct attribute *imanager_fan_attributes[] = { + &sensor_dev_attr_fan1_label.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_max.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + + &sensor_dev_attr_fan2_label.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_max.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + + &sensor_dev_attr_fan3_label.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan3_max.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, + + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_min.dev_attr.attr, + &sensor_dev_attr_pwm1_max.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_mode.dev_attr.attr, + + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_min.dev_attr.attr, + &sensor_dev_attr_pwm2_max.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_mode.dev_attr.attr, + + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm3_min.dev_attr.attr, + &sensor_dev_attr_pwm3_max.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_mode.dev_attr.attr, + + NULL +}; + +static umode_t +imanager_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index) +{ + int err; + + if ((index >= 0) && (index <= 14)) { /* fan */ + err = hwm_core_fan_is_available(index / 5); + if (err < 0) + return 0; + } else if ((index >= 15) && (index <= 29)) { /* temp */ + err = hwm_core_fan_is_available((index - 15) / 5); + if (err < 0) + return 0; + } else if ((index >= 30) && (index <= 34)) { /* pwm */ + err = hwm_core_fan_is_available((index - 30) / 5); + if (err < 0) + return 0; + } + + return attr->mode; +} + +static const struct attribute_group imanager_group_fan = { + .attrs = imanager_fan_attributes, + .is_visible = imanager_fan_is_visible, +}; + +/* + * Module stuff + */ +static int imanager_hwmon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imanager_device_data *idev = dev_get_drvdata(dev->parent); + struct imanager_hwmon_data *data; + struct device *hwmon_dev; + int err, i, num_attr_groups = 0; + + if (!idev) { + dev_err(dev, "Invalid platform data\n"); + return -EINVAL; + } + + err = hwm_core_init(); + if (err) { + dev_err(dev, "Hwmon core init failed\n"); + return -EIO; + } + + data = devm_kzalloc(dev, sizeof(struct imanager_hwmon_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->idev = idev; + platform_set_drvdata(pdev, data); + + data->adc_num = hwm_core_adc_get_max_count(); + data->fan_num = hwm_core_fan_get_max_count(); + + for (i = 0; i < data->fan_num; i++) { + /* set active fan to automatic speed control */ + hwm_core_fan_set_ctrl(i, MODE_AUTO, CTRL_RPM, 0, 0, + NULL, NULL); + hwm_core_fan_get_ctrl(i, &data->hwm.fan[i]); + } + + data->groups[num_attr_groups++] = &imanager_group_in; + + if (data->adc_num > 3) + data->groups[num_attr_groups++] = &imanager_group_other; + + if (data->fan_num) + data->groups[num_attr_groups++] = &imanager_group_fan; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + "imanager_hwmon", data, data->groups); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static struct platform_driver imanager_hwmon_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "imanager_hwmon", + }, + .probe = imanager_hwmon_probe, +}; + +module_platform_driver(imanager_hwmon_driver); + +MODULE_DESCRIPTION("Advantech iManager HWmon Driver"); +MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imanager_hwmon"); diff --git a/include/linux/mfd/imanager/hwmon.h b/include/linux/mfd/imanager/hwmon.h new file mode 100644 index 0000000..2a7e191 --- /dev/null +++ b/include/linux/mfd/imanager/hwmon.h @@ -0,0 +1,120 @@ +/* + * Advantech iManager Hardware Monitoring core + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __HWMON_H__ +#define __HWMON_H__ + +#include <linux/types.h> + +#define HWM_MAX_ADC 5 +#define HWM_MAX_FAN 3 + +/* Voltage computation (10-bit ADC, 0..3V input) */ +#define SCALE_IN 2933 /* (3000mV / (2^10 - 1)) * 1000 */ + +/* Default Voltage Sensors */ +struct hwm_voltage { + bool valid; /* if set, below values are valid */ + + int value; + int min; + int max; + int average; + int lowest; + int highest; + +}; + +struct hwm_fan_temp_limit { + int stop; + int min; + int max; +}; + +struct hwm_fan_limit { + int min; + int max; +}; + +struct hwm_fan_alert { + int min; + int max; + int min_alarm; + int max_alarm; +}; + +struct hwm_sensors_limit { + struct hwm_fan_temp_limit temp; + struct hwm_fan_limit pwm; + struct hwm_fan_limit rpm; +}; + +struct hwm_smartfan { + bool valid; /* if set, below values are valid */ + + int mode; + int type; + int pwm; + int speed; + int pulse; + int alarm; + int temp; + + struct hwm_sensors_limit limit; + struct hwm_fan_alert alert; +}; + +struct hwm_data { + struct hwm_voltage volt[HWM_MAX_ADC]; + struct hwm_smartfan fan[HWM_MAX_FAN]; +}; + +enum fan_unit { + FAN_CPU, + FAN_SYS1, + FAN_SYS2, +}; + +enum fan_ctrl_type { + CTRL_PWM, + CTRL_RPM, +}; + +enum fan_mode { + MODE_OFF, + MODE_FULL, + MODE_MANUAL, + MODE_AUTO, +}; + +int hwm_core_init(void); + +int hwm_core_adc_is_available(int num); +int hwm_core_adc_get_max_count(void); +int hwm_core_adc_get_value(int num, struct hwm_voltage *volt); +const char *hwm_core_adc_get_label(int num); + +int hwm_core_fan_is_available(int num); +int hwm_core_fan_get_max_count(void); +int hwm_core_fan_get_ctrl(int num, struct hwm_smartfan *fan); +int hwm_core_fan_set_ctrl(int num, int fmode, int ftype, int pwm, int pulse, + struct hwm_sensors_limit *limit, + struct hwm_fan_alert *alert); + +int hwm_core_fan_set_rpm_limit(int num, int min, int max); +int hwm_core_fan_set_pwm_limit(int num, int min, int max); +int hwm_core_fan_set_temp_limit(int num, int stop, int min, int max); + +const char *hwm_core_fan_get_label(int num); +const char *hwm_core_fan_get_temp_label(int num); + +#endif -- 2.6.4 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2016-01-10 9:11 ` richard.dorsch @ 2016-01-10 10:25 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-10 10:25 UTC (permalink / raw) Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/hwmon/imanager-hwmon.c:1047:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-hwmon.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/hwmon/imanager-hwmon.c +++ b/drivers/hwmon/imanager-hwmon.c @@ -1044,7 +1044,6 @@ static int imanager_hwmon_probe(struct p static struct platform_driver imanager_hwmon_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_hwmon", }, .probe = imanager_hwmon_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-01-10 10:25 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-10 10:25 UTC (permalink / raw) To: richard.dorsch Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/hwmon/imanager-hwmon.c:1047:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-hwmon.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/hwmon/imanager-hwmon.c +++ b/drivers/hwmon/imanager-hwmon.c @@ -1044,7 +1044,6 @@ static int imanager_hwmon_probe(struct p static struct platform_driver imanager_hwmon_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_hwmon", }, .probe = imanager_hwmon_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v2 4/6] Add Advantech iManager I2C driver
2016-01-10 9:11 ` richard.dorsch
@ 2016-01-10 10:34 kbuild test robot
2016-01-10 9:11 ` richard.dorsch
0 siblings, 1 reply; 40+ messages in thread
From: kbuild test robot @ 2016-01-10 10:34 UTC (permalink / raw)
Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog,
linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga,
Richard Vidal-Dorsch
Hi Richard,
[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.4-rc8 next-20160108]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/richard-dorsch-gmail-com/Add-Advantech-iManager-EC-driver-set/20160110-171635
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
coccinelle warnings: (new ones prefixed by >>)
>> drivers/i2c/busses/imanager-i2c.c:228:3-8: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v2 4/6] Add Advantech iManager I2C driver @ 2016-01-10 9:11 ` richard.dorsch 2016-01-10 10:34 ` kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: richard.dorsch @ 2016-01-10 9:11 UTC (permalink / raw) To: linux-kernel Cc: lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch From: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Richard Vidal-Dorsch <richard.dorsch@gmail.com> --- Documentation/i2c/busses/i2c-imanager | 48 ++++ drivers/i2c/busses/Kconfig | 11 + drivers/i2c/busses/Makefile | 2 + drivers/i2c/busses/imanager-ec-i2c.c | 466 ++++++++++++++++++++++++++++++++++ drivers/i2c/busses/imanager-i2c.c | 240 +++++++++++++++++ include/linux/mfd/imanager/i2c.h | 55 ++++ 6 files changed, 822 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-imanager create mode 100644 drivers/i2c/busses/imanager-ec-i2c.c create mode 100644 drivers/i2c/busses/imanager-i2c.c create mode 100644 include/linux/mfd/imanager/i2c.h diff --git a/Documentation/i2c/busses/i2c-imanager b/Documentation/i2c/busses/i2c-imanager new file mode 100644 index 0000000..d149fbf --- /dev/null +++ b/Documentation/i2c/busses/i2c-imanager @@ -0,0 +1,48 @@ +Kernel driver imanager_i2c +========================== + +This platform driver provides support for iManager I2C/SMBus. + +This driver depends on imanager (mfd). + +Module Parameters +----------------- + +* bus_frequency (unsigned short) +Set desired bus frequency. Valid values (kHz) are: + 50 Slow + 100 Standard (default) + 400 Fast + + +Description +----------- + +The Advantech iManager provides up to four SMBus controllers. One of them +is configured for I2C compatibility. + + +Process Call Support +-------------------- + +Not supported. + + +I2C Block Read Support +---------------------- + +I2C block read is supported. + + +SMBus 2.0 Support +----------------- + +Several SMBus 2.0 features are supported. +No PEC support. + + +Interrupt Support +----------------- + +No interrupt support available + diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 7b0aa82..4c401a4 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -42,6 +42,17 @@ config I2C_ALI15X3 This driver can also be built as a module. If so, the module will be called i2c-ali15x3. +config I2C_IMANAGER + tristate "Advantech iManager I2C Interface" + depends on MFD_IMANAGER + help + This enables support for Advantech iManager I2C of some + Advantech SOM, MIO, AIMB, and PCM modules/boards. + Requires mfd-core and imanager-core to function properly. + + This driver can also be built as a module. If so, the module + will be called i2c-imanager. + config I2C_AMD756 tristate "AMD 756/766/768/8111 and nVidia nForce" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 37f2819..da76404 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_I801) += i2c-i801.o obj-$(CONFIG_I2C_ISCH) += i2c-isch.o obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o +i2c-imanager-objs := imanager-i2c.o imanager-ec-i2c.o +obj-$(CONFIG_I2C_IMANAGER) += i2c-imanager.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o diff --git a/drivers/i2c/busses/imanager-ec-i2c.c b/drivers/i2c/busses/imanager-ec-i2c.c new file mode 100644 index 0000000..f8839dc --- /dev/null +++ b/drivers/i2c/busses/imanager-ec-i2c.c @@ -0,0 +1,466 @@ +/* + * Advantech iManager I2C bus core + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/bug.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/byteorder/generic.h> +#include <linux/mfd/imanager/ec.h> +#include <linux/mfd/imanager/i2c.h> + +#define I2C_SMBUS_BLOCK_SIZE 32 + +#define EVAL_WR_SIZE(x) \ + (x < I2C_SMBUS_BLOCK_SIZE ? x : I2C_SMBUS_BLOCK_SIZE - 1) +#define EVAL_RD_SIZE(x) \ + (x && (x <= I2C_SMBUS_BLOCK_SIZE) ? x : I2C_SMBUS_BLOCK_SIZE) + +#define EC_HWRAM_OFFSET_STATUS 0UL + +#define I2C_ERR_PROTO 0x19UL +#define I2C_ERR_TIMEOUT 0x18UL +#define I2C_ERR_ACCESS 0x17UL +#define I2C_ERR_UNKNOWN 0x13UL +#define I2C_ERR_ADDR_NACK 0x10UL + +#define EC_SMB_DID(N) (0x28 + N) + +struct ec_i2c_status { + u32 error : 7; + u32 complete : 1; +}; + +static const struct imanager_i2c_device *i2c; + +static int i2c_core_eval_status(u8 _status) +{ + struct ec_i2c_status *status = (struct ec_i2c_status *)&_status; + int err = 0; + + switch (status->error) { + case 0: + break; + case I2C_ERR_ADDR_NACK: + err = -ENODEV; + break; + case I2C_ERR_ACCESS: + case I2C_ERR_UNKNOWN: + err = -EAGAIN; + break; + case I2C_ERR_TIMEOUT: + err = -ETIME; + break; + case I2C_ERR_PROTO: + err = -EPROTO; + break; + default: + pr_err("Undefined status code 0x%02X\n", status->error); + err = -EIO; + break; + } + + return err; +} + +static inline int i2c_send_message(u8 cmd, u8 param, struct ec_message *msg) +{ + int err; + + err = imanager_msg_write(cmd, param, msg); + if (err) + return i2c_core_eval_status(err); + + return 0; +} + +static int i2c_core_blk_wr_rw_combined(u8 proto, struct ec_message *msg) +{ + int err; + + if (WARN_ON(!msg)) + return -EINVAL; + + err = imanager_wait_proc_complete(EC_HWRAM_OFFSET_STATUS, 0); + if (err) + return err; + + err = i2c_send_message(proto, i2c->i2coem->did, msg); + if (err) + return err; + + if (msg->rlen) { + if (msg->rlen == 1) + return msg->u.data[0]; + else if (msg->rlen == 2) + return (msg->u.data[1] << 8) | msg->u.data[0]; + else + return msg->rlen; + } + + return 0; +} + +/* Write-Read and Read-Write wrappers */ +static inline int i2c_core_wr_combined(struct ec_message *msg) +{ + return i2c_core_blk_wr_rw_combined(EC_CMD_I2C_WR, msg); +} + +static inline int i2c_core_rw_combined(struct ec_message *msg) +{ + return i2c_core_blk_wr_rw_combined(EC_CMD_I2C_RW, msg); +} + +/* + * iManager I2C core API + */ +int i2c_core_write_quick(u16 addr) +{ + struct ec_message msg = { + .rlen = 0, + .wlen = sizeof(struct ec_message_header), + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = 0, + .wlen = 1, + }, + }, + }; + + return i2c_core_wr_combined(&msg); +} + +int i2c_core_read_byte(u16 addr) +{ + struct ec_message msg = { + .rlen = 1, + .wlen = sizeof(struct ec_message_header), + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = 1, + .wlen = 0, + }, + }, + }; + + return i2c_core_rw_combined(&msg); +} + +int i2c_core_write_byte(u16 addr, u8 cmd) +{ + struct ec_message msg = { + .rlen = 1, + .wlen = sizeof(struct ec_message_header), + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = 1, + .wlen = 1, + .cmd = cmd, + }, + }, + }; + + return i2c_core_wr_combined(&msg); +} + +int i2c_core_read_byte_data(u16 addr, u8 cmd) +{ + struct ec_message msg = { + .rlen = 1, + .wlen = sizeof(struct ec_message_header), + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = 1, + .wlen = 1, + .cmd = cmd, + }, + }, + }; + + return i2c_core_wr_combined(&msg); +} + +int i2c_core_write_byte_data(u16 addr, u8 cmd, u8 value) +{ + struct ec_message msg = { + .rlen = 1, + .wlen = sizeof(struct ec_message_header) + 1, + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = 0, + .wlen = 2, + .cmd = cmd, + }, + .smb.data[0] = value, + }, + }; + + return i2c_core_wr_combined(&msg); +} + +int i2c_core_read_word_data(u16 addr, u8 cmd) +{ + struct ec_message msg = { + .rlen = 2, + .wlen = sizeof(struct ec_message_header), + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = 2, + .wlen = 1, + .cmd = cmd, + }, + }, + }; + + return i2c_core_wr_combined(&msg); +} + +int i2c_core_write_word_data(u16 addr, u8 cmd, u16 value) +{ + struct ec_message msg = { + .rlen = 1, + .wlen = sizeof(struct ec_message_header) + 2, + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = 0, + .wlen = 3, + .cmd = cmd, + }, + .smb.data[0] = LOBYTE16(value), + .smb.data[1] = HIBYTE16(value), + }, + }; + + return i2c_core_wr_combined(&msg); +} + +int i2c_core_write_block_data(u16 addr, u8 cmd, u8 *buf) +{ + int i; + struct ec_message msg = { + .rlen = 1, + .wlen = sizeof(struct ec_message_header) + + EVAL_WR_SIZE(buf[0]), + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = 0, + .wlen = 1 + EVAL_WR_SIZE(buf[0]), + .cmd = cmd, + }, + }, + }; + + if ((buf[0] == 0) || (buf[0] >= I2C_MAX_WRITE_BYTES)) { + pr_err("Invalid I2C write length %d\n", buf[0]); + return -EINVAL; + } + + for (i = 0; i < EVAL_WR_SIZE(buf[0]); i++) + msg.u.data[i + sizeof(struct ec_message_header)] = buf[i + 1]; + + return i2c_core_wr_combined(&msg); +} + +int i2c_core_read_block_data(u16 addr, u8 cmd, u8 *buf) +{ + int i; + int ret; + struct ec_message msg = { + .rlen = EVAL_RD_SIZE(buf[0]), + .wlen = sizeof(struct ec_message_header), + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = EVAL_RD_SIZE(buf[0]), + .wlen = 1, + .cmd = cmd, + }, + }, + }; + + /* + * If buf[0] == 0 EC will read I2C_MAX_READ_BYTES + */ + ret = i2c_core_wr_combined(&msg); + if (ret < 0) { + pr_err("I2C transaction failed\n"); + return ret; + } + + buf[0] = ret; + for (i = 0; i < ret; i++) + buf[i + 1] = msg.u.data[i]; + + return 0; +} + +int i2c_core_read_i2c_block_data(u16 addr, u8 cmd, u8 *buf) +{ + int i; + int ret; + struct ec_message msg = { + .rlen = EVAL_RD_SIZE(buf[0]), + .wlen = sizeof(struct ec_message_header), + .u = { + .smb.hdr = { + .addr_low = LOADDR16(addr), + .addr_high = HIADDR16(addr), + .rlen = EVAL_RD_SIZE(buf[0]), + .wlen = 1, + .cmd = cmd, + }, + }, + }; + + if ((buf[0] == 0) || (buf[0] > I2C_MAX_READ_BYTES)) { + pr_err("Invalid I2C read length\n"); + return -EINVAL; + } + + ret = i2c_core_wr_combined(&msg); + if (ret < 0) { + pr_err("I2C transaction failed\n"); + return ret; + } + + buf[0] = ret; + for (i = 0; i < ret; i++) + buf[i + 1] = msg.u.data[i]; + + return 0; +} + +int i2c_core_write_i2c_block_data(u16 addr, u8 cmd, u8 *buf) +{ + if (WARN_ON(!buf)) + return -EINVAL; + + return i2c_core_write_block_data(addr, cmd, buf); +} + +int i2c_core_smb_get_freq(u32 bus_id) +{ + int ret = 0, f; + int freq_id, freq; + + if (WARN_ON(bus_id > I2C_OEM_1)) + return -EINVAL; + + switch (i2c->ecdev->id) { + case IT8518: + case IT8528: + ret = imanager_read_word(EC_CMD_SMB_FREQ_RD, + EC_SMB_DID(bus_id)); + if (ret < 0) { + pr_err("Failed to get bus frequency\n"); + return ret; + } + + freq_id = HIBYTE16(ret); + f = LOBYTE16(ret); + switch (freq_id) { + case 0: + freq = f; + break; + case 1: + freq = 50; + break; + case 2: + freq = 100; + break; + case 3: + freq = 400; + break; + default: + return -EINVAL; + } + break; + default: + pr_err("EC version not supported!\n"); + return -ENODEV; + } + + return freq; +} + +int i2c_core_smb_set_freq(u32 bus_id, u32 freq) +{ + int err; + u16 val; + + if (WARN_ON(bus_id > I2C_OEM_1)) + return -EINVAL; + + switch (i2c->ecdev->id) { + case IT8518: + case IT8528: + switch (freq) { + case 50: + val = 0x0100; + break; + case 100: + val = 0x0200; + break; + case 400: + val = 0x0300; + break; + default: + if (freq < 50) + val = freq; + else + return -EINVAL; + } + + err = imanager_write_word(EC_CMD_SMB_FREQ_WR, + EC_SMB_DID(bus_id), val); + if (err) { + pr_err("Failed to set I2C bus frequency\n"); + return err; + } + break; + default: + pr_err("EC version not supported!\n"); + return -ENODEV; + } + + return 0; +} + +int i2c_core_init(void) +{ + i2c = imanager_get_i2c_device(); + if (!i2c) + return -ENODEV; + + return 0; +} + diff --git a/drivers/i2c/busses/imanager-i2c.c b/drivers/i2c/busses/imanager-i2c.c new file mode 100644 index 0000000..bbfd453 --- /dev/null +++ b/drivers/i2c/busses/imanager-i2c.c @@ -0,0 +1,240 @@ +/* + * Advantech iManager I2C bus driver + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/mfd/imanager/core.h> +#include <linux/mfd/imanager/i2c.h> + +static uint bus_frequency = 100; +module_param(bus_frequency, uint, 0); +MODULE_PARM_DESC(bus_frequency, + "I2C bus frequency [50, 100, 400]kHz (defaults to 100kHz)"); + +struct imanager_i2c_data { + struct imanager_device_data *idev; + struct i2c_adapter adapter; +}; + +static int imanager_smb_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *smb_data) +{ + struct imanager_i2c_data *data = i2c_get_adapdata(adap); + struct device *dev = data->adapter.dev.parent; + int ret = 0; + int val = 0; + + if (!data) + return -ENODEV; + + addr <<= 1; + + mutex_lock(&data->idev->lock); + + switch (size) { + case I2C_SMBUS_QUICK: + ret = i2c_core_write_quick(addr); + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) /* NOT tested */ + val = i2c_core_write_byte(addr, command); + else + val = i2c_core_read_byte(addr); + + if (val < 0) + ret = val; + else + smb_data->byte = val; + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) + val = i2c_core_write_byte_data(addr, command, + smb_data->byte); + else + val = i2c_core_read_byte_data(addr, command); + + if (val < 0) + ret = val; + else + smb_data->byte = val; + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_WRITE) + val = i2c_core_write_word_data(addr, command, + smb_data->word); + else + val = i2c_core_read_word_data(addr, command); + + if (val < 0) + ret = val; + else + smb_data->word = val; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_WRITE) + ret = i2c_core_write_block_data(addr, command, + smb_data->block); + else + ret = i2c_core_read_block_data(addr, command, + smb_data->block); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_WRITE) + ret = i2c_core_write_i2c_block_data(addr, command, + smb_data->block); + else + ret = i2c_core_read_i2c_block_data(addr, command, + smb_data->block); + break; + default: + dev_err(dev, "Unsupported SMB transaction %d\n", size); + ret = -EOPNOTSUPP; + } + + mutex_unlock(&data->idev->lock); + + return ret; +} + +static int imanager_i2c_access(struct i2c_adapter *adap, struct i2c_msg *msg, + int num) +{ + struct imanager_i2c_data *data = i2c_get_adapdata(adap); + struct device *dev = data->adapter.dev.parent; + + /* + * To be implemented + */ + + dev_info(dev, "i2c_access() is not yet implemented. msg=%p, num=%d\n", + msg, num); + + return 0; +} + +static u32 imanager_smb_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_I2C; +} + +static const struct i2c_algorithm imanager_algorithm = { + .smbus_xfer = imanager_smb_access, + .master_xfer = imanager_i2c_access, + .functionality = imanager_smb_i2c_func, +}; + +static int imanager_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imanager_device_data *idev = dev_get_drvdata(dev->parent); + struct imanager_i2c_data *i2c; + int ret; + + if (!idev) { + dev_err(dev, "Invalid platform data\n"); + return -EINVAL; + } + + ret = i2c_core_init(); + if (ret) { + dev_err(dev, "Failed to initialize I2C core\n"); + return -EIO; + } + + if (bus_frequency > 100) + bus_frequency = 400; + else if (bus_frequency < 50) + bus_frequency = 50; + else + bus_frequency = 100; + + ret = i2c_core_smb_set_freq(I2C_OEM_1, bus_frequency); + if (ret < 0) { + dev_err(dev, "Failed to set I2C bus frequency to %d kHz\n", + bus_frequency); + return ret; + } + + ret = i2c_core_smb_get_freq(I2C_OEM_1); + if (ret < 0) { + dev_err(dev, "Failed to get I2C bus frequency\n"); + return ret; + } + bus_frequency = ret; + dev_info(dev, "Bus frequency: %d kHz\n", bus_frequency); + + i2c = devm_kzalloc(dev, sizeof(struct imanager_i2c_data), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + platform_set_drvdata(pdev, i2c); + i2c_set_adapdata(&i2c->adapter, i2c); + + i2c->idev = idev; + + i2c->adapter.owner = THIS_MODULE; + i2c->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + i2c->adapter.algo = &imanager_algorithm; + + /* set up the sysfs linkage to our parent device */ + i2c->adapter.dev.parent = dev; + + /* Retry up to 3 times on lost arbitration */ + i2c->adapter.retries = 3; + + snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), + "iManager I2C driver"); + + ret = i2c_add_adapter(&i2c->adapter); + if (ret) { + dev_err(dev, "Failed to add SMBus adapter\n"); + return ret; + } + + return 0; +} + +static int imanager_i2c_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imanager_i2c_data *i2c = dev_get_drvdata(dev); + + i2c_del_adapter(&i2c->adapter); + i2c_set_adapdata(&i2c->adapter, NULL); + + return 0; +} + +static struct platform_driver imanager_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "imanager_i2c", + }, + .probe = imanager_i2c_probe, + .remove = imanager_i2c_remove, +}; + +module_platform_driver(imanager_i2c_driver); + +MODULE_DESCRIPTION("Advantech iManager I2C Driver"); +MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imanager_i2c"); diff --git a/include/linux/mfd/imanager/i2c.h b/include/linux/mfd/imanager/i2c.h new file mode 100644 index 0000000..a8ef6c2 --- /dev/null +++ b/include/linux/mfd/imanager/i2c.h @@ -0,0 +1,55 @@ +/* + * Advantech iManager I2C bus core + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __I2C_H__ +#define __I2C_H__ + +#include <linux/types.h> + +#define I2C_MAX_READ_BYTES 32 +#define I2C_MAX_WRITE_BYTES 32 + +/* Only for setting SMBus frequency */ +enum smb_bus_id { + SMB_OEM_0, + SMB_OEM_1, + SMB_OEM_2, + SMB_EEPROM, + SMB_TH_0, + SMB_TH_1, + SMB_SECURITY_EEPROM, + I2C_OEM_1, +}; + +int i2c_core_init(void); + +int i2c_core_write_quick(u16 addr); + +int i2c_core_read_byte(u16 addr); +int i2c_core_write_byte(u16 addr, u8 cmd); + +int i2c_core_write_byte_data(u16 addr, u8 cmd, u8 value); +int i2c_core_read_byte_data(u16 addr, u8 cmd); + +int i2c_core_write_word_data(u16 addr, u8 cmd, u16 value); +int i2c_core_read_word_data(u16 addr, u8 cmd); + +int i2c_core_write_block_data(u16 addr, u8 cmd, u8 *buf); +int i2c_core_read_block_data(u16 addr, u8 cmd, u8 *buf); + +int i2c_core_write_i2c_block_data(u16 addr, u8 cmd, u8 *buf); +int i2c_core_read_i2c_block_data(u16 addr, u8 cmd, u8 *buf); + +int i2c_core_smb_get_freq(u32 bus_id); +int i2c_core_smb_set_freq(u32 bus_id, u32 freq); + +#endif -- 2.6.4 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2016-01-10 9:11 ` richard.dorsch @ 2016-01-10 10:34 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-10 10:34 UTC (permalink / raw) Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/i2c/busses/imanager-i2c.c:228:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-i2c.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/i2c/busses/imanager-i2c.c +++ b/drivers/i2c/busses/imanager-i2c.c @@ -225,7 +225,6 @@ static int imanager_i2c_remove(struct pl static struct platform_driver imanager_i2c_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_i2c", }, .probe = imanager_i2c_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-01-10 10:34 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-10 10:34 UTC (permalink / raw) To: richard.dorsch Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/i2c/busses/imanager-i2c.c:228:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-i2c.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/i2c/busses/imanager-i2c.c +++ b/drivers/i2c/busses/imanager-i2c.c @@ -225,7 +225,6 @@ static int imanager_i2c_remove(struct pl static struct platform_driver imanager_i2c_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_i2c", }, .probe = imanager_i2c_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v2 5/6] Add Advantech iManager Backlight driver
@ 2016-01-10 10:44 kbuild test robot
[not found] ` <1452417098-28667-1-git-send-email-richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
0 siblings, 1 reply; 40+ messages in thread
From: kbuild test robot @ 2016-01-10 10:44 UTC (permalink / raw)
Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog,
linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga,
Richard Vidal-Dorsch
Hi Richard,
[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.4-rc8 next-20160108]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/richard-dorsch-gmail-com/Add-Advantech-iManager-EC-driver-set/20160110-171635
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
coccinelle warnings: (new ones prefixed by >>)
>> drivers/video/backlight/imanager-bl.c:187:3-8: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 40+ messages in thread
[parent not found: <1452417098-28667-1-git-send-email-richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-01-10 10:44 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-10 10:44 UTC (permalink / raw) Cc: kbuild-all-JC7UmRfGjtg, linux-kernel-u79uwXL29TY76Z2rM5mHXA, lm-sensors-GZX6beZjE8VD60Wz+7aTrA, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-watchdog-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, lee.jones-QSEj5FYQhm4dnm+yROfE0A, jdelvare-IBi9RG/b67k, linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ, jo.sunga-ELdSlb/RfAS1Z/+hSey0Gg, Richard Vidal-Dorsch drivers/video/backlight/imanager-bl.c:187:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> Signed-off-by: Fengguang Wu <fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> --- imanager-bl.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/video/backlight/imanager-bl.c +++ b/drivers/video/backlight/imanager-bl.c @@ -184,7 +184,6 @@ static int imanager_backlight_remove(str static struct platform_driver imanager_backlight_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_backlight", }, .probe = imanager_backlight_probe, -- To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-01-10 10:44 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-01-10 10:44 UTC (permalink / raw) To: richard.dorsch Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch drivers/video/backlight/imanager-bl.c:187:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- imanager-bl.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/video/backlight/imanager-bl.c +++ b/drivers/video/backlight/imanager-bl.c @@ -184,7 +184,6 @@ static int imanager_backlight_remove(str static struct platform_driver imanager_backlight_driver = { .driver = { - .owner = THIS_MODULE, .name = "imanager_backlight", }, .probe = imanager_backlight_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [media 7/7] PCI bridge driver for PT3 & PXQ3PE 2016-02-15 6:08 ` info @ 2016-02-15 7:34 kbuild test robot 2016-02-15 6:08 ` info 0 siblings, 1 reply; 40+ messages in thread From: kbuild test robot @ 2016-02-15 7:34 UTC (permalink / raw) To: info Cc: kbuild-all, linux-media, Буди Романто, AreMa Inc, linux-kernel, crope, m.chehab, mchehab, hdegoede, laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski, peter.senna [-- Attachment #1: Type: text/plain, Size: 3131 bytes --] Hi Буди, [auto build test ERROR on linuxtv-media/master] [cannot apply to v4.5-rc4 next-20160212] [if your patch is applied to the wrong git tree, please drop us a note to help improving the system] url: https://github.com/0day-ci/linux/commits/info-are-ma/Driver-bundle-for-PT3-PX-Q3PE/20160215-141501 base: git://linuxtv.org/media_tree.git master config: i386-allmodconfig (attached as .config) reproduce: # save the attached .config to linux build tree make ARCH=i386 All error/warnings (new ones prefixed by >>): In file included from drivers/media/pci/ptx/pt3_pci.c:19:0: >> drivers/media/pci/ptx/ptx_common.h:24:2: error: unknown type name 'fe_delivery_system_t' fe_delivery_system_t type; ^ -- In file included from drivers/media/pci/ptx/ptx_common.c:9:0: >> drivers/media/pci/ptx/ptx_common.h:24:2: error: unknown type name 'fe_delivery_system_t' fe_delivery_system_t type; ^ drivers/media/pci/ptx/ptx_common.c: In function 'ptx_abort': >> drivers/media/pci/ptx/ptx_common.c:199:6: error: implicit declaration of function 'vzalloc' [-Werror=implicit-function-declaration] s = vzalloc(slen); ^ >> drivers/media/pci/ptx/ptx_common.c:199:4: warning: assignment makes pointer from integer without a cast [-Wint-conversion] s = vzalloc(slen); ^ >> drivers/media/pci/ptx/ptx_common.c:203:3: error: implicit declaration of function 'vfree' [-Werror=implicit-function-declaration] vfree(s); ^ cc1: some warnings being treated as errors -- In file included from drivers/media/pci/ptx/pxq3pe_pci.c:18:0: >> drivers/media/pci/ptx/ptx_common.h:24:2: error: unknown type name 'fe_delivery_system_t' fe_delivery_system_t type; ^ drivers/media/pci/ptx/pxq3pe_pci.c: In function 'pxq3pe_remove': >> drivers/media/pci/ptx/pxq3pe_pci.c:499:3: error: implicit declaration of function 'vfree' [-Werror=implicit-function-declaration] vfree(p->sBuf); ^ drivers/media/pci/ptx/pxq3pe_pci.c: In function 'pxq3pe_probe': >> drivers/media/pci/ptx/pxq3pe_pci.c:552:14: error: implicit declaration of function 'vzalloc' [-Werror=implicit-function-declaration] p->sBuf = vzalloc(p->sBufSize); ^ >> drivers/media/pci/ptx/pxq3pe_pci.c:552:12: warning: assignment makes pointer from integer without a cast [-Wint-conversion] p->sBuf = vzalloc(p->sBufSize); ^ cc1: some warnings being treated as errors coccinelle warnings: (new ones prefixed by >>) >> drivers/media/dvb-frontends/tc90522.c:271:3-8: No need to set .owner here. The core will do it. Please review and possibly fold the followup patch. vim +/fe_delivery_system_t +24 drivers/media/pci/ptx/ptx_common.h 18 enum ePTX { 19 PTX_TS_SYNC = 0x47, 20 PTX_TS_NOT_SYNC = 0x74, 21 }; 22 23 struct ptx_subdev_info { > 24 fe_delivery_system_t type; 25 u8 demod_addr, *demod_name, 26 tuner_addr, *tuner_name; 27 }; --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/octet-stream, Size: 53467 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
* [media 7/7] PCI bridge driver for PT3 & PXQ3PE @ 2016-02-15 6:08 ` info 2016-02-15 7:34 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: info @ 2016-02-15 6:08 UTC (permalink / raw) To: linux-media Cc: Буди Романто, AreMa Inc, linux-kernel, crope, m.chehab, mchehab, hdegoede, laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski, peter.senna From: Буди Романто, AreMa Inc <knightrider@are.ma> Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma> --- drivers/media/pci/Kconfig | 2 +- drivers/media/pci/Makefile | 2 +- drivers/media/pci/ptx/Kconfig | 21 ++ drivers/media/pci/ptx/Makefile | 8 + drivers/media/pci/ptx/pt3_pci.c | 509 +++++++++++++++++++++++++++++++ drivers/media/pci/ptx/ptx_common.c | 215 +++++++++++++ drivers/media/pci/ptx/ptx_common.h | 68 +++++ drivers/media/pci/ptx/pxq3pe_pci.c | 607 +++++++++++++++++++++++++++++++++++++ 8 files changed, 1430 insertions(+), 2 deletions(-) create mode 100644 drivers/media/pci/ptx/Kconfig create mode 100644 drivers/media/pci/ptx/Makefile create mode 100644 drivers/media/pci/ptx/pt3_pci.c create mode 100644 drivers/media/pci/ptx/ptx_common.c create mode 100644 drivers/media/pci/ptx/ptx_common.h create mode 100644 drivers/media/pci/ptx/pxq3pe_pci.c diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 48a611b..9d63ad6 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -44,7 +44,7 @@ source "drivers/media/pci/b2c2/Kconfig" source "drivers/media/pci/pluto2/Kconfig" source "drivers/media/pci/dm1105/Kconfig" source "drivers/media/pci/pt1/Kconfig" -source "drivers/media/pci/pt3/Kconfig" +source "drivers/media/pci/ptx/Kconfig" source "drivers/media/pci/mantis/Kconfig" source "drivers/media/pci/ngene/Kconfig" source "drivers/media/pci/ddbridge/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 5f8aacb..984e37c 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -7,7 +7,7 @@ obj-y += ttpci/ \ pluto2/ \ dm1105/ \ pt1/ \ - pt3/ \ + ptx/ \ mantis/ \ ngene/ \ ddbridge/ \ diff --git a/drivers/media/pci/ptx/Kconfig b/drivers/media/pci/ptx/Kconfig new file mode 100644 index 0000000..792acfe --- /dev/null +++ b/drivers/media/pci/ptx/Kconfig @@ -0,0 +1,21 @@ +config DVB_PT3 + tristate "Earthsoft PT3 cards" + depends on DVB_CORE && PCI && I2C + select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QM1D1C0042 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL301RF if MEDIA_SUBDRV_AUTOSELECT + help + Support for Earthsoft PT3 ISDB-S/T PCIe cards. + + Say Y or M if you own such a device and want to use it. + +config DVB_PXQ3PE + tristate "PLEX PX-Q3PE cards" + depends on DVB_CORE && PCI && I2C + select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QM1D1C0042 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL301RF if MEDIA_SUBDRV_AUTOSELECT + help + Support for PLEX PX-Q3PE ISDB-S/T PCIe cards. + + Say Y or M if you own such a device and want to use it. diff --git a/drivers/media/pci/ptx/Makefile b/drivers/media/pci/ptx/Makefile new file mode 100644 index 0000000..b10ad8a --- /dev/null +++ b/drivers/media/pci/ptx/Makefile @@ -0,0 +1,8 @@ +pt3-objs := pt3_pci.o ptx_common.o +pxq3pe-objs := pxq3pe_pci.o ptx_common.o + +obj-$(CONFIG_DVB_PT3) += pt3.o +obj-$(CONFIG_DVB_PXQ3PE) += pxq3pe.o + +ccflags-y += -Idrivers/media/dvb-core -Idrivers/media/dvb-frontends -Idrivers/media/tuners + diff --git a/drivers/media/pci/ptx/pt3_pci.c b/drivers/media/pci/ptx/pt3_pci.c new file mode 100644 index 0000000..3af9e48 --- /dev/null +++ b/drivers/media/pci/ptx/pt3_pci.c @@ -0,0 +1,509 @@ +/* + * DVB driver for Earthsoft PT3 ISDB-S/T PCIE bridge Altera Cyclone IV FPGA EP4CGX15BF14C8N + * + * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma> + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/pci.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include "dvb_frontend.h" +#include "tc90522.h" +#include "qm1d1c0042.h" +#include "mxl301rf.h" +#include "ptx_common.h" + +MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>"); +MODULE_DESCRIPTION("Earthsoft PT3 DVB Driver"); +MODULE_LICENSE("GPL"); + +static struct pci_device_id pt3_id[] = { + {PCI_DEVICE(0x1172, 0x4c15)}, + {}, +}; +MODULE_DEVICE_TABLE(pci, pt3_id); + +enum ePT3 { + PT3_REG_VERSION = 0x00, /* R Version */ + PT3_REG_BUS = 0x04, /* R Bus */ + PT3_REG_SYS_W = 0x08, /* W System */ + PT3_REG_SYS_R = 0x0c, /* R System */ + PT3_REG_I2C_W = 0x10, /* W I2C */ + PT3_REG_I2C_R = 0x14, /* R I2C */ + PT3_REG_RAM_W = 0x18, /* W RAM */ + PT3_REG_RAM_R = 0x1c, /* R RAM */ + PT3_DMA_BASE = 0x40, /* + 0x18*idx */ + PT3_DMA_OFFSET = 0x18, + PT3_DMA_DESC_L = 0x00, /* W DMA descriptor */ + PT3_DMA_DESC_H = 0x04, /* W DMA descriptor */ + PT3_DMA_CTL = 0x08, /* W DMA */ + PT3_TS_CTL = 0x0c, /* W TS */ + PT3_STATUS = 0x10, /* R DMA/FIFO/TS */ + PT3_TS_ERR = 0x14, /* R TS */ + + PT3_I2C_DATA_OFFSET = 0x800, + PT3_I2C_START_ADDR = 0x17fa, + + PT3_PWR_OFF = 0x00, + PT3_PWR_AMP_ON = 0x04, + PT3_PWR_TUNER_ON = 0x40, +}; + +struct pt3_card { + void __iomem *bar_reg, + *bar_mem; +}; + +struct pt3_dma { + dma_addr_t adr; + u8 *dat; + u32 sz, + pos; +}; + +struct pt3_adap { + u32 ts_pos, + ts_count, + desc_count; + void __iomem *dma_base; + struct pt3_dma *ts_info, + *desc_info; +}; + +int pt3_i2c_flush(struct pt3_card *c, u32 start_addr) +{ + u32 i2c_wait(void) + { + while (1) { + u32 val = readl(c->bar_reg + PT3_REG_I2C_R); + + if (!(val & 1)) /* sequence stopped */ + return val; + msleep_interruptible(1); + } + } + i2c_wait(); + writel(1 << 16 | start_addr, c->bar_reg + PT3_REG_I2C_W); /* 0x00010000 start sequence */ + return i2c_wait() & 0b0110 ? -EIO : 0; /* ACK status */ +} + +int pt3_i2c_xfr(struct i2c_adapter *i2c, struct i2c_msg *msg, int sz) +{ + enum pt3_i2c_cmd { + I_END, + I_ADDRESS, + I_CLOCK_L, + I_CLOCK_H, + I_DATA_L, + I_DATA_H, + I_RESET, + I_SLEEP, + I_DATA_L_NOP = 0x08, + I_DATA_H_NOP = 0x0c, + I_DATA_H_READ = 0x0d, + I_DATA_H_ACK0 = 0x0e, + }; + struct ptx_card *card = i2c_get_adapdata(i2c); + struct pt3_card *c = card->priv; + u32 offset = 0; + u8 buf; + bool filled = false; + + void i2c_shoot(u8 dat) + { + if (filled) { + buf |= dat << 4; + writeb(buf, c->bar_mem + PT3_I2C_DATA_OFFSET + offset); + offset++; + } else + buf = dat; + filled ^= true; + } + + void i2c_w(const u8 *dat, u32 size) + { + u32 i, j; + + for (i = 0; i < size; i++) { + for (j = 0; j < 8; j++) + i2c_shoot((dat[i] >> (7 - j)) & 1 ? I_DATA_H_NOP : I_DATA_L_NOP); + i2c_shoot(I_DATA_H_ACK0); + } + } + + void i2c_r(u32 size) + { + u32 i, j; + + for (i = 0; i < size; i++) { + for (j = 0; j < 8; j++) + i2c_shoot(I_DATA_H_READ); + if (i == (size - 1)) + i2c_shoot(I_DATA_H_NOP); + else + i2c_shoot(I_DATA_L_NOP); + } + } + int i, j; + + if (sz < 1 || sz > 3 || !msg || msg[0].flags) /* always write first */ + return -ENOTSUPP; + mutex_lock(&card->lock); + for (i = 0; i < sz; i++) { + u8 byte = (msg[i].addr << 1) | (msg[i].flags & 1); + + /* start */ + i2c_shoot(I_DATA_H); + i2c_shoot(I_CLOCK_H); + i2c_shoot(I_DATA_L); + i2c_shoot(I_CLOCK_L); + i2c_w(&byte, 1); + if (msg[i].flags == I2C_M_RD) + i2c_r(msg[i].len); + else + i2c_w(msg[i].buf, msg[i].len); + } + + /* stop */ + i2c_shoot(I_DATA_L); + i2c_shoot(I_CLOCK_H); + i2c_shoot(I_DATA_H); + i2c_shoot(I_END); + if (filled) + i2c_shoot(I_END); + if (pt3_i2c_flush(c, 0)) + sz = -EIO; + else + for (i = 1; i < sz; i++) + if (msg[i].flags == I2C_M_RD) + for (j = 0; j < msg[i].len; j++) + msg[i].buf[j] = readb(c->bar_mem + PT3_I2C_DATA_OFFSET + j); + mutex_unlock(&card->lock); + return sz; +} + +static const struct i2c_algorithm pt3_i2c_algo = { + .functionality = ptx_i2c_func, + .master_xfer = pt3_i2c_xfr, +}; + +void pt3_lnb(struct ptx_card *card, bool lnb) +{ + struct pt3_card *c = card->priv; + + writel(lnb ? 0b1111 : 0b1100, c->bar_reg + PT3_REG_SYS_W); +} + +int pt3_thread(void *dat) +{ + struct ptx_adap *adap = dat; + struct pt3_adap *p = adap->priv; + struct pt3_dma *ts; + u32 i, + prev; + size_t csize, + remain = 0; + + set_freezable(); + while (!kthread_should_stop()) { + try_to_freeze(); + while (p->ts_info[p->ts_pos].sz > remain) { + remain = p->ts_info[p->ts_pos].sz; + mutex_lock(&adap->lock); + while (remain > 0) { + for (i = 0; i < 20; i++) { + struct pt3_dma *ts; + u32 next = p->ts_pos + 1; + + if (next >= p->ts_count) + next = 0; + ts = &p->ts_info[next]; + if (ts->dat[ts->pos] == PTX_TS_SYNC) + break; + msleep_interruptible(30); + } + if (i == 20) + break; + prev = p->ts_pos - 1; + if (prev < 0 || p->ts_count <= prev) + prev = p->ts_count - 1; + ts = &p->ts_info[p->ts_pos]; + while (remain > 0) { + csize = (remain < (ts->sz - ts->pos)) ? + remain : (ts->sz - ts->pos); + dvb_dmx_swfilter_raw(&adap->demux, &ts->dat[ts->pos], csize); + remain -= csize; + ts->pos += csize; + if (ts->pos < ts->sz) + continue; + ts->pos = 0; + ts->dat[ts->pos] = PTX_TS_NOT_SYNC; + p->ts_pos++; + if (p->ts_pos >= p->ts_count) + p->ts_pos = 0; + break; + } + } + mutex_unlock(&adap->lock); + } + if (p->ts_info[p->ts_pos].sz < remain) + msleep_interruptible(1); + } + return 0; +} + +void pt3_dma_run(struct ptx_adap *adap, bool ON) +{ + struct pt3_adap *p = adap->priv; + void __iomem *base = p->dma_base; + u64 start_addr = p->desc_info->adr, + i; + + if (ON) { + writel(1 << 18, base + PT3_TS_CTL); /* reset error count */ + for (i = 0; i < p->ts_count; i++) { + struct pt3_dma *ts = &p->ts_info[i]; + + memset(ts->dat, 0, ts->sz); + ts->pos = 0; + *ts->dat = PTX_TS_NOT_SYNC; + } + p->ts_pos = 0; + writel(1 << 1, base + PT3_DMA_CTL); /* stop DMA */ + writel(start_addr & 0xffffffff, base + PT3_DMA_DESC_L); + writel(start_addr >> 32, base + PT3_DMA_DESC_H); + writel(1 << 0, base + PT3_DMA_CTL); /* start DMA */ + } else { + writel(1 << 1, base + PT3_DMA_CTL); /* stop DMA */ + while (1) { + if (!(readl(base + PT3_STATUS) & 1)) + break; + msleep_interruptible(1); + } + } +} + +int pt3_stop_feed(struct dvb_demux_feed *feed) +{ + struct ptx_adap *adap = container_of(feed->demux, struct ptx_adap, demux); + + if (adap->kthread) { + pt3_dma_run(adap, false); + kthread_stop(adap->kthread); + adap->kthread = NULL; + } + return 0; +} + +int pt3_start_feed(struct dvb_demux_feed *feed) +{ + int err = 0; + struct ptx_adap *adap = container_of(feed->demux, struct ptx_adap, demux); + + if (!adap->kthread) { + adap->kthread = kthread_run(pt3_thread, adap, "%s_%d", adap->card->name, adap->fe.id); + if (IS_ERR(adap->kthread)) { + err = PTR_ERR(adap->kthread); + adap->kthread = NULL; + } else + pt3_dma_run(adap, true); + } + return err; +} + +void pt3_dma_free(struct ptx_adap *adap) +{ + struct pt3_adap *p = adap->priv; + struct pt3_dma *page; + u32 i; + + if (p->ts_info) { + for (i = 0; i < p->ts_count; i++) { + page = &p->ts_info[i]; + if (page->dat) + pci_free_consistent(adap->card->pdev, page->sz, page->dat, page->adr); + } + kfree(p->ts_info); + } + if (p->desc_info) { + for (i = 0; i < p->desc_count; i++) { + page = &p->desc_info[i]; + if (page->dat) + pci_free_consistent(adap->card->pdev, page->sz, page->dat, page->adr); + } + kfree(p->desc_info); + } +} + +int pt3_power(struct dvb_frontend *fe, u8 pwr) +{ + struct ptx_adap *adap = container_of(fe, struct ptx_adap, fe); + u8 buf[] = {0x1e, pwr | 0b10011001}; + struct i2c_msg msg[] = { + {.addr = fe->id, .flags = 0, .buf = buf, .len = 2,}, + }; + + return i2c_transfer(&adap->card->i2c, msg, 1) == 1 ? 0 : -EIO; +} + +void pt3_remove(struct pci_dev *pdev) +{ + struct ptx_card *card = pci_get_drvdata(pdev); + struct pt3_card *c = card->priv; + struct ptx_adap *adap = card->adap; + int i; + + if (!card) + return; + for (i = 0; i < card->adapn; i++, adap++) { + pt3_dma_run(adap, false); + pt3_dma_free(adap); + ptx_sleep(&adap->fe); + pt3_power(&adap->fe, PT3_PWR_OFF); + } + ptx_unregister_adap_fe(card); + if (c->bar_reg) { + writel(1 << 17, c->bar_reg + PT3_REG_I2C_W); /* i2c_reset */ + iounmap(c->bar_reg); + } + if (c->bar_mem) + iounmap(c->bar_mem); +} + +int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct dma_desc { + u64 page_addr; + u32 page_size; + u64 next_desc; + } __packed; + enum { + DMA_MAX_DESCS = 204, + DMA_PAGE_SIZE = DMA_MAX_DESCS * sizeof(struct dma_desc), + DMA_BLOCK_COUNT = 17, + DMA_BLOCK_SIZE = DMA_PAGE_SIZE * 47, + DMA_TS_BUF_SIZE = DMA_BLOCK_SIZE * DMA_BLOCK_COUNT, + }; + struct ptx_subdev_info pt3_subdev_info[] = { + {SYS_ISDBS, 0b00010001, TC90522_MODNAME, 0x63, QM1D1C0042_MODNAME}, + {SYS_ISDBS, 0b00010011, TC90522_MODNAME, 0x60, QM1D1C0042_MODNAME}, + {SYS_ISDBT, 0b00010000, TC90522_MODNAME, 0x62, MXL301RF_MODNAME}, + {SYS_ISDBT, 0b00010010, TC90522_MODNAME, 0x61, MXL301RF_MODNAME}, + }; + struct ptx_card *card = ptx_alloc(pdev, KBUILD_MODNAME, ARRAY_SIZE(pt3_subdev_info), + sizeof(struct pt3_card), sizeof(struct pt3_adap), pt3_lnb); + struct pt3_card *c = card->priv; + struct ptx_adap *adap; + + bool dma_create(void) + { + struct pt3_adap *p = adap->priv; + struct pt3_dma *descinfo; + struct dma_desc *prev = NULL, + *curr; + u32 i, + j, + desc_remain = 0, + desc_info_pos = 0; + u64 desc_addr; + + p->ts_count = DMA_BLOCK_COUNT; + p->ts_info = kcalloc(p->ts_count, sizeof(struct pt3_dma), GFP_KERNEL); + p->desc_count = 1 + (DMA_TS_BUF_SIZE / DMA_PAGE_SIZE - 1) / DMA_MAX_DESCS; + p->desc_info = kcalloc(p->desc_count, sizeof(struct pt3_dma), GFP_KERNEL); + if (!p->ts_info || !p->desc_info) + return false; + for (i = 0; i < p->ts_count; i++) { + p->ts_info[i].sz = DMA_BLOCK_SIZE; + p->ts_info[i].pos = 0; + p->ts_info[i].dat = pci_alloc_consistent(adap->card->pdev, p->ts_info[i].sz, &p->ts_info[i].adr); + if (!p->ts_info[i].dat) + return false; + } + for (i = 0; i < p->desc_count; i++) { + p->desc_info[i].sz = DMA_PAGE_SIZE; + p->desc_info[i].pos = 0; + p->desc_info[i].dat = pci_alloc_consistent(adap->card->pdev, p->desc_info[i].sz, &p->desc_info[i].adr); + if (!p->desc_info[i].dat) + return false; + } + for (i = 0; i < p->ts_count; i++) + for (j = 0; j < p->ts_info[i].sz / DMA_PAGE_SIZE; j++) { + if (desc_remain < sizeof(struct dma_desc)) { + descinfo = &p->desc_info[desc_info_pos]; + descinfo->pos = 0; + curr = (struct dma_desc *)&descinfo->dat[descinfo->pos]; + desc_addr = descinfo->adr; + desc_remain = descinfo->sz; + desc_info_pos++; + } + if (prev) + prev->next_desc = desc_addr | 0b10; + curr->page_addr = 0b111 | (p->ts_info[i].adr + DMA_PAGE_SIZE * j); + curr->page_size = 0b111 | DMA_PAGE_SIZE; + curr->next_desc = 0b10; + + prev = curr; + descinfo->pos += sizeof(struct dma_desc); + curr = (struct dma_desc *)&descinfo->dat[descinfo->pos]; + desc_addr += sizeof(struct dma_desc); + desc_remain -= sizeof(struct dma_desc); + } + prev->next_desc = p->desc_info->adr | 0b10; + return true; + } + + bool i2c_is_clean(void) + { + return (readl(c->bar_reg + PT3_REG_I2C_R) >> 3) & 1; + } + u8 i; + int err = !card || pci_read_config_byte(pdev, PCI_CLASS_REVISION, &i); + + if (err) + return ptx_abort(pdev, pt3_remove, err, "PCI/DMA/memory error"); + if (i != 1) + return ptx_abort(pdev, pt3_remove, -EINVAL, "Revision 0x%X is not supported", i); + pci_set_master(pdev); + c->bar_reg = pci_ioremap_bar(pdev, 0); + c->bar_mem = pci_ioremap_bar(pdev, 2); + if (!c->bar_reg || !c->bar_mem) + return ptx_abort(pdev, pt3_remove, -EIO, "Failed pci_ioremap_bar"); + err = readl(c->bar_reg + PT3_REG_VERSION); + i = ((err >> 24) & 0xFF); + if (i != 3) + return ptx_abort(pdev, pt3_remove, -EIO, "ID=0x%X, not a PT3", i); + i = ((err >> 8) & 0xFF); + if (i != 4) + return ptx_abort(pdev, pt3_remove, -EIO, "FPGA version 0x%X is not supported", i); + if (ptx_i2c_add_adapter(card, &pt3_i2c_algo) || (!i2c_is_clean() && pt3_i2c_flush(c, 0))) + return ptx_abort(pdev, pt3_remove, err, "Cannot add I2C"); + for (i = 0, adap = card->adap; i < card->adapn; i++, adap++) { + struct dvb_frontend *fe = &adap->fe; + struct pt3_adap *p = adap->priv; + + fe->id = pt3_subdev_info[i].demod_addr; + p->dma_base = c->bar_reg + PT3_DMA_BASE + PT3_DMA_OFFSET * i; + if (!dma_create()) + return ptx_abort(pdev, pt3_remove, -ENOMEM, "Failed dma_create"); + } + err = ptx_register_adap_fe(card, pt3_subdev_info, pt3_start_feed, pt3_stop_feed) || + pt3_power(&card->adap[i - 1].fe, PT3_PWR_TUNER_ON) || + pt3_i2c_flush(c, PT3_I2C_START_ADDR) || + pt3_power(&card->adap[i - 1].fe, PT3_PWR_TUNER_ON | PT3_PWR_AMP_ON); + return err ? ptx_abort(pdev, pt3_remove, err, "Unable to register DVB adapter/frontend") : 0; +} + +static struct pci_driver pt3_driver = { + .name = KBUILD_MODNAME, + .id_table = pt3_id, + .probe = pt3_probe, + .remove = pt3_remove, +}; +module_pci_driver(pt3_driver); + diff --git a/drivers/media/pci/ptx/ptx_common.c b/drivers/media/pci/ptx/ptx_common.c new file mode 100644 index 0000000..2d98d27 --- /dev/null +++ b/drivers/media/pci/ptx/ptx_common.c @@ -0,0 +1,215 @@ +/* + * Common procedures for PT3 & PX-Q3PE DVB driver + * + * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma> + */ + +#include <linux/pci.h> +#include "dvb_frontend.h" +#include "ptx_common.h" + +void ptx_lnb(struct ptx_card *card) +{ + int i; + bool lnb = false; + + for (i = 0; i < card->adapn; i++) + if (card->adap[i].fe.dtv_property_cache.delivery_system == SYS_ISDBS && card->adap[i].ON) { + lnb = true; + break; + } + if (card->lnbON != lnb) { + card->lnb(card, lnb); + card->lnbON = lnb; + } +} + +int ptx_sleep(struct dvb_frontend *fe) +{ + struct ptx_adap *adap = container_of(fe, struct ptx_adap, fe); + + adap->ON = false; + ptx_lnb(adap->card); + return adap->fe_sleep ? adap->fe_sleep(fe) : 0; +} + +int ptx_wakeup(struct dvb_frontend *fe) +{ + struct ptx_adap *adap = container_of(fe, struct ptx_adap, fe); + + adap->ON = true; + ptx_lnb(adap->card); + return adap->fe_wakeup ? adap->fe_wakeup(fe) : 0; +} + +struct ptx_card *ptx_alloc(struct pci_dev *pdev, u8 *name, u8 adapn, u32 sz_card_priv, u32 sz_adap_priv, + void (*lnb)(struct ptx_card *, bool)) +{ + u8 i; + struct ptx_card *card = kzalloc(sizeof(struct ptx_card) + sz_card_priv + adapn * + (sizeof(struct ptx_adap) + sz_adap_priv), GFP_KERNEL); + if (!card) + return NULL; + card->priv = sz_card_priv ? &card[1] : NULL; + card->adap = (struct ptx_adap *)((u8 *)&card[1] + sz_card_priv); + card->pdev = pdev; + card->adapn = adapn; + card->name = name; + card->lnbON = true; + card->lnb = lnb; + for (i = 0; i < card->adapn; i++) { + struct ptx_adap *p = &card->adap[i]; + + p->card = card; + p->priv = sz_adap_priv ? (u8 *)&card->adap[card->adapn] + i * sz_adap_priv : NULL; + } + if (pci_enable_device(pdev) || + pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) || + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) || + pci_request_regions(pdev, name)) { + kfree(card); + return NULL; + } + pci_set_drvdata(pdev, card); + return card; +} + +int ptx_i2c_add_adapter(struct ptx_card *card, const struct i2c_algorithm *algo) +{ + struct i2c_adapter *i2c = &card->i2c; + + i2c->algo = algo; + i2c->dev.parent = &card->pdev->dev; + strcpy(i2c->name, card->name); + i2c_set_adapdata(i2c, card); + mutex_init(&card->lock); + return i2c_add_adapter(i2c); +} + +void ptx_unregister_subdev(struct i2c_client *c) +{ + if (!c) + return; + if (c->dev.driver) + module_put(c->dev.driver->owner); + i2c_unregister_device(c); +} + +struct i2c_client *ptx_register_subdev(struct i2c_adapter *i2c, void *dat, u16 adr, char *type) +{ + struct i2c_client *c; + struct i2c_board_info info = { + .platform_data = dat, + .addr = adr, + }; + + strlcpy(info.type, type, I2C_NAME_SIZE); + request_module("%s", info.type); + c = i2c_new_device(i2c, &info); + if (c) { + if (c->dev.driver && try_module_get(c->dev.driver->owner)) + return c; + i2c_unregister_device(c); + } + return NULL; +} + +void ptx_unregister_adap_fe(struct ptx_card *card) +{ + int i = card->adapn - 1; + struct ptx_adap *adap = card->adap + i; + + for (; i >= 0; i--, adap--) { + if (adap->fe.frontend_priv) + dvb_unregister_frontend(&adap->fe); + if (adap->fe.ops.release) + adap->fe.ops.release(&adap->fe); + ptx_unregister_subdev(adap->tuner); + ptx_unregister_subdev(adap->demod); + if (adap->demux.dmx.close) + adap->demux.dmx.close(&adap->demux.dmx); + if (adap->dmxdev.filter) + dvb_dmxdev_release(&adap->dmxdev); + if (adap->demux.cnt_storage) + dvb_dmx_release(&adap->demux); + if (adap->dvb.name) + dvb_unregister_adapter(&adap->dvb); + } + i2c_del_adapter(&card->i2c); + pci_release_regions(card->pdev); + pci_set_drvdata(card->pdev, NULL); + pci_disable_device(card->pdev); + kfree(card); +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adap_no); +int ptx_register_adap_fe(struct ptx_card *card, const struct ptx_subdev_info *info, + int (*start)(struct dvb_demux_feed *), int (*stop)(struct dvb_demux_feed *)) +{ + struct ptx_adap *adap; + u8 i; + int err; + + for (i = 0, adap = card->adap; i < card->adapn; i++, adap++) { + struct dvb_adapter *dvb = &adap->dvb; + struct dvb_demux *demux = &adap->demux; + struct dmxdev *dmxdev = &adap->dmxdev; + struct dvb_frontend *fe = &adap->fe; + + if (dvb_register_adapter(dvb, card->name, THIS_MODULE, &card->pdev->dev, adap_no) < 0) + return -ENFILE; + demux->feednum = 1; + demux->filternum = 1; + demux->start_feed = start; + demux->stop_feed = stop; + if (dvb_dmx_init(demux) < 0) + return -ENOMEM; + dmxdev->filternum = 1; + dmxdev->demux = &demux->dmx; + err = dvb_dmxdev_init(dmxdev, dvb); + if (err) + return err; + fe->dtv_property_cache.delivery_system = info[i].type; + fe->dvb = &adap->dvb; + adap->demod = ptx_register_subdev(&card->i2c, &adap->fe, info[i].demod_addr, info[i].demod_name); + adap->tuner = ptx_register_subdev(&card->i2c, &adap->fe, info[i].tuner_addr, info[i].tuner_name); + if (!adap->demod || !adap->tuner) + return -ENFILE; + adap->fe_sleep = adap->fe.ops.sleep; + adap->fe_wakeup = adap->fe.ops.init; + adap->fe.ops.sleep = ptx_sleep; + adap->fe.ops.init = ptx_wakeup; + adap->fe.dvb = &adap->dvb; + if (dvb_register_frontend(&adap->dvb, &adap->fe)) + return -EIO; + ptx_sleep(&adap->fe); + mutex_init(&adap->lock); + } + return 0; +} + +int ptx_abort(struct pci_dev *pdev, void remover(struct pci_dev *), int err, char *fmt, ...) +{ + va_list ap; + char *s = NULL; + int slen; + + va_start(ap, fmt); + slen = vsnprintf(s, 0, fmt, ap) + 1; + s = vzalloc(slen); + if (s) { + vsnprintf(s, slen, fmt, ap); + dev_err(&pdev->dev, "%s", s); + vfree(s); + } + va_end(ap); + remover(pdev); + return err; +} + +u32 ptx_i2c_func(struct i2c_adapter *i2c) +{ + return I2C_FUNC_I2C | I2C_FUNC_NOSTART; +} + + diff --git a/drivers/media/pci/ptx/ptx_common.h b/drivers/media/pci/ptx/ptx_common.h new file mode 100644 index 0000000..e5edba41 --- /dev/null +++ b/drivers/media/pci/ptx/ptx_common.h @@ -0,0 +1,68 @@ +/* + * Defs & procs for PT3 & PX-Q3PE DVB driver + * + * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma> + * + * This program is distributed in hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef PTX_COMMON_H +#define PTX_COMMON_H + +#include "dvb_demux.h" +#include "dmxdev.h" + +enum ePTX { + PTX_TS_SYNC = 0x47, + PTX_TS_NOT_SYNC = 0x74, +}; + +struct ptx_subdev_info { + fe_delivery_system_t type; + u8 demod_addr, *demod_name, + tuner_addr, *tuner_name; +}; + +struct ptx_card { + struct ptx_adap *adap; + struct mutex lock; + struct i2c_adapter i2c; + struct pci_dev *pdev; + u8 *name, + adapn; + bool lnbON; + void *priv, + (*lnb)(struct ptx_card *card, bool lnb); +}; + +struct ptx_adap { + struct ptx_card *card; + struct mutex lock; + bool ON; + struct dvb_frontend fe; + struct dvb_adapter dvb; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct i2c_client *demod, + *tuner; + struct task_struct *kthread; + void *priv; + int (*fe_sleep)(struct dvb_frontend *), + (*fe_wakeup)(struct dvb_frontend *); +}; + +struct ptx_card *ptx_alloc(struct pci_dev *pdev, u8 *name, u8 adapn, u32 sz_card_priv, u32 sz_adap_priv, + void (*lnb)(struct ptx_card *, bool)); +int ptx_sleep(struct dvb_frontend *fe); +int ptx_wakeup(struct dvb_frontend *fe); +int ptx_i2c_add_adapter(struct ptx_card *card, const struct i2c_algorithm *algo); +void ptx_unregister_adap_fe(struct ptx_card *card); +int ptx_register_adap_fe(struct ptx_card *card, const struct ptx_subdev_info *info, + int (*start)(struct dvb_demux_feed *), int (*stop)(struct dvb_demux_feed *)); +int ptx_abort(struct pci_dev *pdev, void remover(struct pci_dev *), int err, char *fmt, ...); +u32 ptx_i2c_func(struct i2c_adapter *i2c); + +#endif diff --git a/drivers/media/pci/ptx/pxq3pe_pci.c b/drivers/media/pci/ptx/pxq3pe_pci.c new file mode 100644 index 0000000..5bf0648 --- /dev/null +++ b/drivers/media/pci/ptx/pxq3pe_pci.c @@ -0,0 +1,607 @@ +/* + * DVB driver for PLEX PX-Q3PE ISDB-S/T PCIE receiver + * + * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma> + * + * Main components: + * ASIE5606X8 - controller + * TC90522 - 2ch OFDM ISDB-T + 2ch 8PSK ISDB-S demodulator + * TDA20142 - ISDB-S tuner + * NM120 - ISDB-T tuner + */ + +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include "dvb_frontend.h" +#include "ptx_common.h" +#include "tc90522.h" +#include "tda2014x.h" +#include "nm131.h" + +#define MOD_AUTH "Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>" +MODULE_AUTHOR(MOD_AUTH); +MODULE_DESCRIPTION("PLEX PX-Q3PE Driver"); +MODULE_LICENSE("GPL"); + +static char *auth = MOD_AUTH; +static int ni, + nx, + idx[8] = {0}, + xor[4] = {0}; +module_param(auth, charp, 0); +module_param_array(idx, int, &ni, 0); +module_param_array(xor, int, &nx, 0); + +static struct pci_device_id pxq3pe_id_table[] = { + {0x188B, 0x5220, 0x0B06, 0x0002, 0, 0, 0}, + {} +}; +MODULE_DEVICE_TABLE(pci, pxq3pe_id_table); + +enum ePXQ3PE { + PKT_BYTES = 188, + PKT_NUM = 312, + PKT_BUFSZ = PKT_BYTES * PKT_NUM, + + PXQ3PE_IRQ_STAT = 0x808, + PXQ3PE_IRQ_CLEAR = 0x80C, + PXQ3PE_IRQ_ACTIVE = 0x814, + PXQ3PE_IRQ_DISABLE = 0x818, + PXQ3PE_IRQ_ENABLE = 0x81C, + + PXQ3PE_MODE_GPIO = 0, + PXQ3PE_MODE_TUNER = 1, + PXQ3PE_MODE_STAT = 2, + + PXQ3PE_I2C_ADR_GPIO = 0x4A, + PXQ3PE_I2C_CTL_STAT = 0x940, + PXQ3PE_I2C_ADR = 0x944, + PXQ3PE_I2C_SW_CTL = 0x948, + PXQ3PE_I2C_FIFO_STAT = 0x950, + PXQ3PE_I2C_FIFO_DATA = 0x960, + + PXQ3PE_DMA_OFFSET_PORT = 0x140, + PXQ3PE_DMA_TSMODE = 0xA00, + PXQ3PE_DMA_MGMT = 0xAE0, + PXQ3PE_DMA_OFFSET_CH = 0x10, + PXQ3PE_DMA_ADR_LO = 0xAC0, + PXQ3PE_DMA_ADR_HI = 0xAC4, + PXQ3PE_DMA_XFR_STAT = 0xAC8, + PXQ3PE_DMA_CTL = 0xACC, + + PXQ3PE_MAX_LOOP = 0xFFFF, +}; + +struct pxq3pe_card { + void __iomem *bar; + struct { + dma_addr_t adr; + u8 *dat; + u32 sz; + bool ON[2]; + } dma; +}; + +struct pxq3pe_adap { + u8 tBuf[PKT_BUFSZ], + *sBuf; + u32 tBufIdx, + sBufSize, + sBufStart, + sBufStop, + sBufByteCnt; +}; + +bool pxq3pe_w(struct ptx_card *card, u8 slvadr, u8 regadr, u8 *wdat, u8 bytelen, u8 mode) +{ + struct pxq3pe_card *c = card->priv; + void __iomem *bar = c->bar; + int i, + j, + k; + u8 i2cCtlByte, + i2cFifoWSz; + + if ((readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F) != 0x10 || readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F00) + return false; + writel(0, bar + PXQ3PE_I2C_CTL_STAT); + switch (mode) { + case PXQ3PE_MODE_GPIO: + i2cCtlByte = 0xC0; + break; + case PXQ3PE_MODE_TUNER: + slvadr = 2 * slvadr + 0x20; + regadr = 0; + i2cCtlByte = 0x80; + break; + case PXQ3PE_MODE_STAT: + slvadr = 2 * slvadr + 0x20; + regadr = 0; + i2cCtlByte = 0x84; + break; + default: + return false; + } + writel((slvadr << 8) + regadr, bar + PXQ3PE_I2C_ADR); + for (i = 0; i < 16 && i < bytelen; i += 4) { + udelay(10); + writel(*((u32 *)(wdat + i)), bar + PXQ3PE_I2C_FIFO_DATA); + } + writew((bytelen << 8) + i2cCtlByte, bar + PXQ3PE_I2C_CTL_STAT); + for (j = 0; j < PXQ3PE_MAX_LOOP; j++) { + if (i < bytelen) { + i2cFifoWSz = readb(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F; + for (k = 0; bytelen > 16 && k < PXQ3PE_MAX_LOOP && i2cFifoWSz < bytelen - 16; k++) { + i2cFifoWSz = readb(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F; + udelay(10); + } + if (i2cFifoWSz & 3) + continue; + if (i2cFifoWSz) { + for (k = i; k < bytelen && k - i < i2cFifoWSz; k += 4) + writel(*((u32 *)(wdat + k)), bar + PXQ3PE_I2C_FIFO_DATA); + i = k; + } + } + udelay(10); + if (readl(bar + PXQ3PE_I2C_CTL_STAT) & 0x400000) + break; + } + return j < PXQ3PE_MAX_LOOP ? !(readl(bar + PXQ3PE_I2C_CTL_STAT) & 0x280000) : false; +} + +bool pxq3pe_r(struct ptx_card *card, u8 slvadr, u8 regadr, u8 *rdat, u8 bytelen, u8 mode) +{ + struct pxq3pe_card *c = card->priv; + void __iomem *bar = c->bar; + u8 i2cCtlByte, + i2cStat, + i2cFifoRSz, + i2cByteCnt; + int i = 0, + j, + idx; + bool ret = false; + + if ((readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F) != 0x10 || readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F00) + return false; + writel(0, bar + PXQ3PE_I2C_CTL_STAT); + switch (mode) { + case PXQ3PE_MODE_GPIO: + i2cCtlByte = 0xE0; + break; + case PXQ3PE_MODE_TUNER: + slvadr = 2 * slvadr + 0x20; + regadr = 0; + i2cCtlByte = 0xA0; + break; + default: + return false; + } + writel((slvadr << 8) + regadr, bar + PXQ3PE_I2C_ADR); + writew(i2cCtlByte + (bytelen << 8), bar + PXQ3PE_I2C_CTL_STAT); + i2cByteCnt = bytelen; + j = 0; + while (j < PXQ3PE_MAX_LOOP) { + udelay(10); + i2cStat = (readl(bar + PXQ3PE_I2C_CTL_STAT) & 0xFF0000) >> 16; + if (i2cStat & 0x80) { + if (i2cStat & 0x28) + break; + ret = true; + } + i2cFifoRSz = (readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F00) >> 8; + if (i2cFifoRSz & 3) { + ++j; + continue; + } + for (idx = i; i2cFifoRSz && idx < i2cByteCnt && idx - i < i2cFifoRSz; idx += 4) + *(u32 *)(rdat + idx) = readl(bar + PXQ3PE_I2C_FIFO_DATA); + i = idx; + if (i < bytelen) { + if (i2cFifoRSz) + i2cByteCnt -= i2cFifoRSz; + else + ++j; + continue; + } + i2cStat = (readl(bar + PXQ3PE_I2C_CTL_STAT) & 0xFF0000) >> 16; + if (i2cStat & 0x80) { + if (i2cStat & 0x28) + break; + ret = true; + break; + } + ++j; + } + return !(readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F00) && ret; +} + +int pxq3pe_xfr(struct i2c_adapter *i2c, struct i2c_msg *msg, int sz) +{ + struct ptx_card *card = i2c_get_adapdata(i2c); + u8 i; + bool ret = true; + + if (!i2c || !card || !msg) + return -EINVAL; + for (i = 0; i < sz && ret; i++, msg++) { + u8 slvadr = msg->addr, + regadr = msg->len ? *msg->buf : 0, + mode = slvadr == PXQ3PE_I2C_ADR_GPIO ? PXQ3PE_MODE_GPIO + : sz > 1 && i == sz - 2 ? PXQ3PE_MODE_STAT + : PXQ3PE_MODE_TUNER; + + mutex_lock(&card->lock); + if (msg->flags & I2C_M_RD) { + u8 buf[sz]; + + ret = pxq3pe_r(card, slvadr, regadr, buf, msg->len, mode); + memcpy(msg->buf, buf, msg->len); + } else + ret = pxq3pe_w(card, slvadr, regadr, msg->buf, msg->len, mode); + mutex_unlock(&card->lock); + } + return i; +} + +bool pxq3pe_w_gpio2(struct ptx_card *card, u8 dat, u8 mask) +{ + u8 val; + + return pxq3pe_r(card, PXQ3PE_I2C_ADR_GPIO, 0xB, &val, 1, PXQ3PE_MODE_GPIO) && + (val = (mask & dat) | (val & ~mask), pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 0xB, &val, 1, PXQ3PE_MODE_GPIO)); +} + +void pxq3pe_w_gpio1(struct ptx_card *card, u8 val, u8 mask) +{ + struct pxq3pe_card *c = card->priv; + + mask <<= 3; + writeb((readb(c->bar + 0x890) & ~mask) | ((val << 3) & mask), c->bar + 0x890); +} + +void pxq3pe_w_gpio0(struct ptx_card *card, u8 val, u8 mask) +{ + struct pxq3pe_card *c = card->priv; + + writeb((-(mask & 1) & 4 & -(val & 1)) | (readb(c->bar + 0x890) & ~(-(mask & 1) & 4)), c->bar + 0x890); + writeb((mask & val) | (readb(c->bar + 0x894) & ~mask), c->bar + 0x894); +} + +void pxq3pe_power(struct ptx_card *card, bool ON) +{ + if (ON) { + pxq3pe_w_gpio0(card, 1, 1); + pxq3pe_w_gpio0(card, 0, 1); + pxq3pe_w_gpio0(card, 1, 1); + pxq3pe_w_gpio1(card, 1, 1); + pxq3pe_w_gpio1(card, 0, 1); + pxq3pe_w_gpio2(card, 2, 2); + pxq3pe_w_gpio2(card, 0, 2); + pxq3pe_w_gpio2(card, 2, 2); + pxq3pe_w_gpio2(card, 4, 4); + pxq3pe_w_gpio2(card, 0, 4); + pxq3pe_w_gpio2(card, 4, 4); + } else { + pxq3pe_w_gpio0(card, 0, 1); + pxq3pe_w_gpio0(card, 1, 1); + pxq3pe_w_gpio1(card, 1, 1); + } +} + +irqreturn_t pxq3pe_irq(int irq, void *ctx) +{ + struct ptx_card *card = ctx; + struct pxq3pe_card *c = card->priv; + void __iomem *bar = c->bar; + u32 dmamgmt, + i, + intstat = readl(bar + PXQ3PE_IRQ_STAT); + bool ch = intstat & 0b0101 ? 0 : 1, + port = intstat & 0b0011 ? 0 : 1; + u8 *tbuf = c->dma.dat + PKT_BUFSZ * (port * 2 + ch); + + void pxq3pe_dma_put_stream(struct pxq3pe_adap *p) + { + u8 *src = p->tBuf; + u32 len = p->tBufIdx, + savesz = len <= p->sBufSize - p->sBufStop ? len : p->sBufSize - p->sBufStop, + remain = len - savesz; + + memcpy(&p->sBuf[p->sBufStop], src, savesz); + if (remain) + memcpy(p->sBuf, &src[savesz], remain); + p->sBufStop = (p->sBufStop + len) % p->sBufSize; + if (p->sBufByteCnt == p->sBufSize) + p->sBufStart = p->sBufStop; + else { + if (p->sBufSize >= p->sBufByteCnt + len) + p->sBufByteCnt += len; + else { + p->sBufStart = p->sBufStop; + p->sBufByteCnt = p->sBufSize; + } + } + } + + if (!(intstat & 0b1111)) + return IRQ_HANDLED; + writel(intstat, bar + PXQ3PE_IRQ_CLEAR); + dmamgmt = readl(bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT); + if ((readl(bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_OFFSET_CH * ch + PXQ3PE_DMA_XFR_STAT) & 0x3FFFFF) == PKT_BUFSZ) + for (i = 0; i < PKT_BUFSZ; i += PKT_BYTES) { + u8 i2cadr = !port * 4 + (tbuf[i] == 0xC7 ? 0 : tbuf[i] == 0x47 ? + 1 : tbuf[i] == 0x07 ? 2 : tbuf[i] == 0x87 ? 3 : card->adapn); + struct ptx_adap *adap = &card->adap[i2cadr]; + struct pxq3pe_adap *p = adap->priv; + + if (i2cadr < card->adapn && adap->ON) { + tbuf[i] = PTX_TS_SYNC; + memcpy(&p->tBuf[p->tBufIdx], &tbuf[i], PKT_BYTES); + p->tBufIdx += PKT_BYTES; + if (p->tBufIdx >= PKT_BUFSZ) { + pxq3pe_dma_put_stream(p); + p->tBufIdx = 0; + } + } + } + if (c->dma.ON[port]) + writel(dmamgmt | (2 << (ch * 16)), bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT); + return IRQ_HANDLED; +} + +int pxq3pe_thread(void *dat) +{ + struct ptx_adap *adap = dat; + struct pxq3pe_adap *p = adap->priv; + + set_freezable(); + while (!kthread_should_stop()) { + u8 *rbuf = &p->sBuf[p->sBufStart]; + int i = 0, + j = 0, + k, + sz = p->sBufSize - p->sBufStart; + + try_to_freeze(); + if (!p->sBufByteCnt) { + msleep_interruptible(0); + continue; + } + if (sz > p->sBufByteCnt) + sz = p->sBufByteCnt; + while (j < sz / PKT_BYTES) { + j++; + i += 4; + while (i < j * PKT_BYTES) + for (k = 0; k < 8; k++, i++) + rbuf[i] ^= xor[idx[k]]; + } + dvb_dmx_swfilter_raw(&adap->demux, rbuf, sz); + p->sBufStart = (p->sBufStart + sz) % p->sBufSize; + p->sBufByteCnt -= sz; + } + return 0; +} + +void pxq3pe_dma_stop(struct ptx_adap *adap) +{ + struct ptx_card *card = adap->card; + struct pxq3pe_card *c = card->priv; + u8 i2cadr = adap->fe.id, + i; + bool port = !(i2cadr & 4); + + for (i = 0; i < card->adapn; i++) + if (!c->dma.ON[port] || (i2cadr != i && (i & 4) == (i2cadr & 4) && c->dma.ON[port])) + return; + + i = readb(c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT); + if ((i & 0b1100) == 4) + writeb(i & 0xFD, c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT); + writeb(0b0011 << (port * 2), c->bar + PXQ3PE_IRQ_DISABLE); + c->dma.ON[port] = false; +} + +bool pxq3pe_dma_start(struct ptx_adap *adap) +{ + struct ptx_card *card = adap->card; + struct pxq3pe_card *c = card->priv; + struct pxq3pe_adap *p = adap->priv; + u8 i2cadr = adap->fe.id, + i; + bool port = !(i2cadr & 4); + u32 val = 0b0011 << (port * 2); + + p->sBufByteCnt = 0; + p->sBufStop = 0; + p->sBufStart = 0; + if (c->dma.ON[port]) + return true; + + /* SetTSMode */ + i = readb(c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_TSMODE); + if ((i & 0x80) == 0) + writeb(i | 0x80, c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_TSMODE); + + /* irq_enable */ + writel(val, c->bar + PXQ3PE_IRQ_ENABLE); + if (val != (readl(c->bar + PXQ3PE_IRQ_ACTIVE) & val)) + return false; + + /* cfg_dma */ + for (i = 0; i < 2; i++) { + val = readl(c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT); + writel(c->dma.adr + PKT_BUFSZ * (port * 2 + i), + c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_OFFSET_CH * i + PXQ3PE_DMA_ADR_LO); + writel(0, c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_OFFSET_CH * i + PXQ3PE_DMA_ADR_HI); + writel(0x11C0E520, c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_OFFSET_CH * i + PXQ3PE_DMA_CTL); + writel(val | 3 << (i * 16), + c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT); + } + c->dma.ON[port] = true; + return true; +} + +int pxq3pe_stop_feed(struct dvb_demux_feed *feed) +{ + struct ptx_adap *adap = container_of(feed->demux, struct ptx_adap, demux); + + pxq3pe_dma_stop(adap); + kthread_stop(adap->kthread); + return 0; +} + +int pxq3pe_start_feed(struct dvb_demux_feed *feed) +{ + struct ptx_adap *adap = container_of(feed->demux, struct ptx_adap, demux); + + if (!pxq3pe_dma_start(adap)) + return -EIO; + adap->kthread = kthread_run(pxq3pe_thread, adap, "%s_%d", adap->card->name, adap->fe.id); + return IS_ERR(adap->kthread) ? PTR_ERR(adap->kthread) : 0; +} + +void pxq3pe_lnb(struct ptx_card *card, bool lnb) +{ + pxq3pe_w_gpio2(card, lnb ? 0x20 : 0, 0x20); +} + +void pxq3pe_remove(struct pci_dev *pdev) +{ + struct ptx_card *card = pci_get_drvdata(pdev); + struct pxq3pe_card *c = card->priv; + u8 regctl = 0, + i; + + if (!card) + return; + for (i = 0; i < card->adapn; i++) { + struct ptx_adap *adap = &card->adap[i]; + + pxq3pe_dma_stop(adap); + ptx_sleep(&adap->fe); + } + pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 0x80, ®ctl, 1, PXQ3PE_MODE_GPIO); + pxq3pe_power(card, false); + + /* dma_hw_unmap */ + free_irq(pdev->irq, card); + if (c->dma.dat && dma_ops->free) + dma_ops->free(&pdev->dev, c->dma.sz, c->dma.dat, c->dma.adr, NULL); + for (i = 0; i < card->adapn; i++) { + struct ptx_adap *adap = &card->adap[i]; + struct pxq3pe_adap *p = adap->priv; + + vfree(p->sBuf); + } + if (c->bar) + pci_iounmap(pdev, c->bar); + ptx_unregister_adap_fe(card); +} + +static const struct i2c_algorithm pxq3pe_algo = { + .functionality = ptx_i2c_func, + .master_xfer = pxq3pe_xfr, +}; + +static int pxq3pe_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) +{ + struct ptx_subdev_info pxq3pe_subdev_info[] = { + {SYS_ISDBT, 0x10, TC90522_MODNAME, 0x20, NM131_MODNAME}, + {SYS_ISDBS, 0x11, TC90522_MODNAME, 0x21, TDA2014X_MODNAME}, + {SYS_ISDBT, 0x12, TC90522_MODNAME, 0x22, NM131_MODNAME}, + {SYS_ISDBS, 0x13, TC90522_MODNAME, 0x23, TDA2014X_MODNAME}, + {SYS_ISDBT, 0x14, TC90522_MODNAME, 0x24, NM131_MODNAME}, + {SYS_ISDBS, 0x15, TC90522_MODNAME, 0x25, TDA2014X_MODNAME}, + {SYS_ISDBT, 0x16, TC90522_MODNAME, 0x26, NM131_MODNAME}, + {SYS_ISDBS, 0x17, TC90522_MODNAME, 0x27, TDA2014X_MODNAME}, + }; + struct ptx_card *card = ptx_alloc(pdev, KBUILD_MODNAME, ARRAY_SIZE(pxq3pe_subdev_info), + sizeof(struct pxq3pe_card), sizeof(struct pxq3pe_adap), pxq3pe_lnb); + struct pxq3pe_card *c = card->priv; + struct device *dev = &pdev->dev; + u8 regctl = 0xA0, + i; + u16 cfg; + int err = !card || pci_read_config_word(pdev, PCI_COMMAND, &cfg); + + if (err) + return ptx_abort(pdev, pxq3pe_remove, err, "Memory/PCI/DMA error, card=%p", card); + if (!(cfg & PCI_COMMAND_MASTER)) { + pci_set_master(pdev); + pci_read_config_word(pdev, PCI_COMMAND, &cfg); + if (!(cfg & PCI_COMMAND_MASTER)) + return ptx_abort(pdev, pxq3pe_remove, -EIO, "Bus Mastering is disabled"); + } + c->bar = pci_iomap(pdev, 0, 0); + if (!c->bar) + return ptx_abort(pdev, pxq3pe_remove, -EIO, "I/O map failed"); + if (ptx_i2c_add_adapter(card, &pxq3pe_algo)) + return ptx_abort(pdev, pxq3pe_remove, -EIO, "Cannot add I2C"); + + for (i = 0; i < card->adapn; i++) { + struct ptx_adap *adap = &card->adap[i]; + struct pxq3pe_adap *p = adap->priv; + + adap->fe.id = i; + p->sBufSize = PKT_BYTES * 100 << 9; + p->sBuf = vzalloc(p->sBufSize); + if (!p->sBuf) + return ptx_abort(pdev, pxq3pe_remove, -ENOMEM, "No memory for stream buffer"); + } + + /* dma_map */ + c->dma.sz = PKT_BUFSZ * 4; + if (request_irq(pdev->irq, pxq3pe_irq, IRQF_SHARED, KBUILD_MODNAME, card)) + return ptx_abort(pdev, pxq3pe_remove, -EIO, "IRQ failed"); + if (dev->dma_mask && *dev->dma_mask && dma_ops->alloc) { + u32 gfp = dev->coherent_dma_mask ? 0x21 - (dev->coherent_dma_mask >= 0x1000000) : 0x20; + + if ((!dev->coherent_dma_mask || dev->coherent_dma_mask <= 0xFFFFFFFF) && !(gfp & 1)) + gfp |= 4; + c->dma.dat = dma_ops->alloc(dev, c->dma.sz, &c->dma.adr, gfp, NULL); + } + if (!c->dma.dat) + return ptx_abort(pdev, pxq3pe_remove, -EIO, "DMA mapping failed"); + + /* hw_init */ + writeb(readb(c->bar + 0x880) & 0xC0, c->bar + 0x880); + writel(0x3200C8, c->bar + 0x904); + writel(0x90, c->bar + 0x900); + writel(0x10000, c->bar + 0x880); + writel(0x0080, c->bar + PXQ3PE_DMA_TSMODE); + writel(0x0080, c->bar + PXQ3PE_DMA_TSMODE + PXQ3PE_DMA_OFFSET_PORT); + writel(0x0000, c->bar + 0x888); + writel(0x00CF, c->bar + 0x894); + writel(0x8000, c->bar + 0x88C); + writel(0x1004, c->bar + 0x890); + writel(0x0090, c->bar + 0x900); + writel(0x3200C8, c->bar + 0x904); + pxq3pe_w_gpio0(card, 8, 0xFF); + pxq3pe_w_gpio1(card, 0, 2); + pxq3pe_w_gpio1(card, 1, 1); + pxq3pe_w_gpio0(card, 1, 1); + pxq3pe_w_gpio0(card, 0, 1); + pxq3pe_w_gpio0(card, 1, 1); + for (i = 0; i < 16; i++) + if (!pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 0x10 + i, auth + i, 1, PXQ3PE_MODE_GPIO)) + break; + if (i < 16 || !pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 5, ®ctl, 1, PXQ3PE_MODE_GPIO)) + return ptx_abort(pdev, pxq3pe_remove, -EIO, "hw_init failed"); + pxq3pe_power(card, true); + err = ptx_register_adap_fe(card, pxq3pe_subdev_info, pxq3pe_start_feed, pxq3pe_stop_feed); + return err ? ptx_abort(pdev, pxq3pe_remove, err, "Unable to register DVB adapter & frontend") : 0; +} + +static struct pci_driver pxq3pe_driver = { + .name = KBUILD_MODNAME, + .id_table = pxq3pe_id_table, + .probe = pxq3pe_probe, + .remove = pxq3pe_remove, +}; +module_pci_driver(pxq3pe_driver); + -- 2.3.10 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2016-02-15 6:08 ` info @ 2016-02-15 7:34 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-02-15 7:34 UTC (permalink / raw) To: info Cc: kbuild-all, linux-media, Буди Романто, AreMa Inc, linux-kernel, crope, m.chehab, mchehab, hdegoede, laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski, peter.senna drivers/media/dvb-frontends/tc90522.c:271:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Буди Романто, AreMa Inc <knightrider@are.ma> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- tc90522.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/media/dvb-frontends/tc90522.c +++ b/drivers/media/dvb-frontends/tc90522.c @@ -268,7 +268,6 @@ MODULE_DEVICE_TABLE(i2c, tc90522_id); static struct i2c_driver tc90522_driver = { .driver = { - .owner = THIS_MODULE, .name = tc90522_id->name, }, .probe = tc90522_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-07-27 14:41 ` Julia Lawall 0 siblings, 0 replies; 40+ messages in thread From: Julia Lawall @ 2016-07-27 14:41 UTC (permalink / raw) To: Michal Simek, Dan Williams, Vinod Koul, Sören Brinkmann, Hyun Kwon Cc: kbuild-all, linux-arm-kernel, dmaengine, linux-kernel No need to set .owner here. The core will do it. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Julia Lawall <julia.lawall@lip6.fr> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- xilinx_dpdma.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -1944,7 +1944,6 @@ static struct platform_driver xilinx_dpd .remove = xilinx_dpdma_remove, .driver = { .name = "xilinx-dpdma", - .owner = THIS_MODULE, .of_match_table = xilinx_dpdma_of_match, }, }; ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-07-27 14:41 ` Julia Lawall 0 siblings, 0 replies; 40+ messages in thread From: Julia Lawall @ 2016-07-27 14:41 UTC (permalink / raw) To: linux-arm-kernel No need to set .owner here. The core will do it. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Julia Lawall <julia.lawall@lip6.fr> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- xilinx_dpdma.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -1944,7 +1944,6 @@ static struct platform_driver xilinx_dpd .remove = xilinx_dpdma_remove, .driver = { .name = "xilinx-dpdma", - .owner = THIS_MODULE, .of_match_table = xilinx_dpdma_of_match, }, }; ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] fix platform_no_drv_owner.cocci warnings 2016-07-27 14:41 ` Julia Lawall @ 2016-08-08 5:47 ` Vinod Koul -1 siblings, 0 replies; 40+ messages in thread From: Vinod Koul @ 2016-08-08 5:47 UTC (permalink / raw) To: Julia Lawall Cc: Michal Simek, Dan Williams, Sören Brinkmann, Hyun Kwon, kbuild-all, linux-arm-kernel, dmaengine, linux-kernel On Wed, Jul 27, 2016 at 04:41:16PM +0200, Julia Lawall wrote: > No need to set .owner here. The core will do it. And which tree was this generated against :-) Upstream doesn't have _dpdma.c > > Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci > > Signed-off-by: Julia Lawall <julia.lawall@lip6.fr> > Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> > --- > > xilinx_dpdma.c | 1 - > 1 file changed, 1 deletion(-) > > --- a/drivers/dma/xilinx/xilinx_dpdma.c > +++ b/drivers/dma/xilinx/xilinx_dpdma.c > @@ -1944,7 +1944,6 @@ static struct platform_driver xilinx_dpd > .remove = xilinx_dpdma_remove, > .driver = { > .name = "xilinx-dpdma", > - .owner = THIS_MODULE, > .of_match_table = xilinx_dpdma_of_match, > }, > }; -- ~Vinod ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2016-08-08 5:47 ` Vinod Koul 0 siblings, 0 replies; 40+ messages in thread From: Vinod Koul @ 2016-08-08 5:47 UTC (permalink / raw) To: linux-arm-kernel On Wed, Jul 27, 2016 at 04:41:16PM +0200, Julia Lawall wrote: > No need to set .owner here. The core will do it. And which tree was this generated against :-) Upstream doesn't have _dpdma.c > > Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci > > Signed-off-by: Julia Lawall <julia.lawall@lip6.fr> > Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> > --- > > xilinx_dpdma.c | 1 - > 1 file changed, 1 deletion(-) > > --- a/drivers/dma/xilinx/xilinx_dpdma.c > +++ b/drivers/dma/xilinx/xilinx_dpdma.c > @@ -1944,7 +1944,6 @@ static struct platform_driver xilinx_dpd > .remove = xilinx_dpdma_remove, > .driver = { > .name = "xilinx-dpdma", > - .owner = THIS_MODULE, > .of_match_table = xilinx_dpdma_of_match, > }, > }; -- ~Vinod ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] This patch allows the Terratec Cinergy HTC Stick HD (0ccb:0101) to be used to watch DVB-T.
2016-08-06 15:03 ` Gerard H. Pille
@ 2016-08-06 22:00 kbuild test robot
2016-08-06 15:03 ` Gerard H. Pille
0 siblings, 1 reply; 40+ messages in thread
From: kbuild test robot @ 2016-08-06 22:00 UTC (permalink / raw)
To: Gerard H. Pille; +Cc: kbuild-all, linux-media, Gerard H. Pille
Hi Gerard,
[auto build test WARNING on linuxtv-media/master]
[also build test WARNING on v4.7 next-20160805]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Gerard-H-Pille/This-patch-allows-the-Terratec-Cinergy-HTC-Stick-HD-0ccb-0101-to-be-used-to-watch-DVB-T/20160807-042525
base: git://linuxtv.org/media_tree.git master
coccinelle warnings: (new ones prefixed by >>)
>> drivers/media/dvb-frontends/si2165.c:1192:3-8: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] This patch allows the Terratec Cinergy HTC Stick HD (0ccb:0101) to be used to watch DVB-T. @ 2016-08-06 15:03 ` Gerard H. Pille 2016-08-06 22:00 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: Gerard H. Pille @ 2016-08-06 15:03 UTC (permalink / raw) To: linux-media; +Cc: Gerard H. Pille The patch is generated from a Linux kernel 4.6.1, to which I applied Matthias Schwarzott's patches from 26/07/2016 - as a consequence, his work is included here, please tell me how to separate it when that's necessary. My patch will conflict with the one from Oleh Kravchenko 04/08/2016. I haven't got a clue what I am messing with: no experience with TV hardware, no understanding of I2C programming, my changes are based on USB snooping and what I found in the Windows driver's .inf. Eg. "write pll registers 0x00a0..0x00a3 at once" is no longer true. Won't this break other HW? Signed-off-by: Gerard H. Pille <g.h.p@skynet.be> --- drivers/media/dvb-frontends/Kconfig | 1 + drivers/media/dvb-frontends/si2165.c | 278 +++++++++++++++++------------ drivers/media/dvb-frontends/si2165.h | 27 +-- drivers/media/dvb-frontends/si2165_priv.h | 17 ++ drivers/media/pci/cx23885/cx23885-dvb.c | 30 +++- drivers/media/tuners/si2157.c | 52 ++++-- drivers/media/tuners/si2157_priv.h | 2 + drivers/media/tuners/tuner-types.c | 4 + drivers/media/usb/cx231xx/cx231xx-avcore.c | 8 +- drivers/media/usb/cx231xx/cx231xx-cards.c | 57 +++++- drivers/media/usb/cx231xx/cx231xx-core.c | 3 +- drivers/media/usb/cx231xx/cx231xx-dvb.c | 168 ++++++++++++++--- drivers/media/usb/cx231xx/cx231xx.h | 2 + include/media/tuner.h | 2 + 14 files changed, 461 insertions(+), 190 deletions(-) diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index c645aa8..8272c08 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -67,6 +67,7 @@ config DVB_TDA18271C2DD config DVB_SI2165 tristate "Silicon Labs si2165 based" depends on DVB_CORE && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C/T demodulator. diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 8bf716a..efbc43e 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -25,6 +25,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/firmware.h> +#include <linux/regmap.h> #include "dvb_frontend.h" #include "dvb_math.h" @@ -40,7 +41,9 @@ */ struct si2165_state { - struct i2c_adapter *i2c; + struct i2c_client *client; + + struct regmap *regmap; struct dvb_frontend fe; @@ -108,61 +111,27 @@ static int si2165_write(struct si2165_state *state, const u16 reg, const u8 *src, const int count) { int ret; - struct i2c_msg msg; - u8 buf[2 + 4]; /* write a maximum of 4 bytes of data */ - - if (count + 2 > sizeof(buf)) { - dev_warn(&state->i2c->dev, - "%s: i2c wr reg=%04x: count=%d is too big!\n", - KBUILD_MODNAME, reg, count); - return -EINVAL; - } - buf[0] = reg >> 8; - buf[1] = reg & 0xff; - memcpy(buf + 2, src, count); - - msg.addr = state->config.i2c_addr; - msg.flags = 0; - msg.buf = buf; - msg.len = count + 2; if (debug & DEBUG_I2C_WRITE) deb_i2c_write("reg: 0x%04x, data: %*ph\n", reg, count, src); - ret = i2c_transfer(state->i2c, &msg, 1); + ret = regmap_bulk_write(state->regmap, reg, src, count); - if (ret != 1) { - dev_err(&state->i2c->dev, "%s: ret == %d\n", __func__, ret); - if (ret < 0) - return ret; - else - return -EREMOTEIO; - } + if (ret) + dev_err(&state->client->dev, "%s: ret == %d\n", __func__, ret); - return 0; + return ret; } static int si2165_read(struct si2165_state *state, const u16 reg, u8 *val, const int count) { - int ret; - u8 reg_buf[] = { reg >> 8, reg & 0xff }; - struct i2c_msg msg[] = { - { .addr = state->config.i2c_addr, - .flags = 0, .buf = reg_buf, .len = 2 }, - { .addr = state->config.i2c_addr, - .flags = I2C_M_RD, .buf = val, .len = count }, - }; - - ret = i2c_transfer(state->i2c, msg, 2); + int ret = regmap_bulk_read(state->regmap, reg, val, count); - if (ret != 2) { - dev_err(&state->i2c->dev, "%s: error (addr %02x reg %04x error (ret == %i)\n", + if (ret) { + dev_err(&state->client->dev, "%s: error (addr %02x reg %04x error (ret == %i)\n", __func__, state->config.i2c_addr, reg, ret); - if (ret < 0) - return ret; - else - return -EREMOTEIO; + return ret; } if (debug & DEBUG_I2C_READ) @@ -174,9 +143,9 @@ static int si2165_read(struct si2165_state *state, static int si2165_readreg8(struct si2165_state *state, const u16 reg, u8 *val) { - int ret; - - ret = si2165_read(state, reg, val, 1); + unsigned int val_tmp; + int ret = regmap_read(state->regmap, reg, &val_tmp); + *val = (u8)val_tmp; deb_readreg("R(0x%04x)=0x%02x\n", reg, *val); return ret; } @@ -194,7 +163,7 @@ static int si2165_readreg16(struct si2165_state *state, static int si2165_writereg8(struct si2165_state *state, const u16 reg, u8 val) { - return si2165_write(state, reg, &val, 1); + return regmap_write(state->regmap, reg, val); } static int si2165_writereg16(struct si2165_state *state, const u16 reg, u16 val) @@ -206,20 +175,23 @@ static int si2165_writereg16(struct si2165_state *state, const u16 reg, u16 val) static int si2165_writereg24(struct si2165_state *state, const u16 reg, u32 val) { + int ret; u8 buf[3] = { val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff }; - return si2165_write(state, reg, buf, 3); + ret = si2165_write(state, reg, buf, 2); + if (ret == 0) + return si2165_write(state, reg+2, buf+2, 1); + return ret; } static int si2165_writereg32(struct si2165_state *state, const u16 reg, u32 val) { - u8 buf[4] = { - val & 0xff, - (val >> 8) & 0xff, - (val >> 16) & 0xff, - (val >> 24) & 0xff - }; - return si2165_write(state, reg, buf, 4); + int ret; + ret = si2165_writereg16(state, reg, val & 0xffff); + if (ret == 0) { + ret = si2165_writereg16(state, reg+2, (val >> 16) & 0xffff); + } + return ret; } static int si2165_writereg_mask8(struct si2165_state *state, const u16 reg, @@ -276,6 +248,7 @@ static int si2165_init_pll(struct si2165_state *state) u8 divm = 8; u8 divl = 12; u8 buf[4]; + int ret; /* * hardcoded values can be deleted if calculation is verified @@ -316,9 +289,18 @@ static int si2165_init_pll(struct si2165_state *state) /* write pll registers 0x00a0..0x00a3 at once */ buf[0] = divl; buf[1] = divm; + /* buf[2] = (divn & 0x3f) | ((divp == 1) ? 0x40 : 0x00) | 0x80; buf[3] = divr; return si2165_write(state, 0x00a0, buf, 4); + */ + ret = si2165_write(state, 0x00a0, buf, 2); + if (ret == 0) { + buf[0] = (divn & 0x3f) | ((divp == 1) ? 0x40 : 0x00) | 0x80; + buf[1] = divr; + ret = si2165_write(state, 0x00a2, buf, 2); + } + return ret; } static int si2165_adjust_pll_divl(struct si2165_state *state, u8 divl) @@ -345,7 +327,7 @@ static int si2165_wait_init_done(struct si2165_state *state) return 0; usleep_range(1000, 50000); } - dev_err(&state->i2c->dev, "%s: init_done was not set\n", + dev_err(&state->client->dev, "%s: init_done was not set\n", KBUILD_MODNAME); return ret; } @@ -374,14 +356,14 @@ static int si2165_upload_firmware_block(struct si2165_state *state, wordcount = data[offset]; if (wordcount < 1 || data[offset+1] || data[offset+2] || data[offset+3]) { - dev_warn(&state->i2c->dev, + dev_warn(&state->client->dev, "%s: bad fw data[0..3] = %*ph\n", KBUILD_MODNAME, 4, data); return -EINVAL; } if (offset + 8 + wordcount * 4 > len) { - dev_warn(&state->i2c->dev, + dev_warn(&state->client->dev, "%s: len is too small for block len=%d, wordcount=%d\n", KBUILD_MODNAME, len, wordcount); return -EINVAL; @@ -389,17 +371,26 @@ static int si2165_upload_firmware_block(struct si2165_state *state, buf_ctrl[0] = wordcount - 1; - ret = si2165_write(state, 0x0364, buf_ctrl, 4); + ret = si2165_write(state, 0x0364, buf_ctrl, 2); + if (ret < 0) + goto error; + ret = si2165_write(state, 0x0366, buf_ctrl+2, 2); if (ret < 0) goto error; - ret = si2165_write(state, 0x0368, data+offset+4, 4); + ret = si2165_write(state, 0x0368, data+offset+4, 2); + if (ret < 0) + goto error; + ret = si2165_write(state, 0x036a, data+offset+6, 2); if (ret < 0) goto error; offset += 8; while (wordcount > 0) { - ret = si2165_write(state, 0x36c, data+offset, 4); + ret = si2165_write(state, 0x36c, data+offset, 2); + if (ret < 0) + goto error; + ret = si2165_write(state, 0x36e, data+offset+2, 2); if (ret < 0) goto error; wordcount--; @@ -444,15 +435,15 @@ static int si2165_upload_firmware(struct si2165_state *state) fw_file = SI2165_FIRMWARE_REV_D; break; default: - dev_info(&state->i2c->dev, "%s: no firmware file for revision=%d\n", + dev_info(&state->client->dev, "%s: no firmware file for revision=%d\n", KBUILD_MODNAME, state->chip_revcode); return 0; } /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, state->i2c->dev.parent); + ret = request_firmware(&fw, fw_file, &state->client->dev); if (ret) { - dev_warn(&state->i2c->dev, "%s: firmware file '%s' not found\n", + dev_warn(&state->client->dev, "%s: firmware file '%s' not found\n", KBUILD_MODNAME, fw_file); goto error; } @@ -460,11 +451,11 @@ static int si2165_upload_firmware(struct si2165_state *state) data = fw->data; len = fw->size; - dev_info(&state->i2c->dev, "%s: downloading firmware from file '%s' size=%d\n", + dev_info(&state->client->dev, "%s: downloading firmware from file '%s' size=%d\n", KBUILD_MODNAME, fw_file, len); if (len % 4 != 0) { - dev_warn(&state->i2c->dev, "%s: firmware size is not multiple of 4\n", + dev_warn(&state->client->dev, "%s: firmware size is not multiple of 4\n", KBUILD_MODNAME); ret = -EINVAL; goto error; @@ -472,14 +463,14 @@ static int si2165_upload_firmware(struct si2165_state *state) /* check header (8 bytes) */ if (len < 8) { - dev_warn(&state->i2c->dev, "%s: firmware header is missing\n", + dev_warn(&state->client->dev, "%s: firmware header is missing\n", KBUILD_MODNAME); ret = -EINVAL; goto error; } if (data[0] != 1 || data[1] != 0) { - dev_warn(&state->i2c->dev, "%s: firmware file version is wrong\n", + dev_warn(&state->client->dev, "%s: firmware file version is wrong\n", KBUILD_MODNAME); ret = -EINVAL; goto error; @@ -517,7 +508,7 @@ static int si2165_upload_firmware(struct si2165_state *state) /* start right after the header */ offset = 8; - dev_info(&state->i2c->dev, "%s: si2165_upload_firmware extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n", + dev_info(&state->client->dev, "%s: si2165_upload_firmware extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n", KBUILD_MODNAME, patch_version, block_count, crc_expected); ret = si2165_upload_firmware_block(state, data, len, &offset, 1); @@ -536,7 +527,7 @@ static int si2165_upload_firmware(struct si2165_state *state) ret = si2165_upload_firmware_block(state, data, len, &offset, block_count); if (ret < 0) { - dev_err(&state->i2c->dev, + dev_err(&state->client->dev, "%s: firmware could not be uploaded\n", KBUILD_MODNAME); goto error; @@ -548,7 +539,7 @@ static int si2165_upload_firmware(struct si2165_state *state) goto error; if (val16 != crc_expected) { - dev_err(&state->i2c->dev, + dev_err(&state->client->dev, "%s: firmware crc mismatch %04x != %04x\n", KBUILD_MODNAME, val16, crc_expected); ret = -EINVAL; @@ -560,7 +551,7 @@ static int si2165_upload_firmware(struct si2165_state *state) goto error; if (len != offset) { - dev_err(&state->i2c->dev, + dev_err(&state->client->dev, "%s: firmware len mismatch %04x != %04x\n", KBUILD_MODNAME, len, offset); ret = -EINVAL; @@ -577,7 +568,7 @@ static int si2165_upload_firmware(struct si2165_state *state) if (ret < 0) goto error; - dev_info(&state->i2c->dev, "%s: fw load finished\n", KBUILD_MODNAME); + dev_info(&state->client->dev, "%s: fw load finished\n", KBUILD_MODNAME); ret = 0; state->firmware_loaded = true; @@ -611,7 +602,7 @@ static int si2165_init(struct dvb_frontend *fe) if (ret < 0) goto error; if (val != state->config.chip_mode) { - dev_err(&state->i2c->dev, "%s: could not set chip_mode\n", + dev_err(&state->client->dev, "%s: could not set chip_mode\n", KBUILD_MODNAME); return -EINVAL; } @@ -672,9 +663,13 @@ static int si2165_init(struct dvb_frontend *fe) goto error; /* dsp_addr_jump */ - ret = si2165_writereg32(state, 0x0348, 0xf4000000); + ret = si2165_writereg16(state, 0x0348, 0x0000); + if (ret < 0) + goto error; + ret = si2165_writereg16(state, 0x034a, 0xf400); if (ret < 0) goto error; + /* boot/wdog status */ ret = si2165_readreg8(state, 0x0341, &val); if (ret < 0) @@ -751,6 +746,9 @@ static int si2165_set_oversamp(struct si2165_state *state, u32 dvb_rate) u64 oversamp; u32 reg_value; + if (!dvb_rate) + return -EINVAL; + oversamp = si2165_get_fe_clk(state); oversamp <<= 23; do_div(oversamp, dvb_rate); @@ -769,12 +767,15 @@ static int si2165_set_if_freq_shift(struct si2165_state *state) u32 IF = 0; if (!fe->ops.tuner_ops.get_if_frequency) { - dev_err(&state->i2c->dev, + dev_err(&state->client->dev, "%s: Error: get_if_frequency() not defined at tuner. Can't work without it!\n", KBUILD_MODNAME); return -EINVAL; } + if (!fe_clk) + return -EINVAL; + fe->ops.tuner_ops.get_if_frequency(fe, &IF); if_freq_shift = IF; if_freq_shift <<= 29; @@ -1003,14 +1004,6 @@ static int si2165_set_frontend(struct dvb_frontend *fe) return 0; } -static void si2165_release(struct dvb_frontend *fe) -{ - struct si2165_state *state = fe->demodulator_priv; - - dprintk("%s: called\n", __func__); - kfree(state); -} - static struct dvb_frontend_ops si2165_ops = { .info = { .name = "Silicon Labs ", @@ -1046,67 +1039,83 @@ static struct dvb_frontend_ops si2165_ops = { .set_frontend = si2165_set_frontend, .read_status = si2165_read_status, - - .release = si2165_release, }; -struct dvb_frontend *si2165_attach(const struct si2165_config *config, - struct i2c_adapter *i2c) +static int si2165_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct si2165_state *state = NULL; + struct si2165_platform_data *pdata = client->dev.platform_data; int n; - int io_ret; + int ret = 0; u8 val; char rev_char; const char *chip_name; - - if (config == NULL || i2c == NULL) - goto error; + static const struct regmap_config regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x08ff, + }; /* allocate memory for the internal state */ state = kzalloc(sizeof(struct si2165_state), GFP_KERNEL); - if (state == NULL) + if (state == NULL) { + ret = -ENOMEM; goto error; + } + + /* create regmap */ + state->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(state->regmap)) { + ret = PTR_ERR(state->regmap); + goto error; + } /* setup the state */ - state->i2c = i2c; - state->config = *config; + state->client = client; + state->config.i2c_addr = client->addr; + state->config.chip_mode = pdata->chip_mode; + state->config.ref_freq_Hz = pdata->ref_freq_Hz; + state->config.inversion = pdata->inversion; if (state->config.ref_freq_Hz < 4000000 || state->config.ref_freq_Hz > 27000000) { - dev_err(&state->i2c->dev, "%s: ref_freq of %d Hz not supported by this driver\n", + dev_err(&state->client->dev, "%s: ref_freq of %d Hz not supported by this driver\n", KBUILD_MODNAME, state->config.ref_freq_Hz); + ret = -EINVAL; goto error; } /* create dvb_frontend */ memcpy(&state->fe.ops, &si2165_ops, sizeof(struct dvb_frontend_ops)); + state->fe.ops.release = NULL; state->fe.demodulator_priv = state; + i2c_set_clientdata(client, state); /* powerup */ - io_ret = si2165_writereg8(state, 0x0000, state->config.chip_mode); - if (io_ret < 0) - goto error; + ret = si2165_writereg8(state, 0x0000, state->config.chip_mode); + if (ret < 0) + goto nodev_error; - io_ret = si2165_readreg8(state, 0x0000, &val); - if (io_ret < 0) - goto error; + ret = si2165_readreg8(state, 0x0000, &val); + if (ret < 0) + goto nodev_error; if (val != state->config.chip_mode) - goto error; + goto nodev_error; - io_ret = si2165_readreg8(state, 0x0023, &state->chip_revcode); - if (io_ret < 0) - goto error; + ret = si2165_readreg8(state, 0x0023, &state->chip_revcode); + if (ret < 0) + goto nodev_error; - io_ret = si2165_readreg8(state, 0x0118, &state->chip_type); - if (io_ret < 0) - goto error; + ret = si2165_readreg8(state, 0x0118, &state->chip_type); + if (ret < 0) + goto nodev_error; /* powerdown */ - io_ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF); - if (io_ret < 0) - goto error; + ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF); + if (ret < 0) + goto nodev_error; if (state->chip_revcode < 26) rev_char = 'A' + state->chip_revcode; @@ -1124,12 +1133,12 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config, state->has_dvbc = true; break; default: - dev_err(&state->i2c->dev, "%s: Unsupported Silicon Labs chip (type %d, rev %d)\n", + dev_err(&state->client->dev, "%s: Unsupported Silicon Labs chip (type %d, rev %d)\n", KBUILD_MODNAME, state->chip_type, state->chip_revcode); - goto error; + goto nodev_error; } - dev_info(&state->i2c->dev, + dev_info(&state->client->dev, "%s: Detected Silicon Labs %s-%c (type %d, rev %d)\n", KBUILD_MODNAME, chip_name, rev_char, state->chip_type, state->chip_revcode); @@ -1149,13 +1158,46 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config, sizeof(state->fe.ops.info.name)); } - return &state->fe; + /* return fe pointer */ + *pdata->fe = &state->fe; + return 0; + +nodev_error: + ret = -ENODEV; error: kfree(state); - return NULL; + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int si2165_remove(struct i2c_client *client) +{ + struct si2165_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + kfree(state); + return 0; } -EXPORT_SYMBOL(si2165_attach); + +static const struct i2c_device_id si2165_id_table[] = { + {"si2165", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, si2165_id_table); + +static struct i2c_driver si2165_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "si2165", + }, + .probe = si2165_probe, + .remove = si2165_remove, + .id_table = si2165_id_table, +}; + +module_i2c_driver(si2165_driver); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/si2165.h b/drivers/media/dvb-frontends/si2165.h index 8a15d6a..76c2ca7 100644 --- a/drivers/media/dvb-frontends/si2165.h +++ b/drivers/media/dvb-frontends/si2165.h @@ -28,10 +28,15 @@ enum { SI2165_MODE_PLL_XTAL = 0x21 }; -struct si2165_config { - /* i2c addr - * possible values: 0x64,0x65,0x66,0x67 */ - u8 i2c_addr; +/* I2C addresses + * possible values: 0x64,0x65,0x66,0x67 + */ +struct si2165_platform_data { + /* + * frontend + * returned by driver + */ + struct dvb_frontend **fe; /* external clock or XTAL */ u8 chip_mode; @@ -45,18 +50,4 @@ struct si2165_config { bool inversion; }; -#if IS_REACHABLE(CONFIG_DVB_SI2165) -struct dvb_frontend *si2165_attach( - const struct si2165_config *config, - struct i2c_adapter *i2c); -#else -static inline struct dvb_frontend *si2165_attach( - const struct si2165_config *config, - struct i2c_adapter *i2c) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif /* CONFIG_DVB_SI2165 */ - #endif /* _DVB_SI2165_H */ diff --git a/drivers/media/dvb-frontends/si2165_priv.h b/drivers/media/dvb-frontends/si2165_priv.h index 2b70cf1..e593211 100644 --- a/drivers/media/dvb-frontends/si2165_priv.h +++ b/drivers/media/dvb-frontends/si2165_priv.h @@ -20,4 +20,21 @@ #define SI2165_FIRMWARE_REV_D "dvb-demod-si2165.fw" +struct si2165_config { + /* i2c addr + * possible values: 0x64,0x65,0x66,0x67 */ + u8 i2c_addr; + + /* external clock or XTAL */ + u8 chip_mode; + + /* frequency of external clock or xtal in Hz + * possible values: 4000000, 16000000, 20000000, 240000000, 27000000 + */ + u32 ref_freq_Hz; + + /* invert the spectrum */ + bool inversion; +}; + #endif /* _DVB_SI2165_PRIV */ diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index e5748a9..5d0bbe4 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -867,12 +867,6 @@ static const struct tda10071_platform_data hauppauge_tda10071_pdata = { .tuner_i2c_addr = 0x54, }; -static const struct si2165_config hauppauge_hvr4400_si2165_config = { - .i2c_addr = 0x64, - .chip_mode = SI2165_MODE_PLL_XTAL, - .ref_freq_Hz = 16000000, -}; - static const struct m88ds3103_config dvbsky_t9580_m88ds3103_config = { .i2c_addr = 0x68, .clock = 27000000, @@ -1182,6 +1176,7 @@ static int dvb_register(struct cx23885_tsport *port) struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL; struct vb2_dvb_frontend *fe0, *fe1 = NULL; struct si2168_config si2168_config; + struct si2165_platform_data si2165_pdata; struct si2157_config si2157_config; struct ts2020_config ts2020_config; struct i2c_board_info info; @@ -1839,9 +1834,26 @@ static int dvb_register(struct cx23885_tsport *port) break; /* port c */ case 2: - fe0->dvb.frontend = dvb_attach(si2165_attach, - &hauppauge_hvr4400_si2165_config, - &i2c_bus->i2c_adap); + /* attach frontend */ + memset(&si2165_pdata, 0, sizeof(si2165_pdata)); + si2165_pdata.fe = &fe0->dvb.frontend; + si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL, + si2165_pdata.ref_freq_Hz = 16000000, + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2165", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2165_pdata; + request_module(info.type); + client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); + if (client_demod == NULL || + client_demod->dev.driver == NULL) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + if (fe0->dvb.frontend == NULL) break; fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 57b2508..39fb443 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -80,7 +80,7 @@ static int si2157_init(struct dvb_frontend *fe) struct i2c_client *client = fe->tuner_priv; struct si2157_dev *dev = i2c_get_clientdata(client); struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, len, remaining; + int ret, len, remaining, fw_chunk, inc; struct si2157_cmd cmd; const struct firmware *fw; const char *fw_name; @@ -103,12 +103,12 @@ static int si2157_init(struct dvb_frontend *fe) goto warm; /* power up */ - if (dev->chiptype == SI2157_CHIPTYPE_SI2146) { - memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9); - cmd.wlen = 9; - } else { + if (dev->chiptype == SI2157_CHIPTYPE_SI2157) { memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); cmd.wlen = 15; + } else { + memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9); + cmd.wlen = 9; } cmd.rlen = 1; ret = si2157_cmd_execute(client, &cmd); @@ -131,11 +131,17 @@ static int si2157_init(struct dvb_frontend *fe) #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) #define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0) #define SI2146_A10 ('A' << 24 | 46 << 16 | '1' << 8 | '0' << 0) + #define SI2173_A31 ('A' << 24 | 73 << 16 | '3' << 8 | '1' << 0) switch (chip_id) { case SI2158_A20: case SI2148_A20: fw_name = SI2158_A20_FIRMWARE; + fw_chunk = 17; + break; + case SI2173_A31: + fw_name = SI2173_A31_FIRMWARE; + fw_chunk = 8; break; case SI2157_A30: case SI2147_A30: @@ -164,25 +170,29 @@ static int si2157_init(struct dvb_frontend *fe) goto err; } - /* firmware should be n chunks of 17 bytes */ - if (fw->size % 17 != 0) { + /* firmware should be n chunks of 8 or 17 bytes */ + if (fw->size % fw_chunk != 0) { dev_err(&client->dev, "firmware file '%s' is invalid\n", fw_name); ret = -EINVAL; goto err_release_firmware; } - dev_info(&client->dev, "downloading firmware from file '%s'\n", - fw_name); + dev_info(&client->dev, "downloading firmware from file '%s', size %zu bytes\n", + fw_name, fw->size); - for (remaining = fw->size; remaining > 0; remaining -= 17) { - len = fw->data[fw->size - remaining]; + if (fw_chunk == 8) + inc = 0; + else + inc = 1; + for (remaining = fw->size; remaining > 0; remaining -= fw_chunk) { + len = fw_chunk == 8 ? 8 : fw->data[fw->size - remaining]; if (len > SI2157_ARGLEN) { dev_err(&client->dev, "Bad firmware length\n"); ret = -EINVAL; goto err_release_firmware; } - memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); + memcpy(cmd.args, &fw->data[(fw->size - remaining) + inc], len); cmd.wlen = len; cmd.rlen = 1; ret = si2157_cmd_execute(client, &cmd); @@ -371,7 +381,7 @@ static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops si2157_ops = { .info = { - .name = "Silicon Labs Si2146/2147/2148/2157/2158", + .name = "Silicon Labs Si2146/2147/2148/2157/2158/2173", .frequency_min = 42000000, .frequency_max = 870000000, }, @@ -395,7 +405,11 @@ static void si2157_stat_work(struct work_struct *work) memcpy(cmd.args, "\x42\x00", 2); cmd.wlen = 2; - cmd.rlen = 12; + if (dev->chiptype == SI2157_CHIPTYPE_SI2173) { + cmd.rlen = 9; + } else { + cmd.rlen = 12; + } ret = si2157_cmd_execute(client, &cmd); if (ret) goto err; @@ -470,9 +484,13 @@ static int si2157_probe(struct i2c_client *client, } #endif - dev_info(&client->dev, "Silicon Labs %s successfully attached\n", + dev_info(&client->dev, "Silicon Labs %s (chiptype %d) successfully attached\n", dev->chiptype == SI2157_CHIPTYPE_SI2146 ? - "Si2146" : "Si2147/2148/2157/2158"); + "Si2146" : + dev->chiptype == SI2157_CHIPTYPE_SI2173 ? + "Si2173" : + "Si2147/2148/2157/2158", + dev->chiptype); return 0; @@ -506,6 +524,7 @@ static int si2157_remove(struct i2c_client *client) } static const struct i2c_device_id si2157_id_table[] = { + {"si2173", SI2157_CHIPTYPE_SI2173}, {"si2157", SI2157_CHIPTYPE_SI2157}, {"si2146", SI2157_CHIPTYPE_SI2146}, {} @@ -528,3 +547,4 @@ MODULE_DESCRIPTION("Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(SI2158_A20_FIRMWARE); +MODULE_FIRMWARE(SI2173_A31_FIRMWARE); diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index d6b2c7b..f1a3f69 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -42,6 +42,7 @@ struct si2157_dev { #define SI2157_CHIPTYPE_SI2157 0 #define SI2157_CHIPTYPE_SI2146 1 +#define SI2157_CHIPTYPE_SI2173 2 /* firmware command struct */ #define SI2157_ARGLEN 30 @@ -52,5 +53,6 @@ struct si2157_cmd { }; #define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw" +#define SI2173_A31_FIRMWARE "terratec_cinergy_htc_stick_hd0101.fw" #endif diff --git a/drivers/media/tuners/tuner-types.c b/drivers/media/tuners/tuner-types.c index 98bc15a..0dc57b1 100644 --- a/drivers/media/tuners/tuner-types.c +++ b/drivers/media/tuners/tuner-types.c @@ -1941,6 +1941,10 @@ struct tunertype tuners[] = { .params = tuner_sony_btf_pg463z_params, .count = ARRAY_SIZE(tuner_sony_btf_pg463z_params), }, + [TUNER_SI2173] = { /* Silicon Labs 2173 */ + .name = "Silicon Labs 2173", + }, + }; EXPORT_SYMBOL(tuners); diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index 4919137..d408354 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -360,6 +360,7 @@ int cx231xx_afe_update_power_control(struct cx231xx *dev, case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: case CX231XX_BOARD_OTG102: + case CX231XX_BOARD_TERRATEC_CNRG_HTC_HD: if (avmode == POLARIS_AVMODE_ANALOGT_TV) { while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | FLD_PWRDN_ENABLE_PLL)) { @@ -599,7 +600,8 @@ int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input) return status; } } - if (dev->tuner_type == TUNER_NXP_TDA18271) + if ((dev->tuner_type == TUNER_NXP_TDA18271) || + (dev->tuner_type == TUNER_SI2173)) status = cx231xx_set_decoder_video_input(dev, CX231XX_VMUX_TELEVISION, INPUT(input)->vmux); @@ -906,7 +908,8 @@ int cx231xx_set_decoder_video_input(struct cx231xx *dev, status = vid_blk_write_word(dev, AFE_CTRL, value); - if (dev->tuner_type == TUNER_NXP_TDA18271) { + if ((dev->tuner_type == TUNER_NXP_TDA18271) || + (dev->tuner_type == TUNER_SI2173)) { status = vid_blk_read_word(dev, PIN_CTRL, &value); status = vid_blk_write_word(dev, PIN_CTRL, @@ -1197,6 +1200,7 @@ int cx231xx_set_audio_decoder_input(struct cx231xx *dev, cx231xx_set_field(FLD_SIF_EN, 1)); break; case TUNER_NXP_TDA18271: + case TUNER_SI2173: /* Normal mode: SIF passthrough at 14.32 MHz */ status = cx231xx_read_modify_write_i2c_dword(dev, VID_BLK_I2C_ADDRESS, diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index c63248a..8c851ab 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -815,6 +815,52 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_TERRATEC_CNRG_HTC_HD] = { + .name = "Terratec Cinergy HTC Stick HD", + .tuner_type = TUNER_ABSENT, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .tuner_i2c_master = I2C_1_MUX_3, + .demod_addr = 0x64, + .demod_i2c_master = I2C_2, + .has_dvb = 1, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x1c, + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_PAL, + .no_alt_vanc = 1, + .external_av = 1, + /* Actually, it has a 417, but it isn't working correctly. + * So set to 0 for now until someone can manage to get this + * to work reliably. */ + .has_417 = 0, + .gpio_pin_status_mask = 0x4001000, + .agc_analog_digital_select_gpio = 0x1c, + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, [CX231XX_BOARD_TERRATEC_GRABBY] = { .name = "Terratec Grabby", .tuner_type = TUNER_ABSENT, @@ -864,6 +910,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_CNXT_RDE_250}, {USB_DEVICE(0x0572, 0x58A0), .driver_info = CX231XX_BOARD_CNXT_RDU_250}, + {USB_DEVICE(0x0ccd, 0x0101), + .driver_info = CX231XX_BOARD_TERRATEC_CNRG_HTC_HD}, {USB_DEVICE(0x2040, 0xb110), .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL}, {USB_DEVICE(0x2040, 0xb111), @@ -937,6 +985,11 @@ int cx231xx_tuner_callback(void *ptr, int component, int command, int arg) 1); msleep(10); } + } else if (dev->tuner_type == TUNER_SI2173) { + dev_dbg(dev->dev, + "Tuner CB: RESET: cmd %d : tuner type %d\n", + command, dev->tuner_type); + rc = cx231xx_set_agc_analog_digital_mux_select(dev, arg); } else if (dev->tuner_type == TUNER_NXP_TDA18271) { switch (command) { case TDA18271_CALLBACK_CMD_AGC_ENABLE: @@ -1263,6 +1316,7 @@ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev, /*To workaround error number=-71 on EP0 for VideoGrabber, need set alt here.*/ if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER || + dev->model == CX231XX_BOARD_TERRATEC_CNRG_HTC_HD || dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) { cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3); cx231xx_set_alt_setting(dev, INDEX_VANC, 1); @@ -1674,7 +1728,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } } - if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) { + if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER || + dev->model == CX231XX_BOARD_TERRATEC_CNRG_HTC_HD) { cx231xx_enable_OSC(dev); cx231xx_reset_out(dev); cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3); diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 630f4fc..b942fe4 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -52,7 +52,7 @@ MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); #define cx231xx_isocdbg(fmt, arg...) do {\ if (core_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ + printk(KERN_INFO "%s %s :"fmt" ", \ dev->name, __func__ , ##arg); } while (0) /***************************************************************** @@ -272,6 +272,7 @@ static int __usb_control_msg(struct cx231xx *dev, unsigned int pipe, if (reg_debug) { if (unlikely(rc < 0)) { printk(KERN_CONT "FAILED!\n"); + dump_stack(); return rc; } diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index ab2fb9f..a890be3 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -65,6 +65,7 @@ struct cx231xx_dvb { struct dmx_frontend fe_hw; struct dmx_frontend fe_mem; struct dvb_net net; + struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; }; @@ -150,18 +151,6 @@ static struct tda18271_config pv_tda18271_config = { .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, }; -static const struct si2165_config hauppauge_930C_HD_1113xx_si2165_config = { - .i2c_addr = 0x64, - .chip_mode = SI2165_MODE_PLL_XTAL, - .ref_freq_Hz = 16000000, -}; - -static const struct si2165_config pctv_quatro_stick_1114xx_si2165_config = { - .i2c_addr = 0x64, - .chip_mode = SI2165_MODE_PLL_EXT, - .ref_freq_Hz = 24000000, -}; - static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { .i2c_addr = 0x59, .qam_if_khz = 4000, @@ -586,8 +575,14 @@ static void unregister_dvb(struct cx231xx_dvb *dvb) dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); dvb_dmxdev_release(&dvb->dmxdev); dvb_dmx_release(&dvb->demux); - client = dvb->i2c_client_tuner; /* remove I2C tuner */ + client = dvb->i2c_client_tuner; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + /* remove I2C demod */ + client = dvb->i2c_client_demod; if (client) { module_put(client->dev.driver->owner); i2c_unregister_device(client); @@ -749,19 +744,38 @@ static int dvb_init(struct cx231xx *dev) break; case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: + { + struct i2c_client *client; + struct i2c_board_info info; + struct si2165_platform_data si2165_pdata; - dev->dvb->frontend = dvb_attach(si2165_attach, - &hauppauge_930C_HD_1113xx_si2165_config, - demod_i2c - ); + /* attach demod */ + memset(&si2165_pdata, 0, sizeof(si2165_pdata)); + si2165_pdata.fe = &dev->dvb->frontend; + si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL, + si2165_pdata.ref_freq_Hz = 16000000, - if (dev->dvb->frontend == NULL) { + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2165", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2165_pdata; + request_module(info.type); + client = i2c_new_device(demod_i2c, &info); + if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) { dev_err(dev->dev, "Failed to attach SI2165 front end\n"); result = -EINVAL; goto out_free; } + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod = client; + dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ @@ -774,27 +788,131 @@ static int dvb_init(struct cx231xx *dev) dev->cx231xx_reset_analog_tuner = NULL; break; + } + + case CX231XX_BOARD_TERRATEC_CNRG_HTC_HD: + { + struct i2c_client *client; + struct i2c_board_info info; + struct si2165_platform_data si2165_pdata; + struct si2157_config si2157_config; + + /* attach demod */ + dev_info(dev->dev, + "%s: attach demod\n", + __func__); + memset(&si2165_pdata, 0, sizeof(si2165_pdata)); + si2165_pdata.fe = &dev->dvb->frontend; + si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT, + si2165_pdata.ref_freq_Hz = 24000000, + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2165", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2165_pdata; + request_module(info.type); + client = i2c_new_device(demod_i2c, &info); + if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) { + dev_err(dev->dev, + "Failed to attach SI2165 front end\n"); + result = -EINVAL; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + dev_info(dev->dev, + "%s: demod attached\n", + __func__); + + dvb->i2c_client_demod = client; + memset(&info, 0, sizeof(struct i2c_board_info)); + + dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + /* attach tuner */ + dev_info(dev->dev, + "%s: attach tuner\n", + __func__); + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = dev->dvb->frontend; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + si2157_config.if_port = 0; + si2157_config.inversion = true; + strlcpy(info.type, "si2173", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module("si2157"); + + client = i2c_new_device( + tuner_i2c, + &info); + if (client == NULL || client->dev.driver == NULL) { + dvb_frontend_detach(dev->dvb->frontend); + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + dvb_frontend_detach(dev->dvb->frontend); + result = -ENODEV; + goto out_free; + } + dev_info(dev->dev, + "%s: tuner attached\n", + __func__); + + dev->cx231xx_reset_analog_tuner = NULL; + + dev->dvb->i2c_client_tuner = client; + break; + } case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: { struct i2c_client *client; struct i2c_board_info info; + struct si2165_platform_data si2165_pdata; struct si2157_config si2157_config; - memset(&info, 0, sizeof(struct i2c_board_info)); + /* attach demod */ + memset(&si2165_pdata, 0, sizeof(si2165_pdata)); + si2165_pdata.fe = &dev->dvb->frontend; + si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT, + si2165_pdata.ref_freq_Hz = 24000000, - dev->dvb->frontend = dvb_attach(si2165_attach, - &pctv_quatro_stick_1114xx_si2165_config, - demod_i2c - ); - - if (dev->dvb->frontend == NULL) { + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2165", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2165_pdata; + request_module(info.type); + client = i2c_new_device(demod_i2c, &info); + if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) { dev_err(dev->dev, "Failed to attach SI2165 front end\n"); result = -EINVAL; goto out_free; } + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod = client; + + memset(&info, 0, sizeof(struct i2c_board_info)); + dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index 90c8676..603023d 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -78,6 +78,8 @@ #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx 20 #define CX231XX_BOARD_HAUPPAUGE_955Q 21 #define CX231XX_BOARD_TERRATEC_GRABBY 22 +#define CX231XX_BOARD_TERRATEC_CNRG_HTC_HD 23 + /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 diff --git a/include/media/tuner.h b/include/media/tuner.h index b3edc14..975fb89 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -141,6 +141,8 @@ #define TUNER_SONY_BTF_PG472Z 89 /* PAL+SECAM */ #define TUNER_SONY_BTF_PK467Z 90 /* NTSC_JP */ #define TUNER_SONY_BTF_PB463Z 91 /* NTSC */ +#define TUNER_SI2173 92 /* Silicon Labs 2173 */ + /* tv card specific */ #define TDA9887_PRESENT (1<<0) -- 2.1.4 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2016-08-06 15:03 ` Gerard H. Pille @ 2016-08-06 22:00 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-08-06 22:00 UTC (permalink / raw) To: Gerard H. Pille; +Cc: kbuild-all, linux-media, Gerard H. Pille drivers/media/dvb-frontends/si2165.c:1192:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Gerard H. Pille <g.h.p@skynet.be> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- si2165.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -1189,7 +1189,6 @@ MODULE_DEVICE_TABLE(i2c, si2165_id_table static struct i2c_driver si2165_driver = { .driver = { - .owner = THIS_MODULE, .name = "si2165", }, .probe = si2165_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH] ALSA SoC MAX98927 driver - Initial release
2016-11-23 4:57 ` Ryan Lee
@ 2016-11-23 18:13 kbuild test robot
2016-11-23 4:57 ` Ryan Lee
0 siblings, 1 reply; 40+ messages in thread
From: kbuild test robot @ 2016-11-23 18:13 UTC (permalink / raw)
To: Ryan Lee
Cc: kbuild-all, lgirdwood, broonie, robh+dt, mark.rutland, perex,
tiwai, arnd, michael, oder_chiou, yesanishhere, jacob,
Damien.Horsley, bardliao, kuninori.morimoto.gx, petr, lars, nh6z,
ryans.lee, alsa-devel, devicetree, linux-kernel
Hi Ryan,
[auto build test WARNING on asoc/for-next]
[also build test WARNING on v4.9-rc6 next-20161123]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Ryan-Lee/ALSA-SoC-MAX98927-driver-Initial-release/20161124-004840
base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
coccinelle warnings: (new ones prefixed by >>)
>> sound/soc/codecs/max98927.c:941:3-8: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] ALSA SoC MAX98927 driver - Initial release @ 2016-11-23 4:57 ` Ryan Lee 2016-11-23 18:13 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: Ryan Lee @ 2016-11-23 4:57 UTC (permalink / raw) To: lgirdwood, broonie, robh+dt, mark.rutland, perex, tiwai, arnd, michael, oder_chiou, yesanishhere, jacob, Damien.Horsley, bardliao, kuninori.morimoto.gx, petr, lars, nh6z, ryans.lee, alsa-devel, devicetree, linux-kernel Cc: Ryan Lee Signed-off-by: Ryan Lee <ryans.lee@maximintegrated.com> --- .../devicetree/bindings/sound/max98927.txt | 32 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/max98927.c | 954 +++++++++++++++ sound/soc/codecs/max98927.h | 1253 ++++++++++++++++++++ 5 files changed, 2246 insertions(+) create mode 100755 Documentation/devicetree/bindings/sound/max98927.txt mode change 100644 => 100755 sound/soc/codecs/Kconfig mode change 100644 => 100755 sound/soc/codecs/Makefile create mode 100755 sound/soc/codecs/max98927.c create mode 100755 sound/soc/codecs/max98927.h diff --git a/Documentation/devicetree/bindings/sound/max98927.txt b/Documentation/devicetree/bindings/sound/max98927.txt new file mode 100755 index 0000000..ddcd332 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/max98927.txt @@ -0,0 +1,32 @@ +max98927 audio CODEC + +This device supports I2C. + +Required properties: + + - compatible : "maxim,max98927" + + - vmon-slot-no : slot number used to send voltage information + or in inteleave mode this will be used as + interleave slot. + + - imon-slot-no : slot number used to send current information + + - interleave-mode : When using two MAX98927 in a system it is + possible to create ADC data that that will + overflow the frame size. Digital Audio Interleave + mode provides a means to output VMON and IMON data + from two devices on a single DOUT line when running + smaller frames sizes such as 32 BCLKS per LRCLK or + 48 BCLKS per LRCLK. + + - reg : the I2C address of the device for I2C + +Example: + +codec: max98927@3a { + compatible = "maxim,max98927"; + vmon-slot-no = <1>; + imon-slot-no = <0>; + reg = <0x3a>; +}; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig old mode 100644 new mode 100755 index c67667b..45f21ca --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -86,6 +86,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9867 if I2C select SND_SOC_MAX98925 if I2C select SND_SOC_MAX98926 if I2C + select SND_SOC_MAX98927 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9860 if I2C select SND_SOC_MAX9768 if I2C @@ -573,6 +574,10 @@ config SND_SOC_MAX98925 config SND_SOC_MAX98926 tristate +config SND_SOC_MAX98927 + tristate "Maxim Integrated MAX98927 Speaker Amplifier" + depends on I2C + config SND_SOC_MAX9850 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile old mode 100644 new mode 100755 index 958cd49..1f5fe2c --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -82,6 +82,7 @@ snd-soc-max98371-objs := max98371.o snd-soc-max9867-objs := max9867.o snd-soc-max98925-objs := max98925.o snd-soc-max98926-objs := max98926.o +snd-soc-max98927-objs := max98927.o snd-soc-max9850-objs := max9850.o snd-soc-max9860-objs := max9860.o snd-soc-mc13783-objs := mc13783.o @@ -306,6 +307,7 @@ obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o +obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c new file mode 100755 index 0000000..d85c84f --- /dev/null +++ b/sound/soc/codecs/max98927.c @@ -0,0 +1,954 @@ +/* + * max98927.c -- MAX98927 ALSA Soc Audio driver + * + * Copyright 2013-15 Maxim Integrated Products + * Author: Ryan Lee <ryans.lee@maximintegrated.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <sound/tlv.h> +#include "max98927.h" + +static struct reg_default max98927_reg_map[] = { + {0x0014, 0x78}, + {0x0015, 0xFF}, + {0x0043, 0x04}, + {0x0017, 0x55}, + /* For mono driver we are just enabling one channel*/ + {MAX98927_PCM_Rx_Enables_A, 0x03}, + {MAX98927_PCM_Tx_HiZ_Control_A, 0xfc}, + {MAX98927_PCM_Tx_HiZ_Control_B, 0xff}, + {MAX98927_PCM_Tx_Channel_Sources_A, 0x01}, + {MAX98927_PCM_Tx_Channel_Sources_B, 0x01}, + {MAX98927_Measurement_DSP_Config, 0xf7}, + {0x0025, 0x80}, + {0x0026, 0x01}, + {0x0035, 0x40}, + {0x0036, 0x40}, + {0x0037, 0x02}, + {0x0039, 0x01}, + {0x003c, 0x44}, + {0x003d, 0x04}, + {0x0040, 0x10}, + {0x0042, 0x3f}, + {0x0044, 0x00}, + {0x0045, 0x24}, + {0x007f, 0x06}, + {0x0087, 0x1c}, + {0x0089, 0x03}, + {0x009f, 0x01}, +}; + +void max98927_wrapper_write(struct max98927_priv *max98927, + unsigned int reg, unsigned int val) +{ + if (max98927->regmap) + regmap_write(max98927->regmap, reg, val); + if (max98927->sub_regmap) + regmap_write(max98927->sub_regmap, reg, val); +} + +void max98927_wrap_update_bits(struct max98927_priv *max98927, + unsigned int reg, unsigned int mask, unsigned int val) +{ + if (max98927->regmap) + regmap_update_bits(max98927->regmap, reg, mask, val); + if (max98927->sub_regmap) + regmap_update_bits(max98927->sub_regmap, reg, mask, val); +} + +static int max98927_reg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, unsigned int reg, + unsigned int mask, unsigned int shift) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + int data; + + regmap_read(max98927->regmap, reg, &data); + ucontrol->value.integer.value[0] = + (data & mask) >> shift; + return 0; +} + +static int max98927_reg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, unsigned int reg, + unsigned int mask, unsigned int shift) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + unsigned int sel = ucontrol->value.integer.value[0]; + + max98927_wrap_update_bits(max98927, reg, mask, sel << shift); + dev_dbg(codec->dev, "%s: register 0x%02X, value 0x%02X\n", + __func__, reg, sel); + return 0; +} + +static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + unsigned int invert = 0; + + dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_SLAVE); + break; + case SND_SOC_DAIFMT_CBM_CFM: + max98927->master = true; + max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_MASTER); + break; + case SND_SOC_DAIFMT_CBS_CFM: + max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_HYBRID); + default: + dev_err(codec->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98927_PCM_Mode_Config_PCM_BCLKEDGE; + break; + default: + dev_err(codec->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + max98927->iface |= SND_SOC_DAIFMT_I2S; + max98927_wrap_update_bits(max98927, + MAX98927_PCM_Mode_Config, + max98927->iface, max98927->iface); + break; + case SND_SOC_DAIFMT_LEFT_J: + max98927->iface |= SND_SOC_DAIFMT_LEFT_J; + max98927_wrap_update_bits(max98927, + MAX98927_PCM_Mode_Config, + max98927->iface, max98927->iface); + break; + default: + return -EINVAL; + } + + /* pcm channel configuration */ + if (max98927->iface & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) { + max98927_wrapper_write(max98927, + MAX98927_PCM_Rx_Enables_A, + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH0_EN| + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH1_EN); + max98927_wrapper_write(max98927, + MAX98927_PCM_Tx_Enables_A, + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH0_EN| + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH1_EN); + } + max98927_wrap_update_bits(max98927, MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_BCLKEDGE, invert); + return 0; +} + +/* codec MCLK rate in master mode */ +static const int rate_table[] = { + 5644800, 6000000, 6144000, 6500000, + 9600000, 11289600, 12000000, 12288000, + 13000000, 19200000, +}; + +static int max98927_set_clock(struct max98927_priv *max98927, + struct snd_pcm_hw_params *params) +{ + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98927->ch_size; + int reg = MAX98927_PCM_Clock_setup; + int mask = MAX98927_PCM_Clock_setup_PCM_BSEL_Mask; + int value; + + if (max98927->master) { + int i; + /* match rate to closest value */ + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i] >= max98927->sysclk) + break; + } + if (i == ARRAY_SIZE(rate_table)) { + pr_err("%s couldn't get the MCLK to match codec\n", + __func__); + return -EINVAL; + } + max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode, + MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_Mask, + i << MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_SHIFT); + } + + switch (blr_clk_ratio) { + case 32: + value = 2; + break; + case 48: + value = 3; + break; + case 64: + value = 4; + break; + default: + return -EINVAL; + } + max98927_wrap_update_bits(max98927, + reg, mask, value); + return 0; +} + +static int max98927_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + int sampling_rate = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + max98927_wrap_update_bits(max98927, + MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_16, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_16); + max98927->ch_size = 16; + break; + case 24: + max98927_wrap_update_bits(max98927, + MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_24, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_24); + max98927->ch_size = 24; + break; + case 32: + max98927_wrap_update_bits(max98927, + MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_32, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_32); + max98927->ch_size = 32; + break; + default: + pr_err("%s: format unsupported %d", + __func__, params_format(params)); + goto err; + } + dev_dbg(codec->dev, "%s: format supported %d", + __func__, params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_8000; + break; + case 11025: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_11025; + break; + case 12000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_12000; + break; + case 16000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_16000; + break; + case 22050: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_22050; + break; + case 24000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_24000; + break; + case 32000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_32000; + break; + case 44100: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_44100; + break; + case 48000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_48000; + break; + default: + pr_err("%s rate %d not supported\n", + __func__, params_rate(params)); + goto err; + } + /* set DAI_SR to correct LRCLK frequency */ + max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_1, + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_Mask, sampling_rate); + max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_2, + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_Mask, sampling_rate<<4); + max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_2, + MAX98927_PCM_Sample_rate_setup_2_IVADC_SR_Mask, sampling_rate); + return max98927_set_clock(max98927, params); +err: + return -EINVAL; +} + +#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000 + +#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static int max98927_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + + max98927->sysclk = freq; + return 0; +} + +static const struct snd_soc_dai_ops max98927_dai_ops = { + .set_sysclk = max98927_dai_set_sysclk, + .set_fmt = max98927_dai_set_fmt, + .hw_params = max98927_dai_hw_params, +}; + +static void max98927_handle_pdata(struct snd_soc_codec *codec) +{ + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + struct max98927_reg_default *regInfo; + int cfg_size = 0; + int x; + + if (max98927->regcfg != NULL) + cfg_size = max98927->regcfg_sz / sizeof(uint32_t); + + if (cfg_size <= 0) { + dev_dbg(codec->dev, + "Register configuration is not required.\n"); + return; + } + + /* direct configuration from device tree */ + for (x = 0; x < cfg_size; x += 3) { + regInfo = (struct max98927_reg_default *)&max98927->regcfg[x]; + dev_info(codec->dev, "CH:%d, reg:0x%02x, value:0x%02x\n", + be32_to_cpu(regInfo->ch), + be32_to_cpu(regInfo->reg), + be32_to_cpu(regInfo->def)); + if (be32_to_cpu(regInfo->ch) == PRI_MAX98927 + && max98927->regmap) + regmap_write(max98927->regmap, + be32_to_cpu(regInfo->reg), + be32_to_cpu(regInfo->def)); + else if (be32_to_cpu(regInfo->ch) == SEC_MAX98927 + && max98927->sub_regmap) + regmap_write(max98927->sub_regmap, + be32_to_cpu(regInfo->reg), + be32_to_cpu(regInfo->def)); + } +} + +static int max98927_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + max98927_wrap_update_bits(max98927, + MAX98927_AMP_enables, 1, 1); + /* enable the v and i for vi feedback */ + max98927_wrap_update_bits(max98927, + MAX98927_Measurement_enables, + MAX98927_Measurement_enables_IVADC_V_EN, + MAX98927_Measurement_enables_IVADC_V_EN); + max98927_wrap_update_bits(max98927, + MAX98927_Measurement_enables, + MAX98927_Measurement_enables_IVADC_I_EN, + MAX98927_Measurement_enables_IVADC_I_EN); + max98927_wrap_update_bits(max98927, + MAX98927_Global_Enable, 1, 1); + break; + case SND_SOC_DAPM_POST_PMD: + max98927_wrap_update_bits(max98927, + MAX98927_Global_Enable, 1, 0); + max98927_wrap_update_bits(max98927, + MAX98927_AMP_enables, 1, 0); + /* disable the v and i for vi feedback */ + max98927_wrap_update_bits(max98927, + MAX98927_Measurement_enables, + MAX98927_Measurement_enables_IVADC_V_EN, + 0); + max98927_wrap_update_bits(max98927, + MAX98927_Measurement_enables, + MAX98927_Measurement_enables_IVADC_I_EN, + 0); + break; + default: + return 0; + } + return 0; +} + +static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_AMP_enables, + 0, 0, max98927_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("BE_OUT"), +}; + +static DECLARE_TLV_DB_SCALE(max98927_spk_tlv, 300, 300, 0); +static DECLARE_TLV_DB_SCALE(max98927_digital_tlv, -1600, 25, 0); + +static int max98927_spk_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = max98927->spk_gain; + dev_dbg(codec->dev, "%s: spk_gain setting returned %d\n", __func__, + (int) ucontrol->value.integer.value[0]); + return 0; +} + +static int max98927_spk_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + unsigned int sel = ucontrol->value.integer.value[0]; + + if (sel < ((1 << MAX98927_Speaker_Gain_Width) - 1)) { + max98927_wrap_update_bits(max98927, MAX98927_Speaker_Gain, + MAX98927_Speaker_Gain_SPK_PCM_GAIN_Mask, sel); + max98927->spk_gain = sel; + } + return 0; +} + +static int max98927_digital_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = max98927->digital_gain; + dev_dbg(codec->dev, "%s: spk_gain setting returned %d\n", __func__, + (int) ucontrol->value.integer.value[0]); + return 0; +} + +static int max98927_digital_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + unsigned int sel = ucontrol->value.integer.value[0]; + + if (sel < ((1 << MAX98927_AMP_VOL_WIDTH) - 1)) { + max98927_wrap_update_bits(max98927, MAX98927_AMP_volume_control, + MAX98927_AMP_volume_control_AMP_VOL_Mask, sel); + max98927->digital_gain = sel; + } + return 0; +} + +static int max98927_boost_voltage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, MAX98927_Boost_Control_0, + MAX98927_Boost_Control_0_BST_VOUT_Mask, 0); +} + +static int max98927_boost_voltage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, MAX98927_Boost_Control_0, + MAX98927_Boost_Control_0_BST_VOUT_Mask, 0); +} + +static int max98927_amp_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, MAX98927_Boost_Control_0, + MAX98927_Boost_Control_0_BST_VOUT_Mask, + MAX98927_AMP_VOL_LOCATION_SHIFT); +} + +static int max98927_amp_dsp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, MAX98927_Brownout_enables, + MAX98927_Brownout_enables_AMP_DSP_EN, MAX98927_BDE_DSP_SHIFT); +} + +static int max98927_amp_dsp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, MAX98927_Brownout_enables, + MAX98927_Brownout_enables_AMP_DSP_EN, MAX98927_BDE_DSP_SHIFT); +} + +static int max98927_ramp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, MAX98927_AMP_DSP_Config, + MAX98927_AMP_DSP_Config_AMP_VOL_RMP_BYPASS, + MAX98927_SPK_RMP_EN_SHIFT); +} +static int max98927_ramp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, MAX98927_AMP_DSP_Config, + MAX98927_AMP_DSP_Config_AMP_VOL_RMP_BYPASS, + MAX98927_SPK_RMP_EN_SHIFT); +} + +static int max98927_dre_en_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, MAX98927_DRE_Control, + MAX98927_DRE_Control_DRE_EN, 0); +} +static int max98927_dre_en_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, MAX98927_DRE_Control, + MAX98927_DRE_Control_DRE_EN, 0); +} +static int max98927_amp_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, + MAX98927_AMP_volume_control, + MAX98927_AMP_volume_control_AMP_VOL_SEL, + MAX98927_AMP_VOL_LOCATION_SHIFT); +} +static int max98927_spk_src_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, + MAX98927_Speaker_source_select, + MAX98927_Speaker_source_select_SPK_SOURCE_Mask, 0); +} + +static int max98927_spk_src_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, + MAX98927_Speaker_source_select, + MAX98927_Speaker_source_select_SPK_SOURCE_Mask, 0); +} + +static int max98927_mono_out_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, + MAX98927_PCM_to_speaker_monomix_A, + MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_Mask, + MAX98927_PCM_to_speaker_monomix_A_SHIFT); +} + +static int max98927_mono_out_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, + MAX98927_PCM_to_speaker_monomix_A, + MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_Mask, + MAX98927_PCM_to_speaker_monomix_A_SHIFT); +} + +static bool max98927_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0001 ... 0x0028: + case 0x002B ... 0x004E: + case 0x0051 ... 0x0055: + case 0x005A ... 0x0061: + case 0x0072 ... 0x0087: + case 0x00FF: + case 0x0100: + case 0x01FF: + return true; + } + return false; +}; + +static const char * const max98927_boost_voltage_text[] = { + "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V", + "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V", + "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V", + "9.5V", "9.625V", "9.75V", "9.875V", "10V" +}; + +static const char * const max98927_speaker_source_text[] = { + "i2s", "reserved", "tone", "pdm" +}; + +static const char * const max98927_monomix_output_text[] = { + "ch_0", "ch_1", "ch_1_2_div" +}; + +static const struct soc_enum max98927_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_monomix_output_text), + max98927_monomix_output_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_speaker_source_text), + max98927_speaker_source_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_boost_voltage_text), + max98927_boost_voltage_text), +}; + +static const struct snd_kcontrol_new max98927_snd_controls[] = { + SOC_SINGLE_EXT_TLV("Speaker Volume", MAX98927_Speaker_Gain, + 0, (1<<MAX98927_Speaker_Gain_Width)-1, 0, + max98927_spk_gain_get, max98927_spk_gain_put, + max98927_spk_tlv), + SOC_SINGLE_EXT_TLV("Digital Gain", MAX98927_AMP_volume_control, + 0, (1<<MAX98927_AMP_VOL_WIDTH)-1, 0, + max98927_digital_gain_get, max98927_digital_gain_put, + max98927_digital_tlv), + SOC_SINGLE_EXT("Amp DSP Enable", MAX98927_Brownout_enables, + MAX98927_BDE_DSP_SHIFT, 1, 0, + max98927_amp_dsp_get, max98927_amp_dsp_put), + SOC_SINGLE_EXT("Ramp Switch", MAX98927_AMP_DSP_Config, + MAX98927_SPK_RMP_EN_SHIFT, 1, 1, + max98927_ramp_switch_get, max98927_ramp_switch_put), + SOC_SINGLE_EXT("DRE EN", MAX98927_DRE_Control, + MAX98927_DRE_Control_DRE_SHIFT, 1, 0, + max98927_dre_en_get, max98927_dre_en_put), + SOC_SINGLE_EXT("Amp Volume Location", MAX98927_AMP_volume_control, + MAX98927_AMP_VOL_LOCATION_SHIFT, 1, 0, + max98927_amp_vol_get, max98927_amp_vol_put), + + SOC_ENUM_EXT("Boost Output Voltage", max98927_enum[2], + max98927_boost_voltage_get, max98927_boost_voltage_put), + SOC_ENUM_EXT("Speaker Source", max98927_enum[1], + max98927_spk_src_get, max98927_spk_src_put), + SOC_ENUM_EXT("Monomix Output", max98927_enum[0], + max98927_mono_out_get, max98927_mono_out_put), +}; + +static const struct snd_soc_dapm_route max98927_audio_map[] = { + {"BE_OUT", NULL, "Amp Enable"}, +}; + +static struct snd_soc_dai_driver max98927_dai[] = { + { + .name = "max98927-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98927_RATES, + .formats = MAX98927_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98927_RATES, + .formats = MAX98927_FORMATS, + }, + .ops = &max98927_dai_ops, + } +}; + +static int max98927_probe(struct snd_soc_codec *codec) +{ + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + int ret = 0, reg = 0, i; + + max98927->codec = codec; + codec->control_data = max98927->regmap; + codec->cache_bypass = 1; + + /* Software Reset */ + max98927_wrapper_write(max98927, + MAX98927_Software_Reset, MAX98927_Software_Reset_RST); + + /* Check Revision ID for the primary MAX98927*/ + ret = regmap_read(max98927->regmap, MAX98927_REV_ID, ®); + if (ret < 0) + dev_err(codec->dev, + "Failed to read: 0x%02X\n", MAX98927_REV_ID); + else + dev_info(codec->dev, + "MAX98927 revisionID: 0x%02X\n", reg); + + /* Check Revision ID for the secondary MAX98927*/ + if (max98927->sub_regmap) { + ret = regmap_read(max98927->sub_regmap, MAX98927_REV_ID, ®); + if (ret < 0) + dev_err(codec->dev, + "Failed to read: 0x%02X from secodnary device\n" + , MAX98927_REV_ID); + else + dev_info(codec->dev, + "Secondary device revisionID: 0x%02X\n", reg); + } + + /* Register initialization */ + for (i = 0; i < sizeof(max98927_reg_map)/ + sizeof(max98927_reg_map[0]); i++) + max98927_wrapper_write(max98927, + max98927_reg_map[i].reg, + max98927_reg_map[i].def); + + if (max98927->regmap) + regmap_write(max98927->regmap, + MAX98927_PCM_Tx_Channel_Sources_A, + (max98927->i_l_slot + <<MAX98927_PCM_Tx_Ch_Sources_A_I_SHIFT| + max98927->v_l_slot)&0xFF); + if (max98927->sub_regmap) + regmap_write(max98927->sub_regmap, + MAX98927_PCM_Tx_Channel_Sources_A, + (max98927->i_r_slot + <<MAX98927_PCM_Tx_Ch_Sources_A_I_SHIFT| + max98927->v_r_slot)&0xFF); + + /* Set interleave mode */ + if (max98927->interleave_mode) + max98927_wrap_update_bits(max98927, + MAX98927_PCM_Tx_Channel_Sources_B, + MAX98927_PCM_Tx_Channel_Src_INTERLEAVE_Mask, + MAX98927_PCM_Tx_Channel_Src_INTERLEAVE_Mask); + + max98927_handle_pdata(codec); + + return ret; +} + +static const struct snd_soc_codec_driver soc_codec_dev_max98927 = { + .probe = max98927_probe, + .dapm_routes = max98927_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98927_audio_map), + .dapm_widgets = max98927_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98927_dapm_widgets), + .controls = max98927_snd_controls, + .num_controls = ARRAY_SIZE(max98927_snd_controls), +}; + +static const struct regmap_config max98927_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98927_REV_ID, + .reg_defaults = max98927_reg_map, + .num_reg_defaults = ARRAY_SIZE(max98927_reg_map), + .readable_reg = max98927_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static struct i2c_board_info max98927_i2c_sub_board[] = { + { + I2C_BOARD_INFO("max98927_sub", 0x39), + } +}; + +static struct i2c_driver max98927_i2c_sub_driver = { + .driver = { + .name = "max98927_sub", + .owner = THIS_MODULE, + }, +}; + +struct i2c_client *max98927_add_sub_device(int bus_id, int slave_addr) +{ + struct i2c_client *i2c = NULL; + struct i2c_adapter *adapter; + + max98927_i2c_sub_board[0].addr = slave_addr; + + adapter = i2c_get_adapter(bus_id); + if (adapter) { + i2c = i2c_new_device(adapter, max98927_i2c_sub_board); + if (i2c) + i2c->dev.driver = &max98927_i2c_sub_driver.driver; + } + + return i2c; +} + +int probe_common(struct i2c_client *i2c, struct max98927_priv *max98927) +{ + int ret = 0, value; + + if (!of_property_read_u32(i2c->dev.of_node, "vmon-l-slot", &value)) + max98927->v_l_slot = value & 0xF; + else + max98927->v_l_slot = 0; + if (!of_property_read_u32(i2c->dev.of_node, "imon-l-slot", &value)) + max98927->i_l_slot = value & 0xF; + else + max98927->i_l_slot = 1; + if (!of_property_read_u32(i2c->dev.of_node, "vmon-r-slot", &value)) + max98927->v_r_slot = value & 0xF; + else + max98927->v_r_slot = 2; + if (!of_property_read_u32(i2c->dev.of_node, "imon-r-slot", &value)) + max98927->i_r_slot = value & 0xF; + else + max98927->i_r_slot = 3; + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98927, + max98927_dai, ARRAY_SIZE(max98927_dai)); + if (ret < 0) + dev_err(&i2c->dev, + "Failed to register codec: %d\n", ret); + return ret; +} + +static int max98927_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + + int ret = 0, value; + struct max98927_priv *max98927 = NULL; + + max98927 = devm_kzalloc(&i2c->dev, + sizeof(*max98927), GFP_KERNEL); + + if (!max98927) { + ret = -ENOMEM; + goto err; + } + i2c_set_clientdata(i2c, max98927); + + /* update interleave mode info */ + if (!of_property_read_u32(i2c->dev.of_node, + "interleave_mode", &value)) { + if (value > 0) + max98927->interleave_mode = 1; + else + max98927->interleave_mode = 0; + } else + max98927->interleave_mode = 0; + + /* update direct configuration info */ + max98927->regcfg = of_get_property(i2c->dev.of_node, + "maxim,regcfg", &max98927->regcfg_sz); + + /* check for secondary MAX98927 */ + ret = of_property_read_u32(i2c->dev.of_node, + "maxim,sub_reg", &max98927->sub_reg); + if (ret) { + dev_err(&i2c->dev, "Sub-device slave address was not found.\n"); + max98927->sub_reg = -1; + } + ret = of_property_read_u32(i2c->dev.of_node, + "maxim,sub_bus", &max98927->sub_bus); + if (ret) { + dev_err(&i2c->dev, "Sub-device bus information was not found.\n"); + max98927->sub_bus = i2c->adapter->nr; + } + + /* regmap initialization for primary device */ + max98927->regmap + = devm_regmap_init_i2c(i2c, &max98927_regmap); + if (IS_ERR(max98927->regmap)) { + ret = PTR_ERR(max98927->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + goto err; + } + + /* regmap initialization for secondary device */ + if (max98927->sub_reg > 0) { + max98927->sub_i2c = max98927_add_sub_device(max98927->sub_bus, + max98927->sub_reg); + if (IS_ERR(max98927->sub_i2c)) { + dev_err(&max98927->sub_i2c->dev, + "Second MAX98927 was not found\n"); + ret = PTR_ERR(max98927->regmap); + goto err; + } else { + max98927->sub_regmap = regmap_init_i2c( + max98927->sub_i2c, &max98927_regmap); + if (IS_ERR(max98927->sub_regmap)) { + ret = PTR_ERR(max98927->sub_regmap); + dev_err(&max98927->sub_i2c->dev, + "Failed to allocate sub_regmap: %d\n", + ret); + goto err; + } + } + } + + /* codec registeration */ + ret = probe_common(i2c, max98927); + + return ret; + +err: + if (max98927) + devm_kfree(&i2c->dev, max98927); + return ret; +} + +static int max98927_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max98927_i2c_id[] = { + { "max98927", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98927_i2c_id); + +static const struct of_device_id max98927_of_match[] = { + { .compatible = "maxim,max98927", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98927_of_match); + +static struct i2c_driver max98927_i2c_driver = { + .driver = { + .name = "max98927", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max98927_of_match), + .pm = NULL, + }, + .probe = max98927_i2c_probe, + .remove = max98927_i2c_remove, + .id_table = max98927_i2c_id, +}; + +module_i2c_driver(max98927_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98927 driver"); +MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h new file mode 100755 index 0000000..2305185 --- /dev/null +++ b/sound/soc/codecs/max98927.h @@ -0,0 +1,1253 @@ +/* + * max98927.c -- MAX98927 ALSA Soc Audio driver + * + * Copyright 2008-11 Wolfson Microelectronics PLC. + * Author: Ryan Lee <ryans.lee@maximintegrated.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifndef __MAX98927_REGISTERDEFS_H +#define __MAX98927_REGISTERDEFS_H +#ifdef CONFIG_SND_SOC_MAXIM_DSM +#include <sound/maxim_dsm.h> +#endif /* CONFIG_SND_SOC_MAXIM_DSM */ + +enum { + PRI_MAX98927 = 0, + SEC_MAX98927 = 1, + MAX_DEV_ID_MAX98927, +} MAX98927deviceID; + +enum { + /*Interrupt Raw 1 (Address 0x0001)*/ + MAX98927_Interrupt_Raw_1 = 0x0001, + MAX98927_Interrupt_Raw_1_BDE_ACTIVE_END_RAW = (0x1 << 0), + MAX98927_Interrupt_Raw_1_BDE_ACTIVE_BGN_RAW = (0x1 << 1), + MAX98927_Interrupt_Raw_1_BDE_LEVEL_CHANGE_RAW = (0x1 << 2), + MAX98927_Interrupt_Raw_1_BDE_L8_RAW = (0x1 << 3), + MAX98927_Interrupt_Raw_1_THERMWARN_END_RAW = (0x1 << 4), + MAX98927_Interrupt_Raw_1_THERMWARN_START_RAW = (0x1 << 5), + MAX98927_Interrupt_Raw_1_THERMSHDN_END_RAW = (0x1 << 6), + MAX98927_Interrupt_Raw_1_THERMSHDN_START_RAW = (0x1 << 7), + + /* Interrupt Raw 2 (Address 0x0002)*/ + MAX98927_Interrupt_Raw_2 = 0x0002, + MAX98927_Interrupt_Raw_2_WATCHDOGWARN_RAW = (0x1 << 0), + MAX98927_Interrupt_Raw_2_WATCHDOGFAIL_RAW = (0x1 << 1), + MAX98927_Interrupt_Raw_2_BOOSTCURRLIM_RAW = (0x1 << 2), + MAX98927_Interrupt_Raw_2_CLKSTOP_RAW = (0x1 << 3), + MAX98927_Interrupt_Raw_2_CLKSTART_RAW = (0x1 << 4), + MAX98927_Interrupt_Raw_2_MEASADC_END_RAW = (0x1 << 5), + MAX98927_Interrupt_Raw_2_PWRDN_DONE_RAW = (0x1 << 6), + MAX98927_Interrupt_Raw_2_PWRUP_DONE_RAW = (0x1 << 7), + + /* Interrupt Raw 3 (Address 0x0003)*/ + MAX98927_Interrupt_Raw_3 = 0x0003, + MAX98927_Interrupt_Raw_3_PWRUP_FAIL_RAW = (0x1 << 0), + MAX98927_Interrupt_Raw_3_AUTH_DONE_RAW = (0x1 << 1), + MAX98927_Interrupt_Raw_3_SPK_OVC_RAW = (0x1 << 2), + MAX98927_Interrupt_Raw_3_BST_UVLO_RAW = (0x1 << 3), + + /* Interrupt State 1 (Address 0x0004)*/ + MAX98927_Interrupt_State_1 = 0x0004, + MAX98927_Interrupt_State_1_BDE_ACTIVE_END_STATE = (0x1 << 0), + MAX98927_Interrupt_State_1_BDE_ACTIVE_BGN_STATE = (0x1 << 1), + MAX98927_Interrupt_State_1_BDE_LEVEL_CHANGE_STATE = (0x1 << 2), + MAX98927_Interrupt_State_1_BDE_L8_STATE = (0x1 << 3), + MAX98927_Interrupt_State_1_THERMWARN_END_STATE = (0x1 << 4), + MAX98927_Interrupt_State_1_THERMWARN_START_STATE = (0x1 << 5), + MAX98927_Interrupt_State_1_THERMSHDN_END_STATE = (0x1 << 6), + MAX98927_Interrupt_State_1_THERMSHDN_START_STATE = (0x1 << 7), + + /* Interrupt State 2 (Address 0x0005)*/ + MAX98927_Interrupt_State_2 = 0x0005, + MAX98927_Interrupt_State_2_WATCHDOGWARN_STATE = (0x1 << 0), + MAX98927_Interrupt_State_2_WATCHDOGFAIL_STATE = (0x1 << 1), + MAX98927_Interrupt_State_2_BOOSTCURRLIM_STATE = (0x1 << 2), + MAX98927_Interrupt_State_2_CLKSTOP_STATE = (0x1 << 3), + MAX98927_Interrupt_State_2_CLKSTART_STATE = (0x1 << 4), + MAX98927_Interrupt_State_2_MEASADC_END_STATE = (0x1 << 5), + MAX98927_Interrupt_State_2_PWRDN_DONE_STATE = (0x1 << 6), + MAX98927_Interrupt_State_2_PWRUP_DONE_STATE = (0x1 << 7), + + /* Interrupt State 3 (Address 0x0006)*/ + MAX98927_Interrupt_State_3 = 0x0006, + MAX98927_Interrupt_State_3_PWRUP_FAIL_STATE = (0x1 << 0), + MAX98927_Interrupt_State_3_AUTH_DONE_STATE = (0x1 << 1), + MAX98927_Interrupt_State_3_SPK_OVC_STATE = (0x1 << 2), + MAX98927_Interrupt_State_3_BST_UVLO_STATE = (0x1 << 3), + + /* Interrupt Flag 1 (Address 0x0007)*/ + MAX98927_Interrupt_Flag_1 = 0x0007, + MAX98927_Interrupt_Flag_1_BDE_ACTIVE_END_FLAG = (0x1 << 0), + MAX98927_Interrupt_Flag_1_BDE_ACTIVE_BGN_FLAG = (0x1 << 1), + MAX98927_Interrupt_Flag_1_BDE_LEVEL_CHANGE_FLAG = (0x1 << 2), + MAX98927_Interrupt_Flag_1_BDE_L8_FLAG = (0x1 << 3), + MAX98927_Interrupt_Flag_1_THERMWARN_END_FLAG = (0x1 << 4), + MAX98927_Interrupt_Flag_1_THERMWARN_START_FLAG = (0x1 << 5), + MAX98927_Interrupt_Flag_1_THERMSHDN_END_FLAG = (0x1 << 6), + MAX98927_Interrupt_Flag_1_THERMSHDN_START_FLAG = (0x1 << 7), + + /* Interrupt Flag 2 (Address 0x0008)*/ + MAX98927_Interrupt_Flag_2 = 0x0008, + MAX98927_Interrupt_Flag_2_WATCHDOGWARN_FLAG = (0x1 << 0), + MAX98927_Interrupt_Flag_2_WATCHDOGFAIL_FLAG = (0x1 << 1), + MAX98927_Interrupt_Flag_2_BOOSTCURRLIM_FLAG = (0x1 << 2), + MAX98927_Interrupt_Flag_2_CLKSTOP_FLAG = (0x1 << 3), + MAX98927_Interrupt_Flag_2_CLKSTART_FLAG = (0x1 << 4), + MAX98927_Interrupt_Flag_2_MEASADC_END_FLAG = (0x1 << 5), + MAX98927_Interrupt_Flag_2_PWRDN_DONE_FLAG = (0x1 << 6), + MAX98927_Interrupt_Flag_2_PWRUP_DONE_FLAG = (0x1 << 7), + + /* Interrupt Flag 3 (Address 0x0009)*/ + MAX98927_Interrupt_Flag_3 = 0x0009, + MAX98927_Interrupt_Flag_3_PWRUP_FAIL_FLAG = (0x1 << 0), + MAX98927_Interrupt_Flag_3_AUTH_DONE_FLAG = (0x1 << 1), + MAX98927_Interrupt_Flag_3_SPK_OVC_FLAG = (0x1 << 2), + MAX98927_Interrupt_Flag_3_BST_UVLO_FLAG = (0x1 << 3), + + /* Interrupt Enable 1 (Address 0x000a)*/ + MAX98927_Interrupt_Enable_1 = 0x000a, + MAX98927_Interrupt_Enable_1_BDE_ACTIVE_END_EN = (0x1 << 0), + MAX98927_Interrupt_Enable_1_BDE_ACTIVE_BGN_EN = (0x1 << 1), + MAX98927_Interrupt_Enable_1_BDE_LEVEL_CHANGE_EN = (0x1 << 2), + MAX98927_Interrupt_Enable_1_BDE_L8_EN = (0x1 << 3), + MAX98927_Interrupt_Enable_1_THERMWARN_END_EN = (0x1 << 4), + MAX98927_Interrupt_Enable_1_THERMWARN_START_EN = (0x1 << 5), + MAX98927_Interrupt_Enable_1_THERMSHDN_END_EN = (0x1 << 6), + MAX98927_Interrupt_Enable_1_THERMSHDN_START_EN = (0x1 << 7), + + /* Interrupt Enable 2 (Address 0x000b)*/ + MAX98927_Interrupt_Enable_2 = 0x000b, + MAX98927_Interrupt_Enable_2_WATCHDOGWARN_EN = (0x1 << 0), + MAX98927_Interrupt_Enable_2_WATCHDOGFAIL_EN = (0x1 << 1), + MAX98927_Interrupt_Enable_2_BOOSTCURRLIM_EN = (0x1 << 2), + MAX98927_Interrupt_Enable_2_CLKSTOP_EN = (0x1 << 3), + MAX98927_Interrupt_Enable_2_CLKSTART_EN = (0x1 << 4), + MAX98927_Interrupt_Enable_2_MEASADC_END_EN = (0x1 << 5), + MAX98927_Interrupt_Enable_2_PWRDN_DONE_EN = (0x1 << 6), + MAX98927_Interrupt_Enable_2_PWRUP_DONE_EN = (0x1 << 7), + + /* Interrupt Enable 3 (Address 0x000c)*/ + MAX98927_Interrupt_Enable_3 = 0x000c, + MAX98927_Interrupt_Enable_3_PWRUP_FAIL_EN = (0x1 << 0), + MAX98927_Interrupt_Enable_3_AUTH_DONE_EN = (0x1 << 1), + MAX98927_Interrupt_Enable_3_SPK_OVC_EN = (0x1 << 2), + MAX98927_Interrupt_Enable_3_BST_UVLO_EN = (0x1 << 3), + + /* Interrupt Flag Clear 1 (Address 0x000d)*/ + MAX98927_Interrupt_Flag_Clear_1 = 0x000d, + MAX98927_Interrupt_Flag_Clear_1_BDE_ACTIVE_END_CLR = (0x1 << 0), + MAX98927_Interrupt_Flag_Clear_1_BDE_ACTIVE_BGN_CLR = (0x1 << 1), + MAX98927_Interrupt_Flag_Clear_1_BDE_LEVEL_CHANGE_CLR = (0x1 << 2), + MAX98927_Interrupt_Flag_Clear_1_BDE_L8_CLR = (0x1 << 3), + MAX98927_Interrupt_Flag_Clear_1_THERMWARN_END_CLR = (0x1 << 4), + MAX98927_Interrupt_Flag_Clear_1_THERMWARN_START_CLR = (0x1 << 5), + MAX98927_Interrupt_Flag_Clear_1_THERMSHDN_END_CLR = (0x1 << 6), + MAX98927_Interrupt_Flag_Clear_1_THERMSHDN_START_CLR = (0x1 << 7), + + /* Interrupt Flag Clear 2 (Address 0x000e)*/ + MAX98927_Interrupt_Flag_Clear_2 = 0x000e, + MAX98927_Interrupt_Flag_Clear_2_WATCHDOGWARN_CLR = (0x1 << 0), + MAX98927_Interrupt_Flag_Clear_2_WATCHDOGFAIL_CLR = (0x1 << 1), + MAX98927_Interrupt_Flag_Clear_2_BOOSTCURRLIM_CLR = (0x1 << 2), + MAX98927_Interrupt_Flag_Clear_2_CLKSTOP_CLR = (0x1 << 3), + MAX98927_Interrupt_Flag_Clear_2_CLKSTART_CLR = (0x1 << 4), + MAX98927_Interrupt_Flag_Clear_2_MEASADC_END_CLR = (0x1 << 5), + MAX98927_Interrupt_Flag_Clear_2_PWRDN_DONE_CLR = (0x1 << 6), + MAX98927_Interrupt_Flag_Clear_2_PWRUP_DONE_CLR = (0x1 << 7), + + /* Interrupt Flag Clear 3 (Address 0x000f)*/ + MAX98927_Interrupt_Flag_Clear_3 = 0x000f, + MAX98927_Interrupt_Flag_Clear_3_PWRUP_FAIL_CLR = (0x1 << 0), + MAX98927_Interrupt_Flag_Clear_3_AUTH_DONE_CLR = (0x1 << 1), + MAX98927_Interrupt_Flag_Clear_3_SPK_OVC_CLR = (0x1 << 2), + MAX98927_Interrupt_Flag_Clear_3_BST_UVLO_CLR = (0x1 << 3), + + /* IRQ Control (Address 0x0010)*/ + MAX98927_IRQ_Control = 0x0010, + MAX98927_IRQ_Control_IRQ_EN = (0x1 << 0), + MAX98927_IRQ_Control_IRQ_POL = (0x1 << 1), + MAX98927_IRQ_Control_IRQ_MODE = (0x1 << 2), + + /* Clock monitor enable (Address 0x0011)*/ + MAX98927_Clock_monitor_enable = 0x0011, + MAX98927_Clock_monitor_enable_CMON_ENA = (0x1 << 0), + MAX98927_Clock_monitor_enable_CMON_AUTORESTART_ENA = (0x1 << 1), + + /* Watchdog Control (Address 0x0012)*/ + MAX98927_Watchdog_Control = 0x0012, + MAX98927_Watchdog_Control_WDT_ENA = (0x1 << 0), + MAX98927_Watchdog_Control_WDT_MODE = (0x1 << 1), + MAX98927_Watchdog_Control_WDT_TO_SEL_Mask = (0x3 << 2), + MAX98927_Watchdog_Control_WDT_TO_SEL_5 = (0x0 << 2), + MAX98927_Watchdog_Control_WDT_TO_SEL_10 = (0x1 << 2), + MAX98927_Watchdog_Control_WDT_TO_SEL_35 = (0x2 << 2), + MAX98927_Watchdog_Control_WDT_TO_SEL_50 = (0x3 << 2), + MAX98927_Watchdog_Control_WDT_HW_SOURCE = (0x1 << 4), + + /* Watchdog SW Reset (Address 0x0013)*/ + MAX98927_Watchdog_SW_Reset = 0x0013, + MAX98927_Watchdog_SW_Reset_WDT_SW_RST_Mask = (0xff << 0), + + /* Meas ADC Thermal Warning Threshhold (Address 0x0014)*/ + MAX98927_Meas_ADC_TW_Threshhold = 0x0014, + MAX98927_Meas_ADC_TW_Threshhold_MEAS_ADC_WARN_THRESH_Mask + = (0xff << 0), + + /* Meas ADC Thermal Shutdown Threshhold (Address 0x0015)*/ + MAX98927_Meas_ADC_TS_Threshhold = 0x0015, + MAX98927_Meas_ADC_TS_Threshhold_MEAS_ADC_SHDN_THRESH_Mask + = (0xff << 0), + + /* Meas ADC Thermal Hysteresis (Address 0x0016)*/ + MAX98927_Meas_ADC_Thermal_Hysteresis = 0x0016, + MAX98927_Meas_ADC_TH_MEAS_ADC_THERM_HYST_Mask = (0x1f << 0), + + /* Pin Config (Address 0x0017)*/ + MAX98927_Pin_Config = 0x0017, + MAX98927_Pin_Config_DOUT_DRV_Mask = (0x3 << 0), + MAX98927_Pin_Config_DOUT_DRV_01 = (0x0 << 0), + MAX98927_Pin_Config_DOUT_DRV_11 = (0x2 << 0), + MAX98927_Pin_Config_BCLK_DRV_Mask = (0x3 << 2), + MAX98927_Pin_Config_BCLK_DRV_01 = (0x0 << 2), + MAX98927_Pin_Config_BCLK_DRV_11 = (0x2 << 2), + MAX98927_Pin_Config_LRCLK_DRV_Mask = (0x3 << 4), + MAX98927_Pin_Config_LRCLK_DRV_01 = (0x0 << 4), + MAX98927_Pin_Config_LRCLK_DRV_11 = (0x2 << 4), + MAX98927_Pin_Config_ICC_DRV_Mask = (0x3 << 6), + MAX98927_Pin_Config_ICC_DRV_01 = (0x0 << 6), + MAX98927_Pin_Config_ICC_DRV_11 = (0x2 << 6), + + /* PCM Rx Enables A (Address 0x0018)*/ + MAX98927_PCM_Rx_Enables_A = 0x0018, + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH0_EN = (0x1 << 0), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH1_EN = (0x1 << 1), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH2_EN = (0x1 << 2), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH3_EN = (0x1 << 3), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH4_EN = (0x1 << 4), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH5_EN = (0x1 << 5), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH6_EN = (0x1 << 6), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH7_EN = (0x1 << 7), + + /* PCM Rx Enables B (Address 0x0019)*/ + MAX98927_PCM_Rx_Enables_B = 0x0019, + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH8_EN = (0x1 << 0), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH9_EN = (0x1 << 1), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH10_EN = (0x1 << 2), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH11_EN = (0x1 << 3), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH12_EN = (0x1 << 4), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH13_EN = (0x1 << 5), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH14_EN = (0x1 << 6), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH15_EN = (0x1 << 7), + + /* PCM Tx Enables A (Address 0x001a)*/ + MAX98927_PCM_Tx_Enables_A = 0x001a, + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH0_EN = (0x1 << 0), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH1_EN = (0x1 << 1), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH2_EN = (0x1 << 2), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH3_EN = (0x1 << 3), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH4_EN = (0x1 << 4), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH5_EN = (0x1 << 5), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH6_EN = (0x1 << 6), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH7_EN = (0x1 << 7), + + /* PCM Tx Enables B (Address 0x001b)*/ + MAX98927_PCM_Tx_Enables_B = 0x001b, + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH8_EN = (0x1 << 0), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH9_EN = (0x1 << 1), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH10_EN = (0x1 << 2), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH11_EN = (0x1 << 3), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH12_EN = (0x1 << 4), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH13_EN = (0x1 << 5), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH14_EN = (0x1 << 6), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH15_EN = (0x1 << 7), + + /* PCM Tx HiZ Control A (Address 0x001c)*/ + MAX98927_PCM_Tx_HiZ_Control_A = 0x001c, + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH0_HIZ = (0x1 << 0), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH1_HIZ = (0x1 << 1), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH2_HIZ = (0x1 << 2), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH3_HIZ = (0x1 << 3), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH4_HIZ = (0x1 << 4), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH5_HIZ = (0x1 << 5), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH6_HIZ = (0x1 << 6), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH7_HIZ = (0x1 << 7), + + /* PCM Tx HiZ Control B (Address 0x001d)*/ + MAX98927_PCM_Tx_HiZ_Control_B = 0x001d, + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH8_HIZ = (0x1 << 0), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH9_HIZ = (0x1 << 1), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH10_HIZ = (0x1 << 2), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH11_HIZ = (0x1 << 3), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH12_HIZ = (0x1 << 4), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH13_HIZ = (0x1 << 5), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH14_HIZ = (0x1 << 6), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH15_HIZ = (0x1 << 7), + + /* PCM Tx Channel Sources A (Address 0x001e)*/ + MAX98927_PCM_Tx_Channel_Sources_A = 0x001e, + MAX98927_PCM_Tx_Channel_Sources_A_PCM_IVADC_V_DEST_Mask = (0xf << 0), + MAX98927_PCM_Tx_Channel_Sources_A_PCM_IVADC_I_DEST_Mask = (0xf << 4), + + /* PCM Tx Channel Sources B (Address 0x001f)*/ + MAX98927_PCM_Tx_Channel_Sources_B = 0x001f, + MAX98927_PCM_Tx_Channel_Sources_B_PCM_AMP_DSP_DEST_Mask = (0xf << 0), + MAX98927_PCM_Tx_Channel_Src_INTERLEAVE_Mask = (0x1 << 5), + + /* PCM Mode Config (Address 0x0020)*/ + MAX98927_PCM_Mode_Config = 0x0020, + MAX98927_PCM_Mode_Config_PCM_TX_EXTRA_HIZ = (0x1 << 0), + MAX98927_PCM_Mode_Config_PCM_CHANSEL = (0x1 << 1), + MAX98927_PCM_Mode_Config_PCM_BCLKEDGE = (0x1 << 2), + MAX98927_PCM_Mode_Config_PCM_FORMAT_Mask = (0x7 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_I2S = (0x0 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_LEFT = (0x1 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_TDM_0 = (0x3 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_TDM_1 = (0x4 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_TDM_2 = (0x5 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_ = (0x6 << 3), + MAX98927_PCM_Mode_Config_PCM_CHANSZ_Mask = (0x3 << 6), + MAX98927_PCM_Mode_Config_PCM_CHANSZ_16 = (0x1 << 6), + MAX98927_PCM_Mode_Config_PCM_CHANSZ_24 = (0x2 << 6), + MAX98927_PCM_Mode_Config_PCM_CHANSZ_32 = (0x3 << 6), + + /* PCM Master Mode (Address 0x0021)*/ + MAX98927_PCM_Master_Mode = 0x0021, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask = (0x3 << 0), + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_SLAVE = (0x0 << 0), + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_MASTER = (0x3 << 0), + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_HYBRID = (0x1 << 0), + MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_Mask = (0xf << 2), + MAX98927_PCM_Master_Mode_PCM_CLK_SOURCE = (0x1 << 6), + + /* PCM Clock setup (Address 0x0022)*/ + MAX98927_PCM_Clock_setup = 0x0022, + MAX98927_PCM_Clock_setup_PCM_BSEL_Mask = (0xf << 0), + MAX98927_PCM_Clock_setup_PCM_MSEL_Mask = (0xf << 4), + + /* PCM Sample rate setup 1 (Address 0x0023)*/ + MAX98927_PCM_Sample_rate_setup_1 = 0x0023, + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_Mask = (0xf << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_8000 = (0x0 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_11025 = (0x1 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_12000 = (0x2 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_16000 = (0x3 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_22050 = (0x4 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_24000 = (0x5 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_32000 = (0x6 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_44100 = (0x7 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_48000 = (0x8 << 0), + + /* PCM Sample rate setup 1 (Address 0x0024)*/ + MAX98927_PCM_Sample_rate_setup_2 = 0x0024, + MAX98927_PCM_Sample_rate_setup_2_IVADC_SR_Mask = (0xf << 0), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_Mask = (0xf << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0001 = (0x0 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0011 = (0x2 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0101 = (0x4 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0111 = (0x6 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_1001 = (0x8 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_1011 = (0xa << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_1101 = (0xc << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_ = (0xf << 4), + + /* PCM to speaker monomix A (Address 0x0025)*/ + MAX98927_PCM_to_speaker_monomix_A = 0x0025, + MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CH0_SOURCE_Mask = (0xf << 0), + MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_Mask = (0x3 << 6), + MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_1 = (0x0 << 6), + MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_3 = (0x0 << 6), + + /* PCM to speaker monomix B (Address 0x0026)*/ + MAX98927_PCM_to_spkmonomix_B = 0x0026, + MAX98927_PCM_to_spkmonomix_B_DMONOMIX_CH1_SOURCE_Mask = (0xf << 0), + + /* ICC RX Enables A (Address 0x0027)*/ + MAX98927_ICC_RX_Enables_A = 0x0027, + MAX98927_ICC_RX_Enables_A_ICC_RX_CH0_EN = (0x1 << 0), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH1_EN = (0x1 << 1), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH2_EN = (0x1 << 2), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH3_EN = (0x1 << 3), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH4_EN = (0x1 << 4), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH5_EN = (0x1 << 5), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH6_EN = (0x1 << 6), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH7_EN = (0x1 << 7), + + /* ICC RX Enables B (Address 0x0028)*/ + MAX98927_ICC_RX_Enables_B = 0x0028, + MAX98927_ICC_RX_Enables_B_ICC_RX_CH8_EN = (0x1 << 0), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH9_EN = (0x1 << 1), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH10_EN = (0x1 << 2), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH11_EN = (0x1 << 3), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH12_EN = (0x1 << 4), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH13_EN = (0x1 << 5), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH14_EN = (0x1 << 6), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH15_EN = (0x1 << 7), + + /* ICC TX Enables A (Address 0x002b)*/ + MAX98927_ICC_TX_Enables_A = 0x002b, + MAX98927_ICC_TX_Enables_A_ICC_TX_CH0_EN = (0x1 << 0), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH1_EN = (0x1 << 1), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH2_EN = (0x1 << 2), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH3_EN = (0x1 << 3), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH4_EN = (0x1 << 4), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH5_EN = (0x1 << 5), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH6_EN = (0x1 << 6), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH7_EN = (0x1 << 7), + + /* ICC TX Enables B (Address 0x002c)*/ + MAX98927_ICC_TX_Enables_B = 0x002c, + MAX98927_ICC_TX_Enables_B_ICC_TX_CH8_EN = (0x1 << 0), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH9_EN = (0x1 << 1), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH10_EN = (0x1 << 2), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH11_EN = (0x1 << 3), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH12_EN = (0x1 << 4), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH13_EN = (0x1 << 5), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH14_EN = (0x1 << 6), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH15_EN = (0x1 << 7), + + /* ICC Data Order Select (Address 0x002d)*/ + MAX98927_ICC_Data_Order_Select = 0x002d, + MAX98927_ICC_Data_Order_Select_ICC_DRIVE_MODE = (0x1 << 3), + + /* ICC HiZ Manual Mode (Address 0x002e)*/ + MAX98927_ICC_HiZ_Manual_Mode = 0x002e, + MAX98927_ICC_HiZ_Manual_Mode_ICC_TX_HIZ_MANUAL = (0x1 << 0), + MAX98927_ICC_HiZ_Manual_Mode_ICC_TX_EXTRA_HIZ = (0x1 << 1), + + /* ICC TX HiZ Enables A (Address 0x002f)*/ + MAX98927_ICC_TX_HiZ_Enables_A = 0x002f, + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH0_HIZ = (0x1 << 0), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH1_HIZ = (0x1 << 1), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH2_HIZ = (0x1 << 2), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH3_HIZ = (0x1 << 3), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH4_HIZ = (0x1 << 4), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH5_HIZ = (0x1 << 5), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH6_HIZ = (0x1 << 6), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH7_HIZ = (0x1 << 7), + + /* ICC TX HiZ Enables B (Address 0x0030)*/ + MAX98927_ICC_TX_HiZ_Enables_B = 0x0030, + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH8_HIZ = (0x1 << 0), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH9_HIZ = (0x1 << 1), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH10_HIZ = (0x1 << 2), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH11_HIZ = (0x1 << 3), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH12_HIZ = (0x1 << 4), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH13_HIZ = (0x1 << 5), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH14_HIZ = (0x1 << 6), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH15_HIZ = (0x1 << 7), + + /* ICC Link Enables (Address 0x0031)*/ + MAX98927_ICC_Link_Enables = 0x0031, + MAX98927_ICC_Link_Enables_ICC_LINK_EN = (0x1 << 1), + + /* PDM Tx Enables (Address 0x0032)*/ + MAX98927_PDM_Tx_Enables = 0x0032, + MAX98927_PDM_Tx_Enables_PDM_TX_EN = (0x1 << 0), + MAX98927_PDM_Tx_Enables_PDM_TX_CLK_DIV2 = (0x1 << 1), + + /* PDM Tx HiZ Control (Address 0x0033)*/ + MAX98927_PDM_Tx_HiZ_Control = 0x0033, + MAX98927_PDM_Tx_HiZ_Control_PDM_TX_HIZ = (0x1 << 0), + + /* PDM Tx Control (Address 0x0034)*/ + MAX98927_PDM_Tx_Control = 0x0034, + MAX98927_PDM_Tx_Control_PDM_TX_CH0_SOURCE = (0x1 << 0), + MAX98927_PDM_Tx_Control_PDM_TX_CH1_SOURCE = (0x1 << 1), + + /* PDM Rx Enable (Address 0x0034)*/ + MAX98927_PDM_Rx_Enable = 0x0035, + MAX98927_PDM_Rx_Enable_PDM_RX_EN = (0x1 << 0), + MAX98927_PDM_Rx_Enable_PDM_DSP_EN = (0x1 << 1), + MAX98927_PDM_Rx_Enable_PDM_DITH_EN = (0x1 << 2), + MAX98927_PDM_Rx_Enable_PDM_RX_CH_SEL = (0x1 << 3), + MAX98927_PDM_Rx_Enable_PDM_FIFO_RDY_LVL_Mask = (0xf << 4), + + /* AMP volume control (Address 0x0036)*/ + MAX98927_AMP_volume_control = 0x0036, + MAX98927_AMP_volume_control_AMP_VOL_Mask = (0x7f << 0), + MAX98927_AMP_volume_control_AMP_VOL_SEL = (0x1 << 7), + + /* AMP DSP Config (Address 0x0037)*/ + MAX98927_AMP_DSP_Config = 0x0037, + MAX98927_AMP_DSP_Config_AMP_DCBLK_EN = (0x1 << 0), + MAX98927_AMP_DSP_Config_AMP_DITH_EN = (0x1 << 1), + MAX98927_AMP_DSP_Config_DAC_HALF_REF_CURRENT = (0x1 << 2), + MAX98927_AMP_DSP_Config_DAC_DOUBLE_RFB = (0x1 << 3), + MAX98927_AMP_DSP_Config_AMP_VOL_RMP_BYPASS = (0x1 << 4), + MAX98927_AMP_DSP_Config_DAC_INVERT = (0x1 << 5), + + /* Tone Generator and DC Config (Address 0x0038)*/ + MAX98927_Tone_Generator_and_DC_Config = 0x0038, + MAX98927_Tone_Generator_and_DC_Config_TONE_CONFIG_Mask = (0xf << 0), + + /* DRE Control (Address 0x0039)*/ + MAX98927_DRE_Control = 0x0039, + MAX98927_DRE_Control_DRE_EN = (0x1 << 0), + + /* AMP enables (Address 0x003a)*/ + MAX98927_AMP_enables = 0x003a, + MAX98927_AMP_enables_SPK_EN = (0x1 << 0), + + /* Speaker source select (Address 0x003b)*/ + MAX98927_Speaker_source_select = 0x003b, + MAX98927_Speaker_source_select_SPK_SOURCE_Mask = (0x3 << 0), + MAX98927_Speaker_source_select_SPK_SOURCE_01 = (0x0 << 0), + MAX98927_Speaker_source_select_SPK_SOURCE_11 = (0x2 << 0), + + /* Speaker Gain (Address 0x003c)*/ + MAX98927_Speaker_Gain = 0x003c, + MAX98927_Speaker_Gain_SPK_PCM_GAIN_Mask = (0x7 << 0), + MAX98927_Speaker_Gain_SPK_PCM_GAIN_001 = (0x0 << 0), + MAX98927_Speaker_Gain_SPK_PCM_GAIN_011 = (0x2 << 0), + MAX98927_Speaker_Gain_SPK_PCM_GAIN_101 = (0x4 << 0), + MAX98927_Speaker_Gain_SPK_PCM_GAIN_111 = (0x6 << 0), + MAX98927_Speaker_Gain_SPK_PDM_GAIN_Mask = (0x7 << 4), + MAX98927_Speaker_Gain_SPK_PDM_GAIN_001 = (0x0 << 4), + MAX98927_Speaker_Gain_SPK_PDM_GAIN_011 = (0x2 << 4), + MAX98927_Speaker_Gain_SPK_PDM_GAIN_101 = (0x4 << 4), + MAX98927_Speaker_Gain_SPK_PDM_GAIN_111 = (0x6 << 4), + + /* SSM Configuration (Address 0x003d)*/ + MAX98927_SSM_Configuration = 0x003d, + MAX98927_SSM_Configuration_SSM_MOD_INDEX_Mask = (0x7 << 0), + MAX98927_SSM_Configuration_SSM_MOD_INDEX_001 = (0x0 << 0), + MAX98927_SSM_Configuration_SSM_MOD_INDEX_011 = (0x2 << 0), + MAX98927_SSM_Configuration_SSM_MOD_INDEX_101 = (0x4 << 0), + MAX98927_SSM_Configuration_SSM_MOD_INDEX_ = (0x6 << 0), + MAX98927_SSM_Configuration_SPK_FSW_SEL = (0x1 << 3), + MAX98927_SSM_Configuration_SSM_ENA = (0x1 << 7), + + /* Measurement enables (Address 0x003e)*/ + MAX98927_Measurement_enables = 0x003e, + MAX98927_Measurement_enables_IVADC_V_EN = (0x1 << 0), + MAX98927_Measurement_enables_IVADC_I_EN = (0x1 << 1), + + /* Measurement DSP Config (Address 0x003f)*/ + MAX98927_Measurement_DSP_Config = 0x003f, + MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_EN = (0x1 << 0), + MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_EN = (0x1 << 1), + MAX98927_Measurement_DSP_Config_MEAS_DITH_EN = (0x1 << 2), + MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_Mask = (0x3 << 4), + MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_01 = (0x0 << 4), + MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_11 = (0x2 << 4), + MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_Mask = (0x3 << 6), + MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_01 = (0x0 << 6), + MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_11 = (0x2 << 6), + + /* Boost Control 0 (Address 0x0040)*/ + MAX98927_Boost_Control_0 = 0x0040, + MAX98927_Boost_Control_0_BST_VOUT_Mask = (0x1f << 0), + MAX98927_Boost_Control_0_EXT_PVDD_EN = (0x1 << 7), + + /* Boost Control 3 (Address 0x0041)*/ + MAX98927_Boost_Control_3 = 0x0041, + MAX98927_Boost_Control_3_BST_SKIPLOAD_Mask = (0x3 << 0), + MAX98927_Boost_Control_3_BST_SKIPLOAD_01 = (0x0 << 0), + MAX98927_Boost_Control_3_BST_SKIPLOAD_11 = (0x2 << 0), + MAX98927_Boost_Control_3_BST_PHASE_Mask = (0x7 << 2), + MAX98927_Boost_Control_3_BST_PHASE_001 = (0x0 << 2), + MAX98927_Boost_Control_3_BST_PHASE_011 = (0x2 << 2), + MAX98927_Boost_Control_3_BST_PHASE_ = (0x1 << 2), + MAX98927_Boost_Control_3_BST_SLOWSTART = (0x1 << 5), + + /* Boost Control 1 (Address 0x0042)*/ + MAX98927_Boost_Control_1 = 0x0042, + /*#BYHAND width >= 5:*/ + MAX98927_Boost_Control_1_BST_ILIM_Mask = (0x3f << 0), + + /* Meas ADC Config (Address 0x0043)*/ + MAX98927_Meas_ADC_Config = 0x0043, + MAX98927_Meas_ADC_Config_MEAS_ADC_CH0_EN = (0x1 << 0), + MAX98927_Meas_ADC_Config_MEAS_ADC_CH1_EN = (0x1 << 1), + MAX98927_Meas_ADC_Config_MEAS_ADC_CH2_EN = (0x1 << 2), + + /* Meas ADC Base Divide MSByte (Address 0x0044)*/ + MAX98927_Meas_ADC_Base_Divide_MSByte = 0x0044, + /*#BYHAND width >= 5:*/ + MAX98927_Meas_ADC_Base_Divide_MSByte_MEAS_ADC_BASE_DIV_Mask + = (0xff << 0), + + /* Meas ADC Base Divide LSByte (Address 0x0045)*/ + MAX98927_Meas_ADC_Base_Divide_LSByte = 0x0045, + /*#BYHAND width >= 5:*/ + MAX98927_Meas_ADC_Base_Divide_LSByte_MEAS_ADC_BASE_DIV_Mask + = (0xff << 0), + + /* Meas ADC Chan 0 Divide (Address 0x0046)*/ + MAX98927_Meas_ADC_Chan_0_Divide = 0x0046, + /*#BYHAND width >= 5:*/ + MAX98927_Meas_ADC_Chan_0_Divide_MEAS_ADC_CH0_DIV_Mask = (0xff << 0), + + /* Meas ADC Chan 1 Divide (Address 0x0047)*/ + MAX98927_Meas_ADC_Chan_1_Divide = 0x0047, + /*#BYHAND width >= 5:*/ + MAX98927_Meas_ADC_Chan_1_Divide_MEAS_ADC_CH1_DIV_Mask = (0xff << 0), + + /* Meas ADC Chan 2 Divide (Address 0x0048)*/ + MAX98927_Meas_ADC_Chan_2_Divide = 0x0048, + /*#BYHAND width >= 5:*/ + MAX98927_Meas_ADC_Chan_2_Divide_MEAS_ADC_CH2_DIV_Mask = (0xff << 0), + + /* Meas ADC Chan 0 Filt Config (Address 0x0049)*/ + MAX98927_Meas_ADC_Chan_0_Filt_Config = 0x0049, + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_Mask + = (0x7 << 0), + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_001 + = (0x0 << 0), + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_011 + = (0x2 << 0), + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_101 + = (0x4 << 0), + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_EN + = (0x1 << 3), + + /* Meas ADC Chan 1 Filt Config (Address 0x004a)*/ + MAX98927_Meas_ADC_Chan_1_Filt_Config = 0x004a, + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_Mask + = (0x7 << 0), + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_001 + = (0x0 << 0), + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_011 + = (0x2 << 0), + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_101 + = (0x4 << 0), + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_EN + = (0x1 << 3), + + /* Meas ADC Chan 2 Filt Config (Address 0x004b)*/ + MAX98927_Meas_ADC_Chan_2_Filt_Config = 0x004b, + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_Mask + = (0x7 << 0), + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_001 + = (0x0 << 0), + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_011 + = (0x2 << 0), + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_101 + = (0x4 << 0), + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_EN + = (0x1 << 3), + + /* Meas ADC Chan 0 Readback (Address 0x004c)*/ + MAX98927_Meas_ADC_Chan_0_Readback = 0x004c, + /*#BYHAND width >= 5:*/ + MAX98927_Meas_ADC_Chan_0_Readback_MEAS_ADC_CH0_DATA_Mask + = (0xff << 0), + + /* Meas ADC Chan 1 Readback (Address 0x004d)*/ + MAX98927_Meas_ADC_Chan_1_Readback = 0x004d, + /*#BYHAND width >= 5:*/ + MAX98927_Meas_ADC_Chan_1_Readback_MEAS_ADC_CH1_DATA_Mask + = (0xff << 0), + + /* Meas ADC Chan 2 Readback (Address 0x004e)*/ + MAX98927_Meas_ADC_Chan_2_Readback = 0x004e, + /*#BYHAND width >= 5:*/ + MAX98927_Meas_ADC_Chan_2_Readback_MEAS_ADC_CH2_DATA_Mask + = (0xff << 0), + + /* Brownout status (Address 0x0051)*/ + MAX98927_Brownout_status = 0x0051, + MAX98927_Brownout_status_BDE_STATE_Mask = (0xf << 0), + + /* Brownout enables (Address 0x0052)*/ + MAX98927_Brownout_enables = 0x0052, + MAX98927_Brownout_enables_BDE_EN = (0x1 << 0), + MAX98927_Brownout_enables_BDE_AMP_EN = (0x1 << 1), + MAX98927_Brownout_enables_AMP_DSP_EN = (0x1 << 2), + + /* Brownout level infinite hold (Address 0x0053)*/ + MAX98927_Brownout_level_infinite_hold = 0x0053, + MAX98927_Brownout_level_infinite_hold_BDE_L8_INF_HLD = (0x1 << 1), + + /* Brownout level infinite hold clear (Address 0x0054)*/ + MAX98927_Brownout_level_infinite_hold_clear = 0x0054, + MAX98927_Brownout_level_infinite_hold_clear_BDE_L8_HLD_RLS + = (0x1 << 1), + + /* Brownout level hold (Address 0x0055)*/ + MAX98927_Brownout_level_hold = 0x0055, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout_level_hold_BDE_HLD_Mask = (0xff << 0), + + /* Brownout level 1 threshold (Address 0x0056)*/ + MAX98927_Brownout__level_1_threshold = 0x0056, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_1_threshold_BDE_L1_VTHRESH_Mask = (0xff << 0), + + /* Brownout level 2 threshold (Address 0x0057)*/ + MAX98927_Brownout__level_2_threshold = 0x0057, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_2_threshold_BDE_L2_VTHRESH_Mask = (0xff << 0), + + /* Brownout level 3 threshold (Address 0x0058)*/ + MAX98927_Brownout__level_3_threshold = 0x0058, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_3_threshold_BDE_L3_VTHRESH_Mask = (0xff << 0), + + /* Brownout level 4 threshold (Address 0x0059)*/ + MAX98927_Brownout__level_4_threshold = 0x0059, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_4_threshold_BDE_L4_VTHRESH_Mask = (0xff << 0), + + /* Brownout level 5 threshold (Address 0x005a)*/ + MAX98927_Brownout__level_5_threshold = 0x005a, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_5_threshold_BDE_L5_VTHRESH_Mask = (0xff << 0), + + /* Brownout level 6 threshold (Address 0x005b)*/ + MAX98927_Brownout__level_6_threshold = 0x005b, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_6_threshold_BDE_L6_VTHRESH_Mask = (0xff << 0), + + /* Brownout level 7 threshold (Address 0x005c)*/ + MAX98927_Brownout__level_7_threshold = 0x005c, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_7_threshold_BDE_L7_VTHRESH_Mask = (0xff << 0), + + /* Brownout level 8 threshold (Address 0x005d)*/ + MAX98927_Brownout__level_8_threshold = 0x005d, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_8_threshold_BDE_L8_VTHRESH_Mask = (0xff << 0), + + /* Brownout threshold hysterysis (Address 0x005e)*/ + MAX98927_Brownout_threshold_hysterysis = 0x005e, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout_threshold_hysterysis_BDE_VTHRESH_HYST_Mask + = (0xff << 0), + /* Brownout AMP limiter attack/release (Address 0x005f)*/ + MAX98927_Brownout_AMP_limiter_attack_release = 0x005f, + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_Mask + = (0xf << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0001 + = (0x0 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0011 + = (0x2 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0101 + = (0x4 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0111 + = (0x6 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1001 + = (0x8 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1011 + = (0xa << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1101 + = (0xc << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1111 + = (0xe << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_Mask + = (0xf << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0001 + = (0x0 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0011 + = (0x2 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0101 + = (0x4 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0111 + = (0x6 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1001 + = (0x8 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1011 + = (0xa << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1101 + = (0xc << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1111 + = (0xe << 4), + + /* Brownout AMP gain attack/release (Address 0x0060)*/ + MAX98927_Brownout_AMP_gain_attack_release = 0x0060, + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_Mask + = (0xf << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0001 + = (0x0 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0011 + = (0x2 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0101 + = (0x4 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0111 + = (0x6 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1001 + = (0x8 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1011 + = (0xa << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1101 + = (0xc << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1111 + = (0xe << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_Mask + = (0xf << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0001 + = (0x0 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0011 + = (0x2 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0101 + = (0x4 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0111 + = (0x6 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1001 + = (0x8 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1011 + = (0xa << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1101 + = (0xc << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1111 + = (0xe << 4), + + /* Brownout AMP1 clip mode (Address 0x0061)*/ + MAX98927_Brownout_AMP1_clip_mode = 0x0061, + MAX98927_Brownout_AMP1_clip_mode_AMP_CLIP_MODE = (0x1 << 0), + + /* Brownout level 1 current limit (Address 0x0062)*/ + MAX98927_Brownout__level_1_current_limit = 0x0062, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_1_current_limit_BDE_L1_ILIM_Mask + = (0x3f << 0), + + /* Brownout level 1 amp 1 control 1 (Address 0x0063)*/ + MAX98927_Brownout__level_1_amp_1_control_1 = 0x0063, + MAX98927_Brownout__level_1_amp_1_control_1_BDE_L1_AMP1_LIM_Mask + = (0xf << 0), + + /* Brownout level 1 amp 1 control 2 (Address 0x0064)*/ + MAX98927_Brownout__level_1_amp_1_control_2 = 0x0064, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_1_amp_1_control_2_BDE_L1_AMP1_CLIP_Mask + = (0x3f << 0), + + /* Brownout level 1 amp 1 control 3 (Address 0x0065)*/ + MAX98927_Brownout__level_1_amp_1_control_3 = 0x0065, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_1_amp_1_control_3_BDE_L1_AMP1_GAIN_Mask + = (0x3f << 0), + + /* Brownout level 2 current limit (Address 0x0066)*/ + MAX98927_Brownout__level_2_current_limit = 0x0066, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_2_current_limit_BDE_L2_ILIM_Mask = (0x3f << 0), + + /* Brownout level 2 amp 1 control 1 (Address 0x0067)*/ + MAX98927_Brownout__level_2_amp_1_control_1 = 0x0067, + MAX98927_Brownout__level_2_amp_1_control_1_BDE_L2_AMP1_LIM_Mask + = (0xf << 0), + + /* Brownout level 2 amp 1 control 2 (Address 0x0068)*/ + MAX98927_Brownout__level_2_amp_1_control_2 = 0x0068, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_2_amp_1_control_2_BDE_L2_AMP1_CLIP_Mask + = (0x3f << 0), + + /* Brownout level 2 amp 1 control 3 (Address 0x0069)*/ + MAX98927_Brownout__level_2_amp_1_control_3 = 0x0069, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_2_amp_1_control_3_BDE_L2_AMP1_GAIN_Mask + = (0x3f << 0), + + /* Brownout level 3 current limit (Address 0x006a)*/ + MAX98927_Brownout__level_3_current_limit = 0x006a, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_3_current_limit_BDE_L3_ILIM_Mask = (0x3f << 0), + + /* Brownout level 3 amp 1 control 1 (Address 0x006b)*/ + MAX98927_Brownout__level_3_amp_1_control_1 = 0x006b, + MAX98927_Brownout__level_3_amp_1_control_1_BDE_L3_AMP1_LIM_Mask + = (0xf << 0), + + /* Brownout level 3 amp 1 control 2 (Address 0x006c)*/ + MAX98927_Brownout__level_3_amp_1_control_2 = 0x006c, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_3_amp_1_control_2_BDE_L3_AMP1_CLIP_Mask + = (0x3f << 0), + + /* Brownout level 3 amp 1 control 3 (Address 0x006d)*/ + MAX98927_Brownout__level_3_amp_1_control_3 = 0x006d, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_3_amp_1_control_3_BDE_L3_AMP1_GAIN_Mask + = (0x3f << 0), + + /* Brownout level 4 current limit (Address 0x006e)*/ + MAX98927_Brownout__level_4_current_limit = 0x006e, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_4_current_limit_BDE_L4_ILIM_Mask = (0x3f << 0), + + /* Brownout level 4 amp 1 control 1 (Address 0x006f)*/ + MAX98927_Brownout__level_4_amp_1_control_1 = 0x006f, + MAX98927_Brownout__level_4_amp_1_control_1_BDE_L4_AMP1_LIM_Mask + = (0xf << 0), + + /* Brownout level 4 amp 1 control 2 (Address 0x0070)*/ + MAX98927_Brownout__level_4_amp_1_control_2 = 0x0070, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_4_amp_1_control_2_BDE_L4_AMP1_CLIP_Mask + = (0x3f << 0), + + /* Brownout level 4 amp 1 control 3 (Address 0x0071)*/ + MAX98927_Brownout__level_4_amp_1_control_3 = 0x0071, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_4_amp_1_control_3_BDE_L4_AMP1_GAIN_Mask + = (0x3f << 0), + + /* Brownout level 5 current limit (Address 0x0072)*/ + MAX98927_Brownout__level_5_current_limit = 0x0072, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_5_current_limit_BDE_L5_ILIM_Mask = (0x3f << 0), + + /* Brownout level 5 amp 1 control 1 (Address 0x0073)*/ + MAX98927_Brownout__level_5_amp_1_control_1 = 0x0073, + MAX98927_Brownout__level_5_amp_1_control_1_BDE_L5_AMP1_LIM_Mask + = (0xf << 0), + + /* Brownout level 5 amp 1 control 2 (Address 0x0074)*/ + MAX98927_Brownout__level_5_amp_1_control_2 = 0x0074, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_5_amp_1_control_2_BDE_L5_AMP1_CLIP_Mask + = (0x3f << 0), + + /* Brownout level 5 amp 1 control 3 (Address 0x0075)*/ + MAX98927_Brownout__level_5_amp_1_control_3 = 0x0075, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_5_amp_1_control_3_BDE_L5_AMP1_GAIN_Mask + = (0x3f << 0), + + /* Brownout level 6 current limit (Address 0x0076)*/ + MAX98927_Brownout__level_6_current_limit = 0x0076, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_6_current_limit_BDE_L6_ILIM_Mask = (0x3f << 0), + + /* Brownout level 6 amp 1 control 1 (Address 0x0077)*/ + MAX98927_Brownout__level_6_amp_1_control_1 = 0x0077, + MAX98927_Brownout__level_6_amp_1_control_1_BDE_L6_AMP1_LIM_Mask + = (0xf << 0), + + /* Brownout level 6 amp 1 control 2 (Address 0x0078)*/ + MAX98927_Brownout__level_6_amp_1_control_2 = 0x0078, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_6_amp_1_control_2_BDE_L6_AMP1_CLIP_Mask + = (0x3f << 0), + + /* Brownout level 6 amp 1 control 3 (Address 0x0079)*/ + MAX98927_Brownout__level_6_amp_1_control_3 = 0x0079, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_6_amp_1_control_3_BDE_L6_AMP1_GAIN_Mask + = (0x3f << 0), + + /* Brownout level 7 current limit (Address 0x007a)*/ + MAX98927_Brownout__level_7_current_limit = 0x007a, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_7_current_limit_BDE_L7_ILIM_Mask = (0x3f << 0), + + /* Brownout level 7 amp 1 control 1 (Address 0x007b)*/ + MAX98927_Brownout__level_7_amp_1_control_1 = 0x007b, + MAX98927_Brownout__level_7_amp_1_control_1_BDE_L7_AMP1_LIM_Mask + = (0xf << 0), + + /* Brownout level 7 amp 1 control 2 (Address 0x007c)*/ + MAX98927_Brownout__level_7_amp_1_control_2 = 0x007c, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_7_amp_1_control_2_BDE_L7_AMP1_CLIP_Mask + = (0x3f << 0), + + /* Brownout level 7 amp 1 control 3 (Address 0x007d)*/ + MAX98927_Brownout__level_7_amp_1_control_3 = 0x007d, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_7_amp_1_control_3_BDE_L7_AMP1_GAIN_Mask + = (0x3f << 0), + + /* Brownout level 8 current limit (Address 0x007e)*/ + MAX98927_Brownout__level_8_current_limit = 0x007e, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_8_current_limit_BDE_L8_ILIM_Mask = (0x3f << 0), + + /* Brownout level 8 amp 1 control 1 (Address 0x007f)*/ + MAX98927_Brownout__level_8_amp_1_control_1 = 0x007f, + MAX98927_Brownout__level_8_amp_1_control_1_BDE_L8_AMP1_LIM_Mask + = (0xf << 0), + + /* Brownout level 8 amp 1 control 2 (Address 0x0080)*/ + MAX98927_Brownout__lvl_8_amp_1_control_2 = 0x0080, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__lvl_8_amp_1_control_2_BDE_L8_AMP1_CLIP_Mask + = (0x3f << 0), + MAX98927_Brownout__lvl_8_amp_1_control_2_BDE_L8_AMP1_MUTE + = (0x1 << 7), + + /* Brownout level 8 amp 1 control 3 (Address 0x0081)*/ + MAX98927_Brownout__level_8_amp_1_control_3 = 0x0081, + /*#BYHAND width >= 5:*/ + MAX98927_Brownout__level_8_amp_1_control_3_BDE_L8_AMP1_GAIN_Mask + = (0x3f << 0), + + /* Env Tracker Vout Headroom (Address 0x0082)*/ + MAX98927_Env_Tracker_Vout_Headroom = 0x0082, + /*#BYHAND width >= 5:*/ + MAX98927_Env_Tracker_Vout_Head_ENV_TRACKER_BST_VOUT_HEADROOM_Mask + = (0x1f << 0), + + /* Env Tracker Boost Vout Delay (Address 0x0083)*/ + MAX98927_Env_Tracker_Boost_Vout_Delay = 0x0083, + /*#BYHAND width >= 5:*/ + MAX98927_Env_Tracker_Boost_V_Delay_ENV_TRACKER_BST_VOUT_DELAY_Mask + = (0x1f << 0), + MAX98927_Env_Tracker_Boost_Vout_Delay_ENV_TRACKER_BDE_MODE + = (0x1 << 7), + + /* Env Tracker Release Rate (Address 0x0084)*/ + MAX98927_Env_Tracker_Release_Rate = 0x0084, + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_Mask + = (0x7 << 0), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_001 + = (0x0 << 0), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_011 + = (0x2 << 0), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_101 + = (0x4 << 0), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_111 + = (0x6 << 0), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_PEAK_DET_LPF_BYP_EN + = (0x1 << 3), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_SCALE_Mask + = (0x3 << 4), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_SCALE_01 + = (0x0 << 4), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_SCALE_11 + = (0x2 << 4), + + /* Env Tracker Hold Rate (Address 0x0085)*/ + MAX98927_Env_Tracker_Hold_Rate = 0x0085, + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_Mask + = (0x7 << 0), + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_001 + = (0x0 << 0), + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_011 + = (0x2 << 0), + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_101 + = (0x4 << 0), + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_111 + = (0x6 << 0), + + /* Env Tracker Control (Address 0x0086)*/ + MAX98927_Env_Tracker_Control = 0x0086, + MAX98927_Env_Tracker_Control_ENV_TRACKER_EN = (0x1 << 0), + + /* Env Tracker Boost Vout ReadBack (Address 0x0087)*/ + MAX98927_Env_Tracker__Boost_Vout_ReadBack = 0x0087, + /*#BYHAND width >= 5:*/ + MAX98927_Env_Tracker__Boost_Vout_RB_ENV_TRACKER_BST_VOUT_RD_Mask + = (0x1f << 0), + + /* Advanced Settings (Address 0x0089)*/ + MAX98927_Advanced_Settings = 0x0089, + MAX98927_Advanced_Settings_DAC_HALF_FIR = (0x1 << 0), + MAX98927_Advanced_Settings_PDM_MOD_SEL = (0x1 << 1), + MAX98927_Advanced_Settings_ISOCH_EN = (0x1 << 2), + + /* DAC Test 1 (Address 0x009f)*/ + MAX98927_DAC_Test_1 = 0x009f, + MAX98927_DAC_Test_1_DAC_PCM_TIMING = (0x1 << 0), + MAX98927_DAC_Test_1_DAC_HALFI_AMP = (0x1 << 1), + MAX98927_DAC_Test_1_DAC_LONG_HOLD = (0x1 << 3), + MAX98927_DAC_Test_1_DAC_DISABLE_CHOP = (0x1 << 4), + MAX98927_DAC_Test_1_DAC_TM = (0x1 << 5), + MAX98927_DAC_Test_1_DAC_INVERT_DACCLK = (0x1 << 6), + + /* Authentication key 0 (Address 0x00ea)*/ + MAX98927_Authentication_key_0 = 0x00ea, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_key_0_AUTH_KEY_Mask = (0xff << 0), + + /* Authentication key 1 (Address 0x00eb)*/ + MAX98927_Authentication_key_1 = 0x00eb, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_key_1_AUTH_KEY_Mask = (0xff << 0), + + /* Authentication key 2 (Address 0x00ec)*/ + MAX98927_Authentication_key_2 = 0x00ec, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_key_2_AUTH_KEY_Mask = (0xff << 0), + + /* Authentication key 3 (Address 0x00ed)*/ + MAX98927_Authentication_key_3 = 0x00ed, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_key_3_AUTH_KEY_Mask = (0xff << 0), + + /* Authentication enable (Address 0x00ee)*/ + MAX98927_Authentication_enable = 0x00ee, + MAX98927_Authentication_enable_AUTH_EN = (0x1 << 0), + + /* Authentication result 0 (Address 0x00ef)*/ + MAX98927_Authentication_result_0 = 0x00ef, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_0_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 1 (Address 0x00f0)*/ + MAX98927_Authentication_result_1 = 0x00f0, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_1_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 2 (Address 0x00f1)*/ + MAX98927_Authentication_result_2 = 0x00f1, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_2_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 3 (Address 0x00f2)*/ + MAX98927_Authentication_result_3 = 0x00f2, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_3_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 4 (Address 0x00f3)*/ + MAX98927_Authentication_result_4 = 0x00f3, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_4_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 5 (Address 0x00f4)*/ + MAX98927_Authentication_result_5 = 0x00f4, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_5_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 6 (Address 0x00f5)*/ + MAX98927_Authentication_result_6 = 0x00f5, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_6_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 7 (Address 0x00f6)*/ + MAX98927_Authentication_result_7 = 0x00f6, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_7_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 8 (Address 0x00f7)*/ + MAX98927_Authentication_result_8 = 0x00f7, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_8_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 9 (Address 0x00f8)*/ + MAX98927_Authentication_result_9 = 0x00f8, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_9_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 10 (Address 0x00f9)*/ + MAX98927_Authentication_result_10 = 0x00f9, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_10_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 11 (Address 0x00fa)*/ + MAX98927_Authentication_result_11 = 0x00fa, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_11_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 12 (Address 0x00fb)*/ + MAX98927_Authentication_result_12 = 0x00fb, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_12_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 13 (Address 0x00fc)*/ + MAX98927_Authentication_result_13 = 0x00fc, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_13_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 14 (Address 0x00fd)*/ + MAX98927_Authentication_result_14 = 0x00fd, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_14_AUTH_RESULT_Mask = (0xff << 0), + + /* Authentication result 15 (Address 0x00fe)*/ + MAX98927_Authentication_result_15 = 0x00fe, + /*#BYHAND width >= 5:*/ + MAX98927_Authentication_result_15_AUTH_RESULT_Mask = (0xff << 0), + + /* Global Enable (Address 0x00ff)*/ + MAX98927_Global_Enable = 0x00ff, + MAX98927_Global_Enable_EN = (0x1 << 0), + /* Software Reset (Address 0x0100)*/ + MAX98927_Software_Reset = 0x0100, + MAX98927_Software_Reset_RST = (0x1 << 0), + + /* REV ID (Address 0x01ff)*/ + MAX98927_REV_ID = 0x01ff, + /*#BYHAND width >= 5:*/ + MAX98927_REV_ID_REV_ID_Mask = (0xff << 0), +} MAX98927Registers; + +struct max98927_reg_default { + unsigned int ch; + unsigned int reg; + unsigned int def; +}; +struct max98927_priv { + struct regmap *regmap; + struct regmap *sub_regmap; + struct snd_soc_codec *codec; + struct max98927_pdata *pdata; + const uint32_t *regcfg; + uint32_t regcfg_sz; + unsigned int spk_gain; + unsigned int sysclk; + unsigned int v_l_slot; + unsigned int i_l_slot; + unsigned int v_r_slot; + unsigned int i_r_slot; + bool interleave_mode; + unsigned int ch_size; + unsigned int rate; + unsigned int iface; + unsigned int master; + unsigned int thres_hyste; + unsigned int level5_hold; + unsigned int level6_hold; + unsigned int level7_hold; + unsigned int level8_hold; + unsigned int amp_limit; + unsigned int amp_limit_rel; + unsigned int amp1_level; + unsigned int amp2_level; + unsigned int amp3_level; + unsigned int amp1_level8; + unsigned int amp2_level8; + unsigned int amp3_level8; + unsigned int amp1_level7; + unsigned int amp2_level7; + unsigned int amp3_level7; + unsigned int amp1_level6; + unsigned int amp2_level6; + unsigned int amp3_level6; + unsigned int amp1_level5; + unsigned int amp2_level5; + unsigned int amp3_level5; + unsigned int digital_gain; + unsigned int pdm_gain; + unsigned int level_hold; + struct i2c_client *sub_i2c; + int sub_reg; + int sub_bus; +}; + +#define MAX98927_GLOBAL_SHIFT 0 +#define M98927_DAI_MSEL_SHIFT 4 +#define M98927_DAI_BSEL_SHIFT 0 +#define M98927_DAI_BSEL_32 (2 << M98927_DAI_BSEL_SHIFT) +#define M98927_DAI_BSEL_48 (3 << M98927_DAI_BSEL_SHIFT) +#define M98927_DAI_BSEL_64 (4 << M98927_DAI_BSEL_SHIFT) +#define M98927_DAI_MSEL_32 (2 << M98927_DAI_MSEL_SHIFT) +#define M98927_DAI_MSEL_48 (3 << M98927_DAI_MSEL_SHIFT) +#define M98927_DAI_MSEL_64 (4 << M98927_DAI_MSEL_SHIFT) +#define MAX98927_Speaker_Gain_Width 3 +#define MAX98927_SPK_RMP_EN_SHIFT 4 +#define MAX98927_PDM_GAIN_SHIFT 4 +#define MAX98927_pdm_Gain_Width 3 +#define MAX98927_AMP_VOL_WIDTH 7 +#define MAX98927_AMP_VOL_LOCATION_SHIFT 7 +#define MAX98927_PDM_Rx_Enable_PDM_CH_SHIFT 3 +#define MAX98927_PCM_to_speaker_monomix_A_SHIFT 6 +#define MAX98927_PCM_Sample_rate_setup_2_DIG_IF_SR_48000 (0x8 << 4) +#define MAX98927_PCM_FORMAT_DSP_A (0x3 << 3) +#define MAX98927_DRE_Control_DRE_SHIFT 0x1 +#define MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_SHIFT (2) +#define MAX98927_Brownout_AMP_limiter_attack_release_shift 4 +#define MAX98927_BDE_DSP_SHIFT 2 +#define MAX98927_Speaker_Gain_SPK_PDM_GAIN_SHIFT (4) +#define MAX98927_BDE_AMP_SHIFT (1) +#define MAX98927_PCM_Tx_Ch_Sources_A_I_SHIFT (4) +#endif -- 1.9.1 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2016-11-23 4:57 ` Ryan Lee @ 2016-11-23 18:13 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2016-11-23 18:13 UTC (permalink / raw) To: Ryan Lee Cc: kbuild-all, lgirdwood, broonie, robh+dt, mark.rutland, perex, tiwai, arnd, michael, oder_chiou, yesanishhere, jacob, Damien.Horsley, bardliao, kuninori.morimoto.gx, petr, lars, nh6z, ryans.lee, alsa-devel, devicetree, linux-kernel sound/soc/codecs/max98927.c:941:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Ryan Lee <RyanS.Lee@maximintegrated.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- max98927.c | 1 - 1 file changed, 1 deletion(-) --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -938,7 +938,6 @@ MODULE_DEVICE_TABLE(of, max98927_of_matc static struct i2c_driver max98927_i2c_driver = { .driver = { .name = "max98927", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(max98927_of_match), .pm = NULL, }, ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 1/2] add driver for cypress cy8cmbr3102
2017-02-20 10:33 ` Patrick Vogelaar
@ 2017-02-20 12:05 kbuild test robot
2017-02-20 10:33 ` Patrick Vogelaar
0 siblings, 1 reply; 40+ messages in thread
From: kbuild test robot @ 2017-02-20 12:05 UTC (permalink / raw)
To: Patrick Vogelaar
Cc: kbuild-all, dmitry.torokhov, linux-kernel, devicetree,
linux-input, Patrick Vogelaar
Hi Patrick,
[auto build test WARNING on input/next]
[also build test WARNING on v4.10 next-20170220]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Patrick-Vogelaar/add-driver-for-cypress-cy8cmbr3102/20170220-185014
base: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
coccinelle warnings: (new ones prefixed by >>)
>> drivers/input/misc/cy8cmbr3102.c:210:4-9: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 1/2] add driver for cypress cy8cmbr3102 @ 2017-02-20 10:33 ` Patrick Vogelaar 2017-02-20 12:05 ` kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: Patrick Vogelaar @ 2017-02-20 10:33 UTC (permalink / raw) To: dmitry.torokhov; +Cc: linux-kernel, devicetree, linux-input, Patrick Vogelaar Signed-off-by: Patrick Vogelaar <Patrick.Vogelaar@gigatronik.com> --- drivers/input/misc/Kconfig | 12 +++ drivers/input/misc/Makefile | 1 + drivers/input/misc/cy8cmbr3102.c | 222 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 drivers/input/misc/cy8cmbr3102.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 5b6c522..be3071e 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -822,4 +822,16 @@ config INPUT_HISI_POWERKEY To compile this driver as a module, choose M here: the module will be called hisi_powerkey. +config INPUT_CY8CMBR3102 + tristate "Cypress CY8CMBR3102 CapSense Express controller" + depends on I2C + select INPUT_POLLDEV + default n + help + Say yes here to enable support for the Cypress CY8CMBR3102 + CapSense Express controller. + + To compile this driver as a module, choose M here: the module + will be called cy8cmbr3102. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index b10523f..f9959ed 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -77,3 +77,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o +obj-$(CONFIG_INPUT_CY8CMBR3102) += cy8cmbr3102.o diff --git a/drivers/input/misc/cy8cmbr3102.c b/drivers/input/misc/cy8cmbr3102.c new file mode 100644 index 0000000..e2f5167 --- /dev/null +++ b/drivers/input/misc/cy8cmbr3102.c @@ -0,0 +1,222 @@ +/* Driver for Cypress CY8CMBR3102 CapSense Express Controller + * + * (C) 2017 by Gigatronik Technologies GmbH + * Author: Patrick Vogelaar <patrick.vogelaar@gigatronik.com> + * All rights reserved. + * + * This code is based on mma8450.c and atmel_captouch.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * NOTE: This implementation does not implement the full range of functions the + * Cypress CY8CMBR3102 CapSense Express controller provides. It only implements + * its use for connected touch buttons (yet). + */ + +#define DRV_VERSION "0.1" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/of.h> + +/* I2C Registers */ +#define CY8CMBR3102_DEVICE_ID_REG 0x90 +#define CY8CMBR3102_BUTTON_STAT 0xAA + + +#define CY8CMBR3102_MAX_NUM_OF_BUTTONS 0x02 +#define CY8CMBR3102_DRV_NAME "cy8cmbr3102" +#define CY8CMBR3102_POLL_INTERVAL 200 +#define CY8CMBR3102_POLL_INTERVAL_MAX 300 +#define CY8CMBR3102_DEVICE_ID 2561 +#define CY8CMBR3102_MAX_RETRY 5 + +/* + * @i2c_client: I2C slave device client pointer + * @idev: Input (polled) device pointer + * @num_btn: Number of buttons + * @keycodes: map of button# to KeyCode + * @cy8cmbr3102_lock: mutex lock + */ +struct cy8cmbr3102_device { + struct i2c_client *client; + struct input_polled_dev *idev; + u32 num_btn; + u32 keycodes[CY8CMBR3102_MAX_NUM_OF_BUTTONS]; + struct mutex cy8cmbr3102_lock; +}; + +static const struct i2c_device_id cy8cmbr3102_idtable[] = { + {"cy8cmbr3102", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cy8cmbr3102_idtable); + +static void cy8cmbr3102_poll(struct input_polled_dev *idev) +{ + struct cy8cmbr3102_device *dev = idev->private; + int i, ret, btn_state; + + mutex_lock(&dev->cy8cmbr3102_lock); + + ret = i2c_smbus_read_word_data(dev->client, CY8CMBR3102_BUTTON_STAT); + if (ret < 0) { + dev_err(&dev->client->dev, "i2c io error: %d\n", ret); + mutex_unlock(&dev->cy8cmbr3102_lock); + return; + } + + for (i = 0; i < dev->num_btn; i++) { + btn_state = ret & (0x1 << i); + input_report_key(idev->input, dev->keycodes[i], btn_state); + input_sync(idev->input); + } + + mutex_unlock(&dev->cy8cmbr3102_lock); +} + +static int cy8cmbr3102_remove(struct i2c_client *client) +{ + struct cy8cmbr3102_device *dev = i2c_get_clientdata(client); + struct input_polled_dev *idev = dev->idev; + + dev_dbg(&client->dev, "%s\n", __func__); + + mutex_destroy(&dev->cy8cmbr3102_lock); + input_unregister_polled_device(idev); + + return 0; +} + +static int cy8cmbr3102_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cy8cmbr3102_device *drvdata; + struct device *dev = &client->dev; + struct device_node *node; + int i, err, ret; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(dev, "needed i2c functionality is not supported\n"); + return -EINVAL; + } + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->client = client; + i2c_set_clientdata(client, drvdata); + + /* device is in low-power mode and needs to be waken up */ + for (i = 0; (i < CY8CMBR3102_MAX_RETRY) && + (ret != CY8CMBR3102_DEVICE_ID); i++) { + ret = i2c_smbus_read_word_data(drvdata->client, + CY8CMBR3102_DEVICE_ID_REG); + dev_dbg(dev, "DEVICE_ID (%i): %i\n", i, ret); + } + + if (ret < 0) { + dev_err(&client->dev, "i2c io error: %d\n", ret); + return -EIO; + } else if (ret != CY8CMBR3102_DEVICE_ID) { + dev_err(&client->dev, "read device ID %i is not equal to %i!\n", + ret, CY8CMBR3102_DEVICE_ID); + return -ENXIO; + } + dev_dbg(dev, "device identified by device ID\n"); + + drvdata->idev = devm_input_allocate_polled_device(dev); + if (!drvdata->idev) { + dev_err(dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + node = dev->of_node; + if (!node) { + dev_err(dev, "failed to find matching node in device tree\n"); + return -EINVAL; + } + + if (of_property_read_bool(node, "autorepeat")) + __set_bit(EV_REP, drvdata->idev->input->evbit); + + drvdata->num_btn = of_property_count_u32_elems(node, "linux,keycodes"); + if (drvdata->num_btn > CY8CMBR3102_MAX_NUM_OF_BUTTONS) + drvdata->num_btn = CY8CMBR3102_MAX_NUM_OF_BUTTONS; + + err = of_property_read_u32_array(node, "linux,keycodes", + drvdata->keycodes, drvdata->num_btn); + + if (err) { + dev_err(dev, "failed to read linux,keycodes property: %d\n", + err); + return err; + } + + for (i = 0; i < drvdata->num_btn; i++) + __set_bit(drvdata->keycodes[i], drvdata->idev->input->keybit); + + drvdata->idev->input->id.bustype = BUS_I2C; + drvdata->idev->input->id.product = 0x3102; + drvdata->idev->input->id.version = 0; + drvdata->idev->input->name = CY8CMBR3102_DRV_NAME; + drvdata->idev->poll = cy8cmbr3102_poll; + drvdata->idev->poll_interval = CY8CMBR3102_POLL_INTERVAL; + drvdata->idev->poll_interval_max = CY8CMBR3102_POLL_INTERVAL_MAX; + drvdata->idev->private = drvdata; + drvdata->idev->input->keycode = drvdata->keycodes; + drvdata->idev->input->keycodemax = drvdata->num_btn; + drvdata->idev->input->keycodesize = sizeof(drvdata->keycodes[0]); + __set_bit(EV_KEY, drvdata->idev->input->evbit); + + mutex_init(&drvdata->cy8cmbr3102_lock); + + err = input_register_polled_device(drvdata->idev); + if (err) + return err; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id of_cy8cmbr3102_match[] = { + {.compatible = "cypress,cy8cmbr3102", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_cy8cmbr3102_match); +#endif + +static struct i2c_driver cy8cmbr3102_driver = { + .driver = { + .name = "cy8cmbr3102", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_cy8cmbr3102_match), + }, + .probe = cy8cmbr3102_probe, + .remove = cy8cmbr3102_remove, + .id_table = cy8cmbr3102_idtable, +}; +module_i2c_driver(cy8cmbr3102_driver); + +MODULE_AUTHOR("Patrick Vogelaar <patrick.vogelaar@gigatronik.com>"); +MODULE_DESCRIPTION("Cypress CY8CMBR3102 CapSense Express controller"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); -- 2.7.4 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2017-02-20 10:33 ` Patrick Vogelaar @ 2017-02-20 12:05 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2017-02-20 12:05 UTC (permalink / raw) To: Patrick Vogelaar Cc: kbuild-all, dmitry.torokhov, linux-kernel, devicetree, linux-input, Patrick Vogelaar drivers/input/misc/cy8cmbr3102.c:210:4-9: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Patrick Vogelaar <Patrick.Vogelaar@gigatronik.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- cy8cmbr3102.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/input/misc/cy8cmbr3102.c +++ b/drivers/input/misc/cy8cmbr3102.c @@ -207,7 +207,6 @@ MODULE_DEVICE_TABLE(of, of_cy8cmbr3102_m static struct i2c_driver cy8cmbr3102_driver = { .driver = { .name = "cy8cmbr3102", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_cy8cmbr3102_match), }, .probe = cy8cmbr3102_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings @ 2017-02-20 12:05 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2017-02-20 12:05 UTC (permalink / raw) Cc: kbuild-all, dmitry.torokhov, linux-kernel, devicetree, linux-input, Patrick Vogelaar drivers/input/misc/cy8cmbr3102.c:210:4-9: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Patrick Vogelaar <Patrick.Vogelaar@gigatronik.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> --- cy8cmbr3102.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/input/misc/cy8cmbr3102.c +++ b/drivers/input/misc/cy8cmbr3102.c @@ -207,7 +207,6 @@ MODULE_DEVICE_TABLE(of, of_cy8cmbr3102_m static struct i2c_driver cy8cmbr3102_driver = { .driver = { .name = "cy8cmbr3102", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_cy8cmbr3102_match), }, .probe = cy8cmbr3102_probe, ^ permalink raw reply [flat|nested] 40+ messages in thread
* [nomadik:ixp4 4/9] drivers/gpio/gw_i2c_pld.c:25:10: fatal error: linux/i2c/gw_i2c_pld.h: No such file or directory @ 2018-12-30 5:10 kbuild test robot 2018-12-30 5:10 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 0 siblings, 1 reply; 40+ messages in thread From: kbuild test robot @ 2018-12-30 5:10 UTC (permalink / raw) To: Linus Walleij; +Cc: kbuild-all, linux-arm-kernel [-- Attachment #1: Type: text/plain, Size: 1252 bytes --] tree: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git ixp4 head: fd04bd59327ab42cfa79c1a5aaa3160fc4821402 commit: 443f1c782cc742e40a1ef4cb2bca0afa2fea9baf [4/9] GW i2c PLF GPIO config: i386-allmodconfig (attached as .config) compiler: gcc-7 (Debian 7.3.0-1) 7.3.0 reproduce: git checkout 443f1c782cc742e40a1ef4cb2bca0afa2fea9baf # save the attached .config to linux build tree make ARCH=i386 All errors (new ones prefixed by >>): >> drivers/gpio/gw_i2c_pld.c:25:10: fatal error: linux/i2c/gw_i2c_pld.h: No such file or directory #include <linux/i2c/gw_i2c_pld.h> ^~~~~~~~~~~~~~~~~~~~~~~~ compilation terminated. coccinelle warnings: (new ones prefixed by >>) >> drivers/gpio/gw_i2c_pld.c:351:3-8: No need to set .owner here. The core will do it. Please review and possibly fold the followup patch. vim +25 drivers/gpio/gw_i2c_pld.c > 25 #include <linux/i2c/gw_i2c_pld.h> 26 #include <linux/module.h> 27 #include <linux/export.h> 28 #include <asm/gpio.h> 29 #include <mach/hardware.h> 30 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 66063 bytes --] [-- Attachment #3: Type: text/plain, Size: 176 bytes --] _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH] fix platform_no_drv_owner.cocci warnings 2018-12-30 5:10 [nomadik:ixp4 4/9] drivers/gpio/gw_i2c_pld.c:25:10: fatal error: linux/i2c/gw_i2c_pld.h: No such file or directory kbuild test robot @ 2018-12-30 5:10 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2018-12-30 5:10 UTC (permalink / raw) To: Linus Walleij; +Cc: kbuild-all, linux-arm-kernel From: kbuild test robot <fengguang.wu@intel.com> drivers/gpio/gw_i2c_pld.c:351:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Fixes: 443f1c782cc7 ("GW i2c PLF GPIO") Signed-off-by: kbuild test robot <fengguang.wu@intel.com> --- tree: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git ixp4 head: fd04bd59327ab42cfa79c1a5aaa3160fc4821402 commit: 443f1c782cc742e40a1ef4cb2bca0afa2fea9baf [4/9] GW i2c PLF GPIO gw_i2c_pld.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/gpio/gw_i2c_pld.c +++ b/drivers/gpio/gw_i2c_pld.c @@ -348,7 +348,6 @@ static int gw_i2c_pld_remove(struct i2c_ static struct i2c_driver gw_i2c_pld_driver = { .driver = { .name = "gw_i2c_pld", - .owner = THIS_MODULE, }, .probe = gw_i2c_pld_probe, .remove = gw_i2c_pld_remove, _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 40+ messages in thread
[parent not found: <201906240241.S3kwAGar%lkp@intel.com>]
* [PATCH] fix platform_no_drv_owner.cocci warnings [not found] <201906240241.S3kwAGar%lkp@intel.com> @ 2019-06-23 18:50 ` kbuild test robot 0 siblings, 0 replies; 40+ messages in thread From: kbuild test robot @ 2019-06-23 18:50 UTC (permalink / raw) To: Asmaa Mnebhi Cc: kbuild-all, Corey Minyard, Arnd Bergmann, Greg Kroah-Hartman, openipmi-developer, linux-kernel From: kbuild test robot <lkp@intel.com> drivers/char/ipmi/ipmb_dev_int.c:352:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Fixes: 51bd6f291583 ("Add support for IPMB driver") CC: Asmaa Mnebhi <Asmaa@mellanox.com> Signed-off-by: kbuild test robot <lkp@intel.com> --- tree: https://github.com/cminyard/linux-ipmi for-next head: 21feadd024e747123bc0aaba8d8cbef2b5eda058 commit: 51bd6f291583684f495ea498984dfc22049d7fd2 [5/7] Add support for IPMB driver ipmb_dev_int.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -349,7 +349,6 @@ MODULE_DEVICE_TABLE(acpi, acpi_ipmb_id); static struct i2c_driver ipmb_driver = { .driver = { - .owner = THIS_MODULE, .name = "ipmb-dev", .acpi_match_table = ACPI_PTR(acpi_ipmb_id), }, ^ permalink raw reply [flat|nested] 40+ messages in thread
end of thread, other threads:[~2019-06-23 18:50 UTC | newest] Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <201501160912.S502Nlmz%fengguang.wu@intel.com> 2015-01-16 1:06 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2015-03-11 11:05 ` Thierry Reding 2015-03-18 1:38 ` Linus Walleij 2015-03-24 10:43 ` Thierry Reding 2015-03-25 1:56 ` Fengguang Wu [not found] <201502131224.TGzEj1Ve%fengguang.wu@intel.com> 2015-02-13 4:49 ` kbuild test robot [not found] <201503291531.GP28FwKy%fengguang.wu@intel.com> 2015-03-29 7:33 ` kbuild test robot 2015-03-29 16:24 ` Mark Brown 2015-03-30 12:52 ` Thierry Reding [not found] <201503291545.My1tADgz%fengguang.wu@intel.com> 2015-03-29 7:42 ` kbuild test robot 2015-03-29 16:27 ` Mark Brown [not found] <201503291519.K9Z6Qptb%fengguang.wu@intel.com> 2015-03-29 7:49 ` kbuild test robot 2015-03-30 13:05 ` Thierry Reding 2015-03-30 13:05 ` Thierry Reding 2015-04-07 13:09 ` Linus Walleij 2015-04-08 1:24 ` Fengguang Wu 2015-11-13 11:36 [PATCH] add new platform driver for PCI RC kbuild test robot 2015-11-13 12:06 ` [PATCHv3] " Joao Pinto 2015-11-13 11:36 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2016-01-09 0:50 [PATCH 2/6] Add Advantech iManager GPIO driver kbuild test robot 2016-01-08 22:29 ` richard.dorsch 2016-01-09 0:50 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2016-01-09 0:50 ` kbuild test robot 2016-01-09 2:02 [PATCH 6/6] Add Advantech iManager Watchdog driver kbuild test robot [not found] ` <1452292166-20118-7-git-send-email-richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2016-01-09 2:02 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2016-01-09 2:02 ` kbuild test robot 2016-01-10 10:11 [PATCH v2 1/6] Add Advantech iManager MFD core driver kbuild test robot 2016-01-10 9:10 ` richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w 2016-01-10 10:11 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2016-01-10 10:11 ` kbuild test robot 2016-01-10 10:25 [PATCH v2 3/6] Add Advantech iManager HWmon driver kbuild test robot 2016-01-10 9:11 ` richard.dorsch 2016-01-10 10:25 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2016-01-10 10:25 ` kbuild test robot 2016-01-10 10:34 [PATCH v2 4/6] Add Advantech iManager I2C driver kbuild test robot 2016-01-10 9:11 ` richard.dorsch 2016-01-10 10:34 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2016-01-10 10:34 ` kbuild test robot 2016-01-10 10:44 [PATCH v2 5/6] Add Advantech iManager Backlight driver kbuild test robot [not found] ` <1452417098-28667-1-git-send-email-richard.dorsch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2016-01-10 10:44 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2016-01-10 10:44 ` kbuild test robot 2016-02-15 7:34 [media 7/7] PCI bridge driver for PT3 & PXQ3PE kbuild test robot 2016-02-15 6:08 ` info 2016-02-15 7:34 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2016-07-27 14:41 Julia Lawall 2016-07-27 14:41 ` Julia Lawall 2016-08-08 5:47 ` Vinod Koul 2016-08-08 5:47 ` Vinod Koul 2016-08-06 22:00 [PATCH] This patch allows the Terratec Cinergy HTC Stick HD (0ccb:0101) to be used to watch DVB-T kbuild test robot 2016-08-06 15:03 ` Gerard H. Pille 2016-08-06 22:00 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2016-11-23 18:13 [PATCH] ALSA SoC MAX98927 driver - Initial release kbuild test robot 2016-11-23 4:57 ` Ryan Lee 2016-11-23 18:13 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2017-02-20 12:05 [PATCH 1/2] add driver for cypress cy8cmbr3102 kbuild test robot 2017-02-20 10:33 ` Patrick Vogelaar 2017-02-20 12:05 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot 2017-02-20 12:05 ` kbuild test robot 2018-12-30 5:10 [nomadik:ixp4 4/9] drivers/gpio/gw_i2c_pld.c:25:10: fatal error: linux/i2c/gw_i2c_pld.h: No such file or directory kbuild test robot 2018-12-30 5:10 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot [not found] <201906240241.S3kwAGar%lkp@intel.com> 2019-06-23 18:50 ` kbuild test robot
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.